相変わらず暇なので書きます。ナンプレ攻略プログラムについて。
↑の続き。今回はファイル入出力とメニュー表示。
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.c
基本ループのmenu()
。入力分岐でq
が入るまでループして盤面処理をする。
どのように表示されるかについては
こちら参照。
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
を変更する関数なので参照渡しにする必要がある。
まとめ
ファイル入出力ができて嬉しい!!!
おしまい。