コンソールで動くオセロ
C言語でオセロを作りました
以下なが~いソースです
長いので続きからへ↓
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<unistd.h> enum type{black,white,space}; enum type board[8][8]; enum type user; enum type com; struct vector{ int x; int y; int value; }; void ShowBoard() { int i,j; printf(" 一二三四五六七八\n"); for(j=0;j<8;j++){ printf("%d",j+1); for(i=0;i<8;i++){ switch(board[j][i]){ case space: printf("・"); break; case black: printf("○"); break; case white: printf("●"); break; } } printf("\n"); } printf("\n"); } void copyboard(enum type a[][8],enum type b[][8]) { int i,j; for(j=0;j<8;j++) for(i=0;i<8;i++) a[j][i] = b[j][i]; } int ReverseStone(int x,int y,enum type turn) { int s,t,p,q,num=0; //横 if(board[y][x] != space) return 0; for(t=y-1;t<=y+1;t++) for(s=x-1;s<=x+1;s++){ if(s<0 || s>7 || t<0 || t>7) continue; p = s; q = t; if(y-1 == q){ if(p == x-1){ if(board[y-1][x-1] != turn && board[y-1][x-1] != space){ while(board[q][p] != turn && board[q][p] != space) if(q-1 >= 0 && p-1 >= 0){ q--; p--; }else break; if(board[q][p] == turn){ while(++q<=y-1 && ++p<=x-1){ num++; board[q][p] = turn; } } } }else if(p == x){ if(board[y-1][x] != turn && board[y-1][x] != space){ while(board[q][p] != turn && board[q][p] != space) if(q-1 >= 0){ q--; }else break; if(board[q][p] == turn){ while(++q<=y-1){ num++; board[q][p] = turn; } } } }else if(p== x+1){ if(board[y-1][x+1] != turn && board[y-1][x+1] != space){ while(board[q][p] != turn && board[q][p] != space) if(q-1 >= 0 && p+1 <= 7){ q--; p++; }else break; if(board[q][p] == turn){ while(++q<=y-1 && --p>=x+1){ num++; board[q][p] = turn; } } } } }else if(y == q){ if(p == x-1){ if(board[y][x-1] != turn && board[y][x-1] != space){ while(board[q][p] != turn && board[q][p] != space) if(p-1 >= 0){ p--; }else break; if(board[q][p] == turn){ while(++p<=x-1){ num++; board[q][p] = turn; } } } }else if(p== x) continue; else if(p == x+1){ if(board[y][x+1] != turn && board[y][x+1] != space){ while(board[q][p] != turn && board[q][p] != space) if(p+1 <= 7){ p++; }else break; if(board[q][p] == turn){ while(--p>=x+1){ num++; board[q][p] = turn; } } } } }else if(q == y+1){ if(p == x-1){ if(board[y+1][x-1] != turn && board[y+1][x-1] != space){ while(board[q][p] != turn && board[q][p] != space) if(q+1 <= 7 && p-1 >= 0){ q++; p--; }else break; if(board[q][p] == turn){ while(--q>=y+1 && ++p<=x-1){ num++; board[q][p] = turn; } } } }else if(p == x){ if(board[y+1][x] != turn && board[y+1][x] != space){ while(board[q][p] != turn && board[q][p] != space) if(q+1 <= 7){ q++; }else break; if(board[q][p] == turn){ while(--q>=y+1){ num++; board[q][p] = turn; } } } }else if(p == x+1) if(board[y+1][x+1] != turn && board[y+1][x+1] != space){ while(board[q][p] != turn && board[q][p] != space) if(q+1 <= 7 && p+1 <= 7){ q++; p++; }else break; if(board[q][p] == turn){ while(--q>=y+1 && --p>=x+1){ num++; board[q][p] = turn; } } } } } if(num) board[y][x] = turn; return num; } void CPU(enum type turn) { enum type b[8][8]; int c,i,j,max=0; struct vector v[8][8]; copyboard(b,board); for(j=0;j<8;j++) for(i=0;i<8;i++){ v[j][i].x = i; v[j][i].y = j; v[j][i].value = ReverseStone(i,j,turn); copyboard(board,b); } for(j=0;j<8;j++) for(i=0;i<8;i++) if(max < v[j][i].value) max = v[j][i].value; while(1){ for(j=0;j<8;j++) for(i=0;i<8;i++){ if(v[j][i].value == max) if(rand() % 8 == 0){ if(!(c = ReverseStone(v[j][i].x,v[j][i].y,turn))) printf("CPU : う~ん、パス!w\n"); ShowBoard(); if(c) printf("CPU : Your Turn... !!! (with putting stone on (%d,%d)\n",i+1,j+1); return; } } } } void PutStone(enum type turn) { int x,y; char input[4]; if(turn == user){ printf("Now is your turn !!\n"); do{ scanf("%d %d",&x,&y); x--; y--; if(!ReverseStone(x,y,turn)) printf("Youcan't put stone there !!\n"); else break; }while(1); ShowBoard(); printf("You : I set Stone (%d,%d) !! Turn End !!\n",x+1,y+1); }else{ CPU(turn); } } int CheckBoard() { int i,j; for(i=0;i<8;i++) for(j=0;j<8;j++) if(board[i][j] == space) return 0; return 1; } enum type JudgeGame() { int i,j,w=0,b=0; for(j=0;j<8;j++) for(i=0;i<8;i++) if(board[j][i] == white) w++; else if(board[j][i] == black) b++; if(w > b) return white; if(w < b) return black; else return space; } void EndGame() { int b=0,w=0,i,j; enum type win = JudgeGame(); printf("Game Maker : 結果発表~~~~!!!\n"); if(win == space){ printf("Game Maker : Draw !!!!!\n"); return; } if(user == black){ if(win == user) printf("Game Maker : Player , WIN !!!!\n"); else{ printf("Game Maker : Player , LOSE ↓↓\n"); } }else if(user == white){ if(win == user) printf("Game Maker : Player , WIN !!!!\n"); else{ printf("Game Maker : Player, LOSE ↓↓\n"); } printf("We wait for your next challenge !!!\n"); printf("------END-------"); } } int play() { enum type t = black; while(1){ PutStone(t); if(CheckBoard()) break; if(t == black) t = white; else t = black; } EndGame(); } int CPUvs() { int u,c; for(u=0;u<8;u++) for(c=0;c<8;c++) board[u][c] = space; srand((unsigned)time(NULL)); board[3][3] = board[4][4] = white; board[3][4] = board[4][3] = black; enum type t = black; user = black; com = white; while(1){ //sleep(1); CPU(t); if(CheckBoard()) break; if(t == black) t = white; else t = black; } printf("----終戦----\n"); } void SetBoard(){ int u,c; for(u=0;u<8;u++) for(c=0;c<8;c++) board[u][c] = space; srand((unsigned)time(NULL)); board[3][3] = board[4][4] = white; board[3][4] = board[4][3] = black; printf("Dice was thrown !!\n"); do{ printf("Your number is %d !\n",u = rand() %6 +1); printf("Com number is %d !\n",c = rand() %6 +1); }while(u == c); printf("So %s get first move!!\n",u > c ? "user" : "com"); printf("Ready...Fight!!\n"); if(u > c){ user = black; com = white; }else{ user = white; com = black; } } int main(int argc,char* argv[]) { int i,b=0,w=0,d=0,j; if(argc > 1){ for(i=0;i<2000;i++){ CPUvs(); if(j = JudgeGame() == white) w++; else if(j == black) b++; else d++; } printf("White Win = %d\n",w); printf("Black Win = %d\n",b); printf("Draw = %d\n",d); } else{ SetBoard(); ShowBoard(); play(); } }
実行例
$ ./osero Dice was thrown !! Your number is 6 ! Com number is 3 ! So user get first move!! Ready...Fight!! 一二三四五六七八 1・・・・・・・・ 2・・・・・・・・ 3・・・・・・・・ 4・・・●○・・・ 5・・・○●・・・ 6・・・・・・・・ 7・・・・・・・・ 8・・・・・・・・ Now is your turn !! 4 3 一二三四五六七八 1・・・・・・・・ 2・・・・・・・・ 3・・・○・・・・ 4・・・○○・・・ 5・・・○●・・・ 6・・・・・・・・ 7・・・・・・・・ 8・・・・・・・・ You : I set Stone (4,3) !! Turn End !! 一二三四五六七八 1・・・・・・・・ 2・・・・・・・・ 3・・・○・・・・ 4・・・○○・・・ 5・・●●●・・・ 6・・・・・・・・ 7・・・・・・・・ 8・・・・・・・・ CPU : Your Turn... !!! (with putting stone on (3,5) Now is your turn !! 3 6 一二三四五六七八 1・・・・・・・・ 2・・・・・・・・ 3・・・○・・・・ 4・・・○○・・・ 5・・●○●・・・ 6・・○・・・・・ 7・・・・・・・・ 8・・・・・・・・ You : I set Stone (3,6) !! Turn End !! 一二三四五六七八 1・・・・・・・・ 2・・・・・・・・ 3・・・○●・・・ 4・・・●●・・・ 5・・●○●・・・ 6・・○・・・・・ 7・・・・・・・・ 8・・・・・・・・ CPU : Your Turn... !!! (with putting stone on (5,3) Now is your turn !! ...
フォントの関係で盤がみづらいですが実際にはこんな感じ↓
荒削りのソースなのでもっと短くは書けそうですが...
にしても盤面の一マスの状態が高々3つなので、enumの配列で盤面を作ってみたらだいぶ書きやすかったです
実行例だと途中までしか書いてませんが、最後までプレイできます。
scanfで(x y)形式の数字を受け取るので、うっかり入力をミスるとゲームが固まります;;
別の実装方法も考えましたが、面倒だったので、ユーザーの良心を信じてscanfにしました。
このオセロを作るにあたって一番面倒だったのが石をひっくり返す関数のReverseStoneです
特にアイデアもなにもなかったので、石を置いたまわり八方向についてすべて調べ、ひっくり返せるものをすべてひっくり返すということを書いてあるので、とても長い割には同じようなことが書いてある、あんまりよくない関数となっています。これだけ長かったので、デバッグでたくさんミスが見つかりました...。CPUのAIも作ったのですが、オセロについての知識も乏しく、どのようなものがよいのかわからなかったので、たくさん石がとれるところに適当に置く、としました。乱数を用いているので毎回同じ場所には指さないようになっています。CPUは石がおける場所がないときはパスをするようになっているのですが、プレイヤーのパス判定は実装していないのでちょっとガバガバなオセロになっています。
おまけとして、コマンドライン引数になんか入れるとCPU同士で2000回対戦して先手と後手の勝数を集計して表示するようにしました。さくっと作っただけなので途中の盤面はすべて表示されてコンソールがしっちゃかめっちゃかになりますが、面白い結果が見られます。これを見ていて、お互いに盤面に石がおけない状況になるとお互いにパスしまくって無限ループになる問題を見つけたんですけど、これはこれからの課題にしておきたい...
オセロのAIとかその辺は流行りのディープラーニングでなんとか作れないですかねー?