/* game hex main(hex.c) created in 2006.07.29 modified in 2006.11.23 modified in 2006.11.24 modified in 2006.11.27 modified in 2006.11.29 modified in 2006.12.5 modified in 2006.12.6 modified in 2006.12.13 modified in 2006.12.21 modified in 2006.12.23 modified in 2007.1.11 modified in 2007.1.16 modified in 2008.12.25 by DEGUCHI Hiroshi */ #include "hex.h" /* #include #include #define BUFSIZE 255 #define MINSIZE (3+2) #define MAXSIZE (26+2) #define ERROR (-1) #define EMPTY 0 #define MARU 1 #define BATSU 2 #define B_MARU (MARU+2) #define B_BATSU (BATSU+2) #define MAXCHAR (B_BATSU+1) 対戦関数と名前の登録 extern int (*hexgame[2])(); */ #include /* hex MARU BATSU 平面 */ static int hex[MAXSIZE][MAXSIZE] = { { 2, 2, 2, 2, 2, 3 }, { 1, 0, 0, 0, 0, 3 }, { 1, 0, 0, 0, 0, 3 }, { 1, 0, 0, 0, 0, 3 }, { 1, 0, 0, 0, 0, 3 }, { 1, 4, 4, 4, 4, 4 }, }; static int size = 10; static int turn = MARU; /* 盤表示なし */ static int nodisp; /* hex 到達解析用 平面 */ static int hex_c[MAXSIZE][MAXSIZE]; /* 1234   ×××× A○・・・・○A B○・・・・○B C○・・・・○C D○・・・・○D  ××××  1234 →i、↓j (i,j)と隣接関係にあるのは:  (i-1,j-1) (i, j-1)      \ / (i-1,j)−(i,j)−(i+1, j)      / \   (i,j+1) (i+1,j+1) */ /* hexのsize×size分を全てvalにする */ static void clear( int val, int size, int hex[MAXSIZE][MAXSIZE] ) { int i, j; for( j = 0; j < size; j++ ) for( i = 0; i < size; i++ ) hex[j][i] = val; } /* hexの初期化 */ static void init( int size, int hex[MAXSIZE][MAXSIZE] ) { int i, j; clear( EMPTY, size+2, hex ); for( i = 0; i < size+1; i++ ) hex[0][i] = BATSU, hex[size+1][i+1] = (BATSU+2); for( j = 0; j < size+1; j++ ) hex[j+1][0] = MARU, hex[j][size+1] = (MARU+2); } /* turnの勝ち判定 1:turnの勝ち、0:その他 */ static int check( int turn, int size ) { int x, y; int dist, f, min; int i, j, im, jm; /* hex_cの準備 */ /* →i、↓j 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 */ clear( -1, size+2, hex_c ); for( j = 0; j < size+2; j++ ) if( hex[j][0] == turn ) hex_c[j][0] = 0; for( i = 0; i < size+2; i++ ) if( hex[0][i] == turn ) hex_c[0][i] = 0; /* hex_c[x][y] に出発辺からの距離をセットする */ for( f = 1, dist = 0; f == 1; dist++ ) for( f = 0, j = 1; j <= size; j++ ) for( i = 1; i <= size; i++ ) if( hex[j][i] == turn && hex_c[j][i] < 0 ) if( hex_c[j-1][i] == dist || hex_c[j-1][i-1] == dist || hex_c[j][i-1] == dist || hex_c[j][i+1] == dist || hex_c[j+1][i+1] == dist || hex_c[j+1][i] == dist ) hex_c[j][i] = dist+1, f = 1; /* 到着辺上の最小値を求める */ /* MARU */ for( min = 0, j = 1; j < size+2; j++ ) if( hex[j][size+1] == turn+2 && hex_c[j][size] > 0 ) if( min == 0 || hex_c[j][size] < min ) min = hex_c[jm=j][size]; if( min > 0 ) { hex_c[jm][size+1] = min+1; return(1); } /* BATSU */ for( min = 0, i = 1; i < size+2; i++ ) if( hex[size+1][i] == turn+2 && hex_c[size][i] > 0 ) if( min == 0 || hex_c[size][i] < min ) min = hex_c[size][im=i]; if( min > 0 ) { hex_c[size+1][im] = min+1; return(1); } return(0); } /* winning pathをhex中にturn+2で描く */ static void winpath( int turn, int size ) { int i, j; int dist; /* 到達点hex_c[j][i]と距離distを求める */ dist = -1; /* MARU */ for( i = size+1, j = 1; j < size+2; j++ ) if( (dist = hex_c[j][i]) > 0 ) break; if( dist < 0 ) /* BATSU */ for( i = 1, j = size+1; i < size+2; i++ ) if( (dist = hex_c[j][i]) > 0 ) break; /* printf("j = %d, i = %d, dist = %d\n", j, i, dist ); */ /* hex_c中のpathを逆に辿りhex中にturn+2で描く */ for( ; hex[j][i] = turn+2, dist-- > 0; ) { /* printf("j = %d, i = %d, dist = %d\n", j, i, dist ); */ if( hex_c[j-1][i] == dist ) j--; else if( hex_c[j-1][i-1] == dist ) j--, i--; else if( hex_c[j][i-1] == dist ) i--; else if( hex_c[j][i+1] == dist ) i++; else if( hex_c[j+1][i+1] == dist ) j++, i++; else if( hex_c[j+1][i] == dist ) j++; else break; } } /* 数値を表示用文字列に変換 */ char *digitcnv( int d ) { switch(d) { case 0: return("0"); case 1: return("1"); case 2: return("2"); case 3: return("3"); case 4: return("4"); case 5: return("5"); case 6: return("6"); case 7: return("7"); case 8: return("8"); case 9: return("9"); case 10: return("10"); case 11: return("11"); case 12: return("12"); case 13: return("13"); case 14: return("14"); case 15: return("15"); case 16: return("16"); case 17: return("17"); case 18: return("18"); case 19: return("19"); case 20: return("20"); case 21: return("21"); case 22: return("22"); case 23: return("23"); case 24: return("24"); case 25: return("25"); case 26: return("26"); } return("◆"); } /* 文字を表示用文字列に変換 */ char *charcnv( int c ) { switch(c) { case EMPTY: return("・"); case MARU: return("○"); case BATSU: return("☆"); case (MARU+2): return("●"); case (BATSU+2): return("★"); case MAXCHAR: return("※"); case ' ': return(" "); case '.': return("・"); case '0': return("0"); case '1': return("1"); case '2': return("2"); case '3': return("3"); case '4': return("4"); case '5': return("5"); case '6': return("6"); case '7': return("7"); case '8': return("8"); case '9': return("9"); case '@': return("@"); case 'A': case 'a': return("A"); case 'B': case 'b': return("B"); case 'C': case 'c': return("C"); case 'D': case 'd': return("D"); case 'E': case 'e': return("E"); case 'F': case 'f': return("F"); case 'G': case 'g': return("G"); case 'H': case 'h': return("H"); case 'I': case 'i': return("I"); case 'J': case 'j': return("J"); case 'K': case 'k': return("K"); case 'L': case 'l': return("L"); case 'M': case 'm': return("M"); case 'N': case 'n': return("N"); case 'O': case 'o': return("O"); case 'P': case 'p': return("P"); case 'Q': case 'q': return("Q"); case 'R': case 'r': return("R"); case 'S': case 's': return("S"); case 'T': case 't': return("T"); case 'U': case 'u': return("U"); case 'V': case 'v': return("V"); case 'W': case 'w': return("W"); case 'X': case 'x': return("X"); case 'Y': case 'y': return("Y"); case 'Z': case 'z': return("Z"); } return("■"); } /* hex盤を画面表示 1234   ×××× A○・・・・○A B○・・・・○B C○・・・・○C D○・・・・○D  ××××  1234 */ static void disp( int size ) { char buf[BUFSIZE]; int i, j, k; for( i = 0; i < size; i++ ) printf(" "); printf("   "); for( i = 0; i < size; i++ ) printf("%s", digitcnv(i+1) ); printf("\n"); for( i = 0; i < size; i++ ) printf(" "); printf("  "); for( i = 0; i < size; i++ ) printf("%s", charcnv(BATSU) ); printf("\n"); for( j = 0; j < size; j++ ) { for( k = size-j; k>1; k-- ) printf(" "); printf("%s%s", charcnv('A'+j), charcnv(MARU) ); for( i = 0; i < size; i++ ) { if( i > 0 ) printf(""); printf("%s", charcnv(hex[j+1][i+1]) ); } printf("%s%s\n", charcnv(MARU), charcnv('A'+j) ); } printf("  "); for( i = 0; i < size; i++ ) printf("%s", charcnv(BATSU) ); printf("\n"); printf(" "); for( i = 0; i < size; i++ ) printf("%s", digitcnv(i+1) ); printf("\n"); } /* キー入力(1a 1A A1 a1 ...) *xp = 1234←1234 *yp = 1234←ABCD 1:正常入力、0:異常入力 */ static int keyin( int *xp, int *yp ) { char buf[BUFSIZE], y; int x; if( fgets(buf, BUFSIZE, stdin) ) { if( sscanf( buf, "%d%c", xp, &y ) == 2 || sscanf( buf, "%c%d", &y, xp ) == 2 ) { if( 'A' <= y && y <= 'Z' ) { *yp = y - 'A' + 1; return( 1 ); } else if( 'a' <= y && y <= 'z' ) { *yp = y - 'a' + 1; return( 1 ); } } return( 0 ); } return( 0 ); } /* 人がプレーする(自分の関数をデバッグする)為の関数 part:MARU or BATSU (xc,yc):直前の相手の手、自分が先手のときは(0,0) msg:事前準備(part==EMPTY)のときは msg に自分の名前を名乗る  その他毎回のコメントを msg に設定しても良い、無いときは0 戻り値  0:降参、1:正常終了 */ int human( int part, int xc, int yc, char **msg ) { int x, y; /* 事前準備 */ if( part==EMPTY ) { /* 自分のユーザ名(学籍番号由来の関数名と同じ)を名乗る */ *msg = "human"; return(1); } *msg = 0; /* 通常は何も言わない */ do { if( nodisp ) disp( size ); printf("%s の番です。座標入力して下さい(例:1a ):", charcnv(part) ); if( keyin( &x, &y ) == 0 ) continue; /* x座標に負数を入力すると降参することができる */ if( x < 0 ) { puthex( part, x, y ); *msg = "参りました"; return( 0 ); } else if( gethex( part, x, y ) == EMPTY ) return( puthex( part, x, y ) ); else printf("そこには打てません!\n"); } while(1); } /* 乱数でプレーする関数 part:MARU or BATSU (xc,yc):直前の相手の手、自分が先手のときは(0,0) msg:事前準備(part==EMPTY)のときは msg に自分の名前を名乗る  その他毎回のコメントを msg に設定しても良い、無いときは0 戻り値  0:降参、1:正常終了 */ int p_rand( int part, int xc, int yc, char **msg ) { int x, y; time_t ltime; static int first = 1; /* 事前準備 */ if( part==EMPTY ) { /* 自分のユーザ名(学籍番号由来の関数名と同じ)を名乗る */ *msg = "p_rand"; return(1); } /* 初期化 */ if( first == 1 ) { first = 0; time( <ime ); srand(ltime); } *msg = 0; /* 通常は何も言わない */ do{ x = rand()%size+1; y = rand()%size+1; if( gethex( part, x, y ) == EMPTY ) return( puthex( part, x, y ) ); } while(1); } static int xx, yy; /* 打った手 */ static int zz; /* 打った数 */ /* 先手後手の関数(hexgame)と名前(hexname)をhexgame.cに定義しておく Usage:hex size rev nodisp-log size = 盤のサイズ rev = 0:正順、1:逆順(先手後手交代) nodisp-log 何もなければ毎回盤を表示、1より大きければログ保存 ログファイル名:hexlog.txt */ int main( int argc, char *argv[] ) { char buf[BUFSIZE]; char cbuf[BUFSIZE]; int px, py; /* 直前の手 */ int frame; int rev; /* 先攻後攻 */ int hexturn[2] = { MARU, BATSU }; char *hexname[2]; /* プレーヤー名 */ char hexname2[2][BUFSIZE]; char *msg = 0; /* メッセージ */ FILE *ofp; char logfile[] = "hexlog.txt"; /* ログファイル名 */ FILE *cofp; char clogfile[] = "clog.txt"; /* 勝敗ログファイル名 */ time_t now; struct tm *tm_now; /* size設定 */ if( argc > 1 && sscanf( argv[1], "%d", &size ) == 1 ) { if( size < MINSIZE-2 ) size = MINSIZE-2; else if( MAXSIZE-2 < size ) size = MAXSIZE-2; } else { do{ printf("サイズ(%d〜%d)を入力して下さい:", MINSIZE-2, MAXSIZE-2 ); fgets( buf, BUFSIZE, stdin ); sscanf( buf, "%d", &size ); } while( size < MINSIZE-2 || MAXSIZE-2 < size ); } init( size, hex ); /* 先手後手 */ if( argc > 2 && sscanf( argv[2], "%d", &rev ) == 1 && rev == 1) ; else rev = 0; /* 盤・メッセージ表示 */ if( argc > 3 && sscanf( argv[3], "%d", &nodisp ) == 1 ) { if( nodisp < 1 ) nodisp = 1; } else nodisp = 0; /* ログ保存準備 */ ofp = cofp = NULL; if( nodisp > 1 ) { if( (ofp = fopen( logfile,"a"))== NULL ) { printf("ログファイル(%s)がオープンできませんでした。\n", logfile ); exit(1); } if( nodisp > 2 && (cofp = fopen( clogfile,"a"))== NULL ) { printf("勝敗ログファイル(%s)がオープンできませんでした。\n", clogfile ); exit(1); } } /* 日付時刻設定 */ now = time(NULL); tm_now = localtime(&now); sprintf( buf, "%4d/%02d/%02d %02d:%02d:%02d", tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec ); /* プレーヤー名を設定 */ hexgame[0]( EMPTY, 0, 0, &hexname[0] ); hexgame[1]( EMPTY, 0, 0, &hexname[1] ); sprintf( hexname2[rev], "先手:%s(%s)", charcnv(hexturn[rev]), hexname[rev] ); sprintf( hexname2[1-rev], "後手:%s(%s)", charcnv(hexturn[1-rev]), hexname[1-rev] ); sprintf( buf, "%s 先手:%svs %s サイズ:%d → ", buf, hexname2[rev], hexname2[1-rev], size ); printf("%s\n", buf ); if( ofp ) { fprintf( ofp, "%s", buf ); fflush( ofp ); } /* 開始 */ px = py = 0; for( frame = 0; ; frame++, rev = 1 - rev ) { if( !nodisp ) disp( size ); if( msg ) printf("「%s」by %s(%s)\n", msg, charcnv(hexturn[1-rev]), hexname[1-rev] ); zz = 0; /* puthexで打つ度に1増える */ /* rev:hexturn[rev]の番 */ if( hexgame[rev]( hexturn[rev], px, py, &msg ) == 0 ) { /* 降参 */ sprintf( buf, "%sの勝!", hexname2[1-rev] ); sprintf( cbuf, "%s %s\n", hexname[1-rev], hexname[rev] ); sprintf( buf, "%s ∵%sが(", buf, hexname2[rev] ); if( xx >= 0 ) sprintf( buf, "%s%s,", buf, digitcnv(xx) ); else sprintf( buf, "%s-%s,", buf, digitcnv(-xx) ); if( yy >= 0 ) sprintf( buf, "%s%s", buf, charcnv('A'+yy-1) ); else sprintf( buf, "%s-%s", buf, charcnv('A'-yy-1) ); sprintf( buf, "%s)で降参した\n", buf ); break; } /* turn+2で描きなおしたものをturnに戻しておく */ if( frame > 0 ) hex[py][px] = hexturn[1-rev]; /* 反則(2手以上を一回に打とうとした)判定 */ if( zz > 1 ) { hex[yy][xx] = hexturn[rev]+2; sprintf( buf, "%sの勝!\n", hexname2[1-rev] ); sprintf( cbuf, "%s %s\n", hexname[1-rev], hexname[rev] ); sprintf( buf, "%s ∵%sが(%s,%s)に打ったのが %d 手目\n", buf, hexname2[rev], digitcnv(xx), charcnv('A'+yy-1), zz ); break; } /* 到達判定 */ if( check( hexturn[rev], size ) ) { /* 到達 */ winpath( hexturn[rev], size ); sprintf( buf, "%sの勝!\n", hexname2[rev] ); sprintf( cbuf, "%s %s\n", hexname[rev], hexname[1-rev] ); break; } /* 打った手が分かる様にturn+2で描き直しておく */ hex[py=yy][px=xx] = hexturn[rev]+2; } disp( size ); if( msg ) printf("「%s」by %s\n", msg, hexname2[rev] ); printf("%s", buf ); if( ofp ) { fprintf( ofp, "%s", buf ); fclose( ofp ); } if( cofp ) { fprintf( cofp, "%s", cbuf ); fclose( cofp ); } return(0); } /* hex盤のサイズを求める */ int getsize() { return( size ); } /* 相手(MARU⇔BATSU)を求める */ int counter( int part ) { return( MARU + BATSU - part ); } /* hex(x,y)を求める 戻り値 ERROR(-1):異常終了、EMPTY,MARU,BATSU(0,1,2):正常終了 */ int gethex( int part, int x, int y ) { int res; if( 1 <= x && x <= size && 1 <= y && y <= size ) switch( res = hex[y][x] ) { case (MARU+2): return( MARU ); case (BATSU+2): return( BATSU ); default: return( res ); } return( ERROR ); } /* hex(x,y)をpartにする 戻り値 0:異常終了(打てない処に打とうとした 1:正常終了 打った手は(打てなくても) xx, yy に代入される zzの値(打つ前はzzが0になっている) 1:正常 2...:2手以上打とうとした */ int puthex( int part, int x, int y ) { ++zz, yy=y, xx=x; if( 1 <= x && x <= size && 1 <= y && y <= size && hex[y][x] == EMPTY ) { hex[y][x] = part; return(1); } else return(0); } /* end of hex.c */