おはやし日記

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

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

相変わらず暇なので書きます。ナンプレ攻略プログラムについて。

o-treetree.hatenablog.com

↑の続き。今回はファイル入出力とメニュー表示。

fileio

ファイル入出力をする。

fileio.h

#define SAVE_FILE "np/save.dat"
#define DIR_NP "np/"

セーブファイルの名前と、データを入れるディレクト

ファイルの形式

数字のみ

%1d
123456789
< 81個の数字0~9 >

1行目に、変換指定子%1dを書き、2行目から盤面の数字を入れる。

%1d
123 456 789
456 789 123
...

のように整形してもいいし、極端に言えば81桁の数字にしても良い。

候補情報含む16進数

%x
1234 5678 90ab cdef 1234 5678 90ab cdef 1234
< 16進数のdata空白区切りで81個 >

fileio.c

int read_np(const char *filename){
    FILE *fp;
    if( (fp = fopen(filename, "r")) ==NULL){
        printw("%s file open error!!", filename);
        fclose(fp);
        return -1;
    }
    int i, buf;
    char format[50], fbuf[50];
    if(fscanf(fp, "%%%s", fbuf)){
        /* なにかしら変換指定子の存在を確認 */
        /* "%hoge"の形式のみ通過 fbufには"hoge"のみ入る */
        sprintf(format, "%%%s", fbuf);
        /* "hoge" で格納されているのを "%hoge" に戻してformatに入れる */
        for(i=0; i<81; i++){
            fscanf(fp, format, &buf);
            if(!strcmp(format, "%1d")){
                inputNum(i, buf);
            }else if(!strcmp(format, "%x") || !strcmp(format, "%4x")){
                data[i] = buf;
            }
        }
    }else{
        printw("in %s, no format!", filename);
        fclose(fp);
        return -1;
    }
    fclose(fp);
    return 0;
}

データが1桁なのか16進法なのかわからんといけないのでif(fscanf(fp, "%%%s", fbuf))でデータ1行目の変換指定子を読んで判定する。

1桁の盤面データであればinputNumし、16進数データなら直接dataに突っ込む


void read_file(){
    char fname[FILENAME_MAX];
    char buf[FILENAME_MAX];
    printw("\n%s[filename] -> %s", DIR_NP, DIR_NP);
    echo();
    scanw("%s", buf);
    noecho();
    sprintf(fname, "%s%s", DIR_NP, buf);
    int r = read_np(fname);
    if(r==0) printw("read %s", fname);
    getch();
}

void read_save(){
    addch('\n');//read_fileと改行数を合わせる
    int r = read_np(SAVE_FILE);
    if(r==0) printw("read %s", SAVE_FILE);
    getch();
}

read_file()はファイル名を指定して読み込む。read_save()はセーブファイルを読み込む。//改行数を合わせるというのは表示上の話


void save_data(){
    addch('\n');//read_fileと改行数を合わせる
    FILE *fp;
    if( (fp = fopen(SAVE_FILE, "w")) ==NULL){
        addstr("file open error!!");
        return;
    }
    fprintf(fp, "%%x\n");
    /*再読み込みで必要な変換指定子 %x を書き込む*/
    for(int i=0; i<81; i++){
        fprintf(fp, "%04x ", data[i]); //ここは%04x固定で
        if(i%9==8)fputc('\n', fp);
    }
    /*10行目時間情報書き込み*/
    time_t now = time(NULL);
    struct tm *timer = localtime(&now);
    fprintf(fp, "saved at %4d/%02d/%02d %02d:%02d:%02d",
    timer->tm_year + 1900,
    timer->tm_mon + 1,
    timer->tm_mday,
    timer->tm_hour,
    timer->tm_min,
    timer->tm_sec
    );
    fclose(fp);
    printw("write %s", SAVE_FILE);
}

セーブする。おしまい。

基本ループのmenu()。入力分岐でqが入るまでループして盤面処理をする。

どのように表示されるかについては

o-treetree.hatenablog.com

こちら参照。

void menu(){
    int i, ch, now;
    ch = now = 0;
    reset(data);reset(buf);reset(save);
    while(1){
        /* 盤面, メニュー表示 */
        showAll();
        markKouho2or3();
        mark_data_void();
        mark(now, 2, '$');
        printmenu(now);

        /* 入力分岐 */
        ch = getch();
        if(ch == 'q') break;
        if(ch == 'c'){changeKouho(&now); continue;}
        if(ch == 'b'){move_data(buf, data); continue;}
        if(ch == 'd'){move_data(data, buf); delete_data(now); continue;}
        if(ch == 'x'){move_data(data, buf); reset(data); now=0; continue;}
        if(ch == 'i'){read_file(); continue;}
        if(ch == 'w'){save_data(); continue;}
        if(ch == 'r'){read_save(); continue;}
        if(ch == 'k'){
            for(i=1; i<=9; i++){
                showAllKouho(i);
                getch();
            }
        }
        if(ch == ' '){
            now++;
            if(now==81)now=0;
        }
        if(KEY_DOWN <= ch && ch <= KEY_RIGHT){
            udrl(ch, &now);
        }
        if('0' <= ch && ch <= '9'){
            move_data(data, buf);
            inputNum(now, ch - '0');
            now++;
            if(now==81)now=0;
        }

        /* 判定処理 */
        only_two_pair_all();
        findPairAll();
        read_and_delete();
        findAllOnlyOne();
        checkNumAll();
    }
}

以下、menu()で使う順に

void showAll(){
    int i, j;
    move(0,0);
    for(i=0;i<81;i++){
        if(NUM(data[i])){
            printw("[%d] ", NUM(data[i]));
        }else{
            printw("[ ] ");
        }
        if(ROW(i)==2 || ROW(i)==5)addstr("@ ");
        if(ROW(i) == 8)addstr("@\n");
        if(i%27==26){
            for(j=0;j<YOKO;j++)addch('=');
            addch('\n');
        }
    }
    refresh();
}

全マス書き出し。


void mark(int n, int cp, char c){
    /* マス n の枠を色番号 cp の文字 c で囲む */
    attron(COLOR_PAIR(cp));
    mvprintw(LS[LINE(n)], RS[ROW(n)]-1, "%c", c);
    mvprintw(LS[LINE(n)], RS[ROW(n)]+1, "%c", c);
    attron(COLOR_PAIR(1));
}

void markKouho2or3(){
    for(int i=0; i<81; i++){
        if(sumKouho(i) == 2) mark(i, 4, '!');
        if(sumKouho(i) == 3) mark(i, 3, '?');
    }
}

void mark_data_void(){
    // 確定なしで候補全滅だと0x0000になる。間違い。
    for(int i=0; i<81; i++){
        if(data[i] == 0x0000) mark(i, 2, 'X');
    }
}

候補の数2, 3であるものをマーク、間違った盤面入力によって候補が全滅したマスをマークする。


void printmenu(int now){
    int i;
    move(12,0);
    printw("now(%2d: %04x)", now, data[now]);
    if(NUM(data[now]) > 0){
        printw("[%d] < ", NUM(data[now]));
    }else{
        printw("[_] < ");
    }
    for(i=1; i<=9; i++){
        if(FLAG(data[now], i)){
            printw("%d ", i);
        }
    }
    addstr(">\n");
    addstr("[^] [v] [<] [>] [Space] [");
    addstr_rev_nml("K", "ouho] [");
    addstr_rev_nml("C", "hange]\n[");
    addstr_rev_nml("B", "ack] [");
    addstr_rev_nml("D", "elete] [");
    addstr_rev_nml("X", ":reset] [");
    addstr_rev_nml("I", "mport]\n[");
    addstr_rev_nml("W", "rite_save] [");
    addstr_rev_nml("R", "ead_save] [");
    addstr_rev_nml("Q", "uit]");
    refresh();
}

void addstr_rev_nml(char *r, char *n){
    reverse(ON);addstr(r);reverse(OFF);addstr(n);
}

//setting.c
void reverse(int OnOff){
    if(OnOff)attron(A_REVERSE);
    if(!OnOff)attrset(0);
}

メニュー表示。addstr_rev_nml(char *r, char *n)は、rを白黒反転表示してnを通常表示する。


//if(ch == 'c'){changeKouho(&now); continue;}
void changeKouho(int *n){
    int now = *n;
    int i, change;
    move(12,0);
    for(i=1; i<=9; i++){
        if(FLAG(data[now], i)){
            printw("%d ", i);
        }
    }
    addstr("whitch to change -> ");
    change = getch() - '0';
    if(1 <= change && change <= 9){
        if(FLAG(data[now], change)){
            flag_off(data[now], change);
        }else{
            flag_on(data[now], change);
        }
    }
}

なぜ参照渡しにしたのかはわからんが、現在立っている候補を列挙し、選択した候補のフラグを逆転させる。


//if(ch == 'b'){move_data(buf, data); continue;}
//if(ch == 'd'){move_data(data, buf); delete_data(now); continue;}
//if(ch == 'x'){move_data(data, buf); reset(data); now=0; continue;}

void move_data(np from[], np to[]){
    for(int i=0; i<81; i++){
        to[i] = from[i];
    }
}

データのコピー。なんか操作するときは一度bufに保存しているので1つ戻るくらいはできる


/*
if(ch == 'k'){
    for(i=1; i<=9; i++){
        showAllKouho(i);
        getch();
    }
}
*/

void showAllKouho(int k){
    int i, j;
    move(0,0);
    for(i=0;i<81;i++){
        if(FLAG(data[i],k) && !NUM(data[i])){
            attron(COLOR_PAIR(3));
            printw("<%d> ", k);
            attron(COLOR_PAIR(1));
        }else if(NUM(data[i]) != 0){
            printw("[%d] ", NUM(data[i]));
        }else{
            printw("[ ] ");
        }
        if(i%3==2)addstr("@ ");
        if(ROW(i) == 8)addch('\n');
        if(i%27==26){
            for(j=0;j<41;j++)addch('=');
            addch('\n');
        }
    }
    refresh();
}

showAll()の変形で、1~9まで順番に、候補があるマスを緑色で表示する。


/*
if(KEY_DOWN <= ch && ch <= KEY_RIGHT){
    udrl(ch, &now);
}
*/

void udrl(int ch, int *n){
    int now = *n;
    switch(ch){
        case KEY_UP:
            *n = LINE(now)!=0 ? now-9 : now+72;
            break;
        case KEY_DOWN:
            *n = LINE(now)!=8 ? now+9 : now-72;
            break;
        case KEY_LEFT:
            *n = ROW(now)!=0 ? now-1 : now+8;
            break;
        case KEY_RIGHT:
            *n = ROW(now)!=8 ? now+1 : now-8;
            break;
    }
}

これは、矢印キーが入力された時に現在地nowを変更する関数なので参照渡しにする必要がある。

まとめ

ファイル入出力ができて嬉しい!!!

おしまい。

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