「脳内オセロで脳トレ」で作成した脳トレ用のコマンドラインオセロですが、試してみると使い勝手が悪かったので、いろいろ手直ししようと思います。
ついでにバグも修正します。
まず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)


コメント