「脳内オセロで脳トレ」で作成した脳トレ用のコマンドラインオセロですが、試してみると使い勝手が悪かったので、いろいろ手直ししようと思います。
ついでにバグも修正します。
まずUNIXの”Do One Thing and Do It Well”の考え方に基づいて、余分な所をそぎ落とします。
最初に目につくこの部分。目的は脳内オセロなので、ボードを可視化する必要はありません。
8 char bd[10][10]={ 9 Z, '1','2','3','4','5','6','7','8', Z , 10 11 Z, _ , _ , _ , _ , _ , _ , _ , _ , '1', 12 Z, _ , _ , _ , _ , _ , _ , _ , _ , '2', 13 Z, _ , _ , _ , _ , _ , _ , _ , _ , '3', 14 Z, _ , _ , _ , Q , X , _ , _ , _ , '4', 15 Z, _ , _ , _ , X , Q , _ , _ , _ , '5', 16 Z, _ , _ , _ , _ , _ , _ , _ , _ , '6', 17 Z, _ , _ , _ , _ , _ , _ , _ , _ , '7', 18 Z, _ , _ , _ , _ , _ , _ , _ , _ , '8', 19 20 Z, Z , Z , Z , Z , Z , Z , Z , Z , Z , 21 };
よって、こうします。
8 char bd[10][10];
初期化もなくなったので、memsetも加えます。string.hも必要ですね。
memset(bd,0,sizeof(bd));
ボードを表示する関数は、いざという時、石の座標がわかるように変更します。
23 void disp() 24 { 25 int x,y; 26 for(y = 0; y < 9; y++){ 27 for(x = 0; x < 10; x++){ 28 printf("%c",bd[y][x]); 29 printf("|"); 30 } 31 printf("\n"); 32 } 33 } 34
このようになりました。
void disp() { int x,y; for(y = 1; y <= 8; y++) for(x = 1; x <= 8; x++) if(bd[y][x] != 0 ) printf("%d%d,%c\n",x,y,bd[y][x]); }
QがBLACKでしたが、非常にわかりにくいので、BLACKはB、WHITEはWとします。
あと、これはバグですが、石が裏返る方向を見つけたら、一方向しか裏返しにしません。以下の77行目でbreakしているからです。全方向のチェックを実行するように修正します。また、相手の石の上にも置くことができていました。これに関しては、ボードが空かどうかで判定を入れれば良いですね。
70 printf("You are %s. X=? Y=?", turn==0 ?"BLACK(Q)":"WHITE(X)"); 71 scanf("%d,%d",&x,&y); 72 if(x>8 || y>8 || x<1 || y<1 )continue; 73 t = turn==0?'Q':'X'; 74 for(i=0;i<8;i++) 75 if(check(x,y, xx[i],yy[i], t, 1)){ 76 bd[y][x] = t; 77 break; 78 } 79 if(i==8)continue;
修正後のソースです。
1 #include <stdio.h> 2 #include <string.h> 3 4 char bd[10][10]; 5 6 void disp() 7 { 8 int x,y; 9 for(y = 1; y <= 8; y++) 10 for(x = 1; x <= 8; x++) 11 if(bd[y][x] != 0 ) 12 printf("%d%d,%c\n",x,y,bd[y][x]); 13 } 14 15 int check(int x, int y, int xx, int yy, char bw, int first); 16 17 int check(int x, int y, int xx, int yy, char bw, int first) 18 { 19 int my_x = x + xx; 20 int my_y = y + yy; 21 22 if(bd[my_y][my_x] == bw) 23 if(first == 1) 24 return 0; 25 else 26 return 1; 27 28 switch(bd[my_y][my_x]){ 29 case 0: 30 return 0; 31 } 32 33 if(!check(my_x,my_y, xx,yy, bw, 0) ) 34 return 0; 35 36 bd[my_y][my_x] = bw; 37 return 1; 38 } 39 40 int main(int argc, char* argv[]) 41 { 42 int i; 43 char disc; 44 int xx[]={-1,-1, 0, 1, 1, 1, 0, -1}; 45 int yy[]={ 0,-1,-1,-1, 0, 1, 1, 1}; 46 int x,y,turn=0,t,turnend_flag; 47 char c; 48 49 memset(bd,0,sizeof(bd)); 50 51 bd[4][4]='W'; bd[5][4]='B'; 52 bd[4][5]='B'; bd[5][5]='W'; 53 54 for(;;){ 55 disp(); 56 disc = turn==0?'B':'W'; 57 printf("YOU=%c\n",disc); 58 fflush(stdout); 59 x=0;y=0; 60 scanf("%d,%d",&x,&y); 61 if(x>8 || y>8 || x<1 || y<1 )continue; 62 turnend_flag = 0; 63 if(bd[y][x] == 0) 64 for(i=0;i<8;i++) 65 if(check(x,y, xx[i],yy[i], disc, 1)){ 66 bd[y][x] = disc; 67 turnend_flag = 1; 68 } 69 if(turnend_flag) 70 turn = 1 - turn; 71 } 72 73 return 0; 74 }
さて、本題である自動テストです。GDBのコマンドファイルを用いてテストします。このようなGDBコマンドファイルとなりました。
takk@deb83:~/scrap$ cat -n reversi.gdb 1 set pagination off 2 3 file a.out 4 5 break 60 6 7 run 8 9 # Black scanf x y 10 set x=3 11 set y=4 12 jump 61 13 14 # White scanf x y 15 set x=3 16 set y=5 17 jump 61 18 19 # Black scanf x y 20 set x=3 21 set y=6 22 jump 61 23 24 quit
1行目 Enterを求められるのでOFFするおまじないです。
3行目 file 実行ファイルで、デバッグするELFを指定します。
5行目 ブレイクする箇所をscanf実行前にしたいので、60行目にブレイクポイントを設定します。
9〜11行目 scanfの代わりに値を設定しています。ここをいろいろ変えてテストケースとなります。
12行目 60行目のscanfをスキップし、61行目からプログラムの実行を再開するためのコマンドです。
24行目 quitは、GDB自体のquitです。
さてデバッグスクリプトを実行してみます。-gを指定して、ビルドし、その後 gdb -xでコマンドファイルを指定します。
takk@deb83:~/scrap$ gcc -g reversi.c takk@deb83:~/scrap$ gdb -x reversi.gdb > result.txt
これで、result.txtに結果が格納されました。確認してみましょう。
takk@deb83:~/scrap$ cat -n result.txt 1 GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1 2 Copyright (C) 2014 Free Software Foundation, Inc. 3 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> 4 This is free software: you are free to change and redistribute it. 5 There is NO WARRANTY, to the extent permitted by law. Type "show copying" 6 and "show warranty" for details. 7 This GDB was configured as "i586-linux-gnu". 8 Type "show configuration" for configuration details. 9 For bug reporting instructions, please see: 10 <http://www.gnu.org/software/gdb/bugs/>. 11 Find the GDB manual and other documentation resources online at: 12 <http://www.gnu.org/software/gdb/documentation/>. 13 For help, type "help". 14 Type "apropos word" to search for commands related to "word". 15 Breakpoint 1 at 0x8048716: file reversi.c, line 60. 16 44,W 17 54,B 18 45,B 19 55,W 20 YOU=B 21 22 Breakpoint 1, main (argc=1, argv=0xbffff7b4) at reversi.c:60 23 60 scanf("%d,%d",&x,&y); 24 34,B 25 44,B 26 54,B 27 45,B 28 55,W 29 YOU=W 30 31 Breakpoint 1, main (argc=1, argv=0xbffff7b4) at reversi.c:60 32 60 scanf("%d,%d",&x,&y); 33 34,B 34 44,B 35 54,B 36 35,W 37 45,W 38 55,W 39 YOU=B 40 41 Breakpoint 1, main (argc=1, argv=0xbffff7b4) at reversi.c:60 42 60 scanf("%d,%d",&x,&y); 43 34,B 44 44,B 45 54,B 46 35,B 47 45,B 48 55,W 49 36,B 50 YOU=W 51 52 Breakpoint 1, main (argc=1, argv=0xbffff7b4) at reversi.c:60 53 60 scanf("%d,%d",&x,&y); 54 A debugging session is active. 55 56 Inferior 1 [process 24106] will be killed. 57 58 Quit anyway? (y or n) [answered Y; input not from terminal] takk@deb83:~/scrap$
オセロの棋譜テストケースを作るのは、さすがにマウス操作じゃないと骨が折れるので割愛します。
(reversi.c reversi.gdbの取得は、git clone https://github.com/takkete/scrap.git)
コメント