脳内オセロの自動テスト(GDB)

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

まず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)

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA