おはやし日記

テーマ……バイク←プログラミング←旅

プログラムにナンプレを解かせたい - その7

最終回。↓のつづき。

o-treetree.hatenablog.com

プログラムがパワーアップした後の要点は、

o-treetree.hatenablog.com

ここ↑で書いたcalc.cの計算部分と

o-treetree.hatenablog.com

ここ↑で書いたfileio.cのファイル入出力です。

今回は、なんとなく作ったログ書き出しプログラムについて書きます。あと、makefileについて。

全体のプログラムはこちら↓

GitHub - oha-yashi/sudoku: ナンプレ攻略プログラム

(追記@2020/5月) これをJavaScriptに移植したwebバージョンができてます!詳しくは ↓

o-treetree.hatenablog.com

log.c

sudoku/log.c

動作

githubからcloneしたあと(詳細はプログラムにナンプレを解かせたい - その4 - おはやし日記

$ make log

コンパイル

logdataフォルダに、盤面データtest↓をいれて

%1d
000 400 065
080 000 000
700 090 000
000 000 120
065 008 000
004 000 000
000 600 009
100 000 700
000 005 000
$ ./log test (auto)

で実行。(autoを加えるとステップごとにEnterを叩く必要がなくなる)

sudoku/log $ ./log test

Enterを叩いていくとこのように表示され、logdata/test_logが作成される。中身は↓のようになります(クソ長い)

gistbeaa9476c970a13069f85a74f521a2d5

解答ステップごとに、全ての候補が書き出されています。さらに、前のステップから変更があったマスについてはマスの下の線が

^^^^^

となって示されています。

雑に解説

log.cはなんとなくあったらいいかも知れんと思って突貫で作ったので割と作りが雑です。

write_log()

  • 数字の書き込み
    for(i=0;i<9;i++){
        
        fprintf(fp, "%d:@", i);
        for(j=0;j<3;j++){
            roop = i*9 + j*3;
            fprintf(fp, "|%c %c %c|%c %c %c|%c %c %c|@",
                PUT_LOG(roop  , 1), PUT_LOG(roop  , 2), PUT_LOG(roop  , 3),
                PUT_LOG(roop+1, 1), PUT_LOG(roop+1, 2), PUT_LOG(roop+1, 3),
                PUT_LOG(roop+2, 1), PUT_LOG(roop+2, 2), PUT_LOG(roop+2, 3)
            );
        }
        fprintf(fp, "\n ");

        fprintf(fp, "%d:@", i);
        for(j=0;j<3;j++){
            roop = i*9 + j*3;
            fprintf(fp, "|%c %c %c|%c %c %c|%c %c %c|@",
                PUT_LOG(roop  , 4), PUT_LOG(roop  , 5), PUT_LOG(roop  , 6),
                PUT_LOG(roop+1, 4), PUT_LOG(roop+1, 5), PUT_LOG(roop+1, 6),
                PUT_LOG(roop+2, 4), PUT_LOG(roop+2, 5), PUT_LOG(roop+2, 6)
            );
        }
        fprintf(fp, "\n ");

        fprintf(fp, "%d:@", i);
        for(j=0;j<3;j++){
            roop = i*9 + j*3;
            fprintf(fp, "|%c %c %c|%c %c %c|%c %c %c|@",
                PUT_LOG(roop  , 7), PUT_LOG(roop  , 8), PUT_LOG(roop  , 9),
                PUT_LOG(roop+1, 7), PUT_LOG(roop+1, 8), PUT_LOG(roop+1, 9),
                PUT_LOG(roop+2, 7), PUT_LOG(roop+2, 8), PUT_LOG(roop+2, 9)
            );
        }
        fprintf(fp, "\n ");

roopはマスの番号。PUT_LOGはマクロ。数字or空白の表示なのでfprintf%cに突っ込む。

#define PUT_LOG(n, i)   FLAG(data[n], i) ? i+'0' : ' '
  • 枠線(横)の書き込み
if(i==2||i==5||i==8){
    fprintf(fp, "@@@@");
    AT_LINE(i*9+0);AT_LINE(i*9+1);AT_LINE(i*9+2);
    fprintf(fp, "@@");
    AT_LINE(i*9+3);AT_LINE(i*9+4);AT_LINE(i*9+5);
    fprintf(fp, "@@");
    AT_LINE(i*9+6);AT_LINE(i*9+7);AT_LINE(i*9+8);
    fprintf(fp, "@\n ");
}else{
    fprintf(fp, "----");
    DASH_LINE(i*9+0);DASH_LINE(i*9+1);DASH_LINE(i*9+2);
    fprintf(fp, "--");
    DASH_LINE(i*9+3);DASH_LINE(i*9+4);DASH_LINE(i*9+5);
    fprintf(fp, "--");
    DASH_LINE(i*9+6);DASH_LINE(i*9+7);DASH_LINE(i*9+8);
    fprintf(fp, "@\n ");
}

3列ごとに@で太い線を、その他は-で細い線を引く。AT_LINEDASH_LINEはマクロ。

#define AT_LINE(n)      data[n]==buf[n] ? fprintf(fp, "@@@@@@") : fprintf(fp, "^^^^^@")
#define DASH_LINE(n)    data[n]==buf[n] ? fprintf(fp, "------") : fprintf(fp, "^^^^^-")

さっきみたいにfprintfの中身だけ変えればよかったな、まあええわ。要は、前ステップのデータbuf[]と操作後のデータdata[]が違っていれば何かしら変更があったということなので横線を^^^^^にして示す。

前に作ったmenu.c/menu()をログ書き込みに特化させた

void menu_log(){
    int cnt = 1, ssK = sumsumKouho();
    while(cnt < LIMIT){
        if(ssK==81) break;
        /* 判定処理 */
        only_two_pair_all();
        if(ssK > sumsumKouho()){
            /* 処理で候補を減らせたら */
            write_log(cnt, 1, "two pairs -> delete num except pair");
            move_data(data, buf);
            ssK = sumsumKouho();
        }if(ssK==81) break;

        findPairAll();
        if(ssK > sumsumKouho()){
            write_log(cnt, 2, "two pairs -> delete pair in LineRowBlock");
            move_data(data, buf);
            ssK = sumsumKouho();
        }if(ssK==81) break;

        read_and_delete();
        if(ssK > sumsumKouho()){
            write_log(cnt, 3, "read LineRowBlock and delete");
            move_data(data, buf);
            ssK = sumsumKouho();
        }if(ssK==81) break;

        findAllOnlyOne();
        if(ssK > sumsumKouho()){
            write_log(cnt, 4, "onlyone in LineRowBlock -> input");
            move_data(data, buf);
            ssK = sumsumKouho();
        }if(ssK==81) break;

        checkNumAll();
        if(ssK > sumsumKouho()){
            write_log(cnt, 5, "onlyone in data -> input");
            move_data(data, buf);
            ssK = sumsumKouho();
        }if(ssK==81) break;

        if(isauto){}else getch();
        cnt++;
    }
}

# define LIMIT 30回まで処理。用意した処理で対応できずに詰んだら無限ループになってしまうので。ssk=sumsumKouho()は、盤面全体で候補のフラグが立っている数。全マスが埋まればssK=81となって終了。

isautoグローバル変数isauto=0。ここ、if(!isauto)getch();でよかったな…………。コマンドライン引数の3つ目にautoを入れていればgetch()不要で次のステップに進む。

makefile

sudoku/makefile

make は、コマンドをmakefileに書き込んでおくと短い呼び出しでそれをやってくれる機能(?多分結構違う)です。

なんか解説することあるかなと思ったけどmakefileにメモってあるから要らんかなw

ファイル構造が

sudoku
|
|- makefile
|
|- source
    |
    |- calc.c
    |- calc.h
...

ってなっているので、コンパイルに使うファイルの探し先として

VPATH = source

としています。

まとめ

プログラム君がどう動いているのか知りたかったので作ってみただけです。

おしまい!!!

参考にしたサイト

ここに載せた以外にもあるんですけど、大学や高専情報科か何かのページが多いのでリンク貼っていいのかわからんのもあったり…………まあ検索すれば出て来ます。

ncurses

ビット演算

makefile

プライバシーポリシー ・お問い合わせはこちら