環境
Visual Studio CodeでC言語で書かせてもらいました。
エディタはCygwin64 Terminalです。
説明目次
・オセロ盤の作成
・駒を置く処理
・駒を裏返す処理
・ここにたくさん説明が来る…
・対戦相手の選択
・ゲーム終了の処理
・番外編…AIについて
・オセロプログラムの全体像
オセロ盤の作成
8×8です。増やそうか迷いましたがとりあえず無難なものを作ります。
先に完成形を提示して話を進めていきます。
スラッシュや数字の部分はプレイヤーがどこに駒を置くかというときに必要になると思うので作成しました。
プレイヤーにどの位置に駒を置きたいかわかりやすくするために一行目に番号を並べます。
int i;
printf("\n \\ ");
for (i = 1; i < 9; i++){
printf(" %d ", i);
}
これで画像のような一列目ができます。
次に縦の番号と駒の部分を並べていきます。
左上の駒から右へ行き
1 2 3 4 5…
9…
という形に。名前は思いつかなかったので盤から 「ban[x]」にします。
空欄に何もないと寂しかったのと、数えにくかったので「+」を置きます。
int i,j=0;
char ban[64];
ban[27]=-1;
ban[28]=1;
ban[35]=1;
ban[36]=-1;
for (i = 0; i < 64; i++){
if (i % 8 == 0){
j++;
printf("\n %d ", j);
}
if (ban[i] == 0){
printf(" + ");
}else if (ban[i] > 0){
printf(" ● "); //1、黒、プレイヤー
}else{
printf(" ○ "); //-1、白、AI
}
}
「//」「/**/」は説明文です。
プログラムの通り、ban[x]に入った数字が0なら空、1ならプレイヤーの駒が、-1ならAIの駒が置かれていると認識させます。0、-1、1という数字の意味は後で触れるので今は触れないです。
数字と改行はiを8で割った時余りが出なかったら改行させ数字を先に打ち込むようにさせました。
これで、後はban[x]に数字をぶち込むだけです。
初期状態として中央四つは既におかれているのでそちらも上のプログラムに描かせてもらっています。
駒を置く処理
簡単に見えて少し面倒で、オセロには挟めないと置けないというルールがあります。
少し変?なやり方をしたのでここではプレイヤーが盤面のどこに駒を置くかを聞く処理を描いていきます。
例の如く完成形から上げます。
「printf」を使ってメッセージは誰でもできるのでそこのプログラムは省きます。
「縦」の後に「scanf」を打ち込んで縦の文字を読み取ります。
ここは「横」を先にするか非常に悩みましたが、図形の面積の公式は縦×横という…
冗談はさておき、ここでも問題は起こるもので、改行しているだけで盤面の駒は横並びです。
まあ難しく考えず、以下のようなプログラムを組みました。
int x,y,z;
//縦の数字要求ループ
for(;;){
if(z>0){
z=0;
break;
}
printf("パスをする場合0を入れてください。\n縦");
scanf("%d", &y);
if (y<9&y>0){
/*横の数字要求ループ*/
}else if(y==0){//パスの処理
break;
}else{
printf("1~8の数字を打ち込んでください。");
}
}
以上は縦の数字要求からのプログラムです。
色を変えた方が見やすいと思うのですが、文字の色は変えられない?全て同じ色になってしまうようです。
このプログラムの一番外の枠として打ち込まれた「縦」の数字が何だったのかとしています。
もちろん縦には「y」を割り振っています。
「y」が指定された範囲、提示していた舐めプ用のパス、その他の数字だった場合の3つに分けています。
0~8の数字が打たれるまで無限に話を聞きます。
あれ、初めのif文は?そう思うかもしれないので省かせてもらった横の数字ループを
//横の数字要求ループ
for(;;){
printf("縦の数字を間違えた場合は0を打ち込んでください。\n横");
scanf("%d", &x);
if(x<9&x>0){
z++;
break;
}else if(x==0){
printf("縦の数字から入力し直してください。");
break;
}else{
printf("1~8の数字を打ち込んでください。\n");
}
}
並べたらわかりにくかったので分けました。/横の数字要求ループ/の場所に上記のプログラムが入ります。
0は折角縦と横で要求分けたのでやり直し用です。
分けなくてもよかったですが、試運転中押し間違いが多くイライラしたので分けました。縦とやっていることは同じですが、ここで「break」しても縦ループに戻ったので縦ループの頭に無理矢理ループ離脱の処理をねじ込みました。汚い…。
最後にパスの処理を付け足します。
if(z>0){
printf("\nあなたはパスを選択しました。\n");
z=0;
break;
}
プレイヤーの手番をループさせる関係上、この命令で離脱できるよう、縦の数字要求ループの外にこのプログラムを置いておきます。
X=10*y;
Y=2*y;
y=X-Y;
y-=8;
x--;
x+=y;
最後は数学の時間です。これはプレイヤーが何も考えずに打った縦と横の数字をban[x]の「x」に入れれるようにする処理です。
一言でいうなら8進数→10進数の変換です。多分。
8進数→10進数の変換は調べれば出てくるのですが、なぜ8進数になっているか、何が8進数かの説明が面倒なのでしません。また、これは自分で適当に書いたので、説明があっている保証は全くないのであしからず。
プログラムの説明とは?と考える羽目になりそうなのでこの辺で。
駒を裏返す処理
この処理があほみたいにきつかったです。私は3回死にました。
結果的には力業になりました。
まずプレイヤーまたはAIが指定したban[x]が0以外の場合、つまり1か-1の場合。まあ単純に駒が置かれている場合は駒が置けないようにします。
if(ban[x]==0){
/*ここに裏返す処理が入る*/
}else{
printf("既に駒が置かれています。");
}
次にこれを突破した場合、相手の駒を自分の駒が挟めているかの処理に移ります。が、これは後回し。
文章だけではきついと思ったのであまり力にならないですが画像を置きました。
オセロは駒を置いた場所から8方向を見ることになります。
私はこれを1方向ずつ挟めているか確認することにしました。
しかしこちらから説明するとややこしくなるのでまずは裏返す処理に触れてから離させてもらいます。
改めて裏返す処理
8方向をしらみつぶしに裏返す処理をさせるので、画像のように右方向を見ていく、といったイメージを持ってください。
処理として隣接する駒に対して
・何もない
・敵の駒がある
・自分の駒がある
に分けられます。これをそれぞれの処理の場合で分けますと、
・何もない
ひっくり返していない場合終了
ひっくり返した駒を元に戻す
・敵の駒がある
駒をひっくり返す処理
盤面端の場合もとに戻す処理に渡す。
・自分の駒がある
ひっくり返していてもいなくても終了。
この処理に分けられます。
そうです、いきなりひっくり返していき、ひっくり返せなかったら元に戻すような処理にしました。他にもいろいろありましたが、一番面白そうだったのでそうしました。後悔はしています。
とりあえずそれぞれの処理のプログラムを書きます。
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[x+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[x+H+K]=-1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[x+H+K]==-1){//隣接が敵の駒
ban[x+H+K]=1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
if(T==1){
break;
}else{
l++;
break;
}
}
}
ややこしいのでコメントを多めに付けましたが、説明より何をしているのかを書きます。
ここで裏返す処理の話を中断し、隣接する駒の話をします。
8方向の隣接する駒を確認する方法
本来横並びのban[x]ですが、この「x」に8を足した場合、盤上でいうひとつ下のban[x]を参照することがわかります。これを利用して、
右なら「x」に+1、
右下なら「x」に+1+8となり+9
左上なら「x」に-1-8となり-9
を足せばよいことがわかります。これが盤面の隣接する駒の状態把握方法です。
kとhにはそれぞれ
-1、0、1を順に入れていくので、この二つの文字に1と8を乗算してban[x]の「x」に足すようにすれば上下左右ななめ全ての方向に移動を行う処理ができます。ただ、kとhが0の場合移動を行わないためエラーを吐いてしまいます。そのためその状況を省く処理をさせます。
組んだプログラムがこちらになります。
//8方向にひっくり返して成功した場合駒の配置をする。
//縦軸と横軸の確認
Y=x/8;//縦軸の座標
X=x%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//例外を除外
}
はい、閉じかっこは書くのが面倒だったので許してください。とりあえず全体像を見てもらいましたが、言っていたこと以上の処理は1回しかしていません。
このまま動かすとエラーが多かったのと、盤面の端を端と認識せず反対側の駒をひっくり返す不具合が起こったので
物理的にひっくり返せない場所の処理
というコメントの後の処理で省いています。
というのも、**右端に駒を置いて右側に裏返せる駒があると思いますか?**ありませんよね。そういうことです。右側の駒を裏返すには少なくとも右に2マスの空きがないと裏返せないことを利用しています。でないと…バグについては既に書きました。
これにて8方向の駒の状況把握方法はおしまいです。
今度こそ裏返す処理
次に裏返すプログラムを見ていきます。
上の方に行ってしまったので全く同じものですが改めて裏返す処理のプログラムを載せます。
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[x+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[x+H+K]=-1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[x+H+K]==-1){//隣接が敵の駒
ban[x+H+K]=1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
if(T==1){
break;
}else{
l++;
break;
}
}
}
中でも問題は「ひっくり返す処理」と「元に戻す処理」です。
プログラムの説明をするうえで仮に隣接した駒が敵の駒だったとします。
その場合図のような状況に陥ります。
考え方として、
上がひっくり返しを元に戻す、
真ん中がひっくり返し続行、
下がひっくり返しの確定、
状況です。
プログラムでは敵の駒が隣接していた場合例外なく先にひっくり返してその奥の駒が何かを見に行く処理をしています。この処理で大事なのがひっくり返した数のカウントです。
上の状況から見ていきます。
まず上の処理です。
上の状況は先にひっくり返してしまった駒を元に戻さないといけない状況になっています。ここでは裏返した時と同様に一つずつ裏返して元のマスに戻ってくる処理をしています。ここで必要になるのが先ほど挙げたひっくり返した数です。この数だけひっくり返せば行き過ぎることがありません。
次に真ん中の処理です。
説明した通り、続行なのですが、問題は端まで全て敵の駒だった解きです。8方向の処理の時に行き過ぎないように処理はしていますが、裏返した駒はそのままになってしまいます。
なのでループさせている「i」の数を減らし、もう一度ループさせています。ついでに「p」を利用して端まで敵の駒だったと伝える処理をしています。これにより、疑似的に上の状況を作り出して、元に戻す処理をしてもらっています。
最後に下の処理です。
やつは四天王の中で最も最弱!
というわけでひっくり返した数が増えていた場合のみ、裏返し確認しましたという意味で「l」(←小文字のエル)
の値を増やすのみです。
既にそれまでの駒は裏返した処理をしているのでこれで終わりです。
そして最後に最も必要な処理であるこのマスに駒はおけるのか、という処理が来ます。
if(ban[x]==0){
/*ここに8方向の駒状況確認とか、裏返しの処理が入る*/
if(l>0){//駒をひっくり返せたか
ban[x]=1;//今の場所に駒を配置
c++;//駒の累計加算
break;//手番を終わらせる
}else if(l==0){
printf("この場所には置けません。");
}
}else{
printf("既に駒が置かれています。");
}
最初にと最後にあるif else文
は既に説明しましたが、駒が置かれているかいないかの処理です。前後がわかりやすいよう書きました。置かれていなかった場合今までの話してきた裏返す処理に入ることができるというわけです。
裏返し確認しました、という処理が行われていれば、「l」(←小文字のエル)
が増えているのでそれを指標にif文
で最初のマスに自分の駒を置きます。もし置けないようでしたら、置けないといいプレイヤーの手番のループを利用して、はじめの縦、横を選ぶ場所からやり直してもらいます。
そして今までどこにも出ていない文字、「c」。書いてある通り駒を置いた際に増えるように設定してあります。この値は見出しゲーム終了の処理で必要になります。
対戦相手の選択
例の如く先に処理結果画面を載せます。
初めは難易度の選択としていましたが、適当なAIを作ってしまったばかりに強さの基準がばらついたのでどうせなら学生にしてしまおう、ということで学生にしました。
画像では選択肢にない「4」を選択して大学生が乱入してきています。
プログラムは以下の通りです。
//対戦相手選択画面
printf("対戦相手の番号を選択してください。\n1:小学生\n2:中学生\n3:高校生\n");
scanf("%d", &a); //以後の対戦相手指標
if (a == 1){
printf("小学生と対戦します。\n\n");
}else if (a == 2){
printf("中学生と対戦します。\n\n");
}else if (a == 3){
printf("高校生と対戦します。\n\n");
}else{
printf("大学生が乱入してきました。\n\n");
}
ではAIを作っていきます。
といっても既にある裏返す処理にランダムで出力した数字をban[x]に代入するだけの単純な作業です。
ここでは最も普通のAIとして中学生を使っていきます。
中学生は
# include <stdlib.h>
# include <time.h>
srand((unsigned int)time(NULL));
x=rand()%64;
これを裏返す処理の前の置き、3つ変更するだけです。
1つ目、駒が置けなかった場合のメッセージは邪魔なので消すこと
2つ目、裏返した駒の変更を1
→-1
、-1
→1
にすること
3つ目、パスの処理。
問題は3つ目で、見出しの今度こそひっくり返す処理の最後に描かれているプログラムを持ってきます。
if(ban[x]==0){
/*ここに8方向の駒状況確認とか、裏返しの処理が入る*/
if(l>0){//駒をひっくり返せたか
ban[x]=1;//今の場所に駒を配置
c++;//駒の累計加算
break;//手番を終わらせる
}else if(l==0){
printf("この場所には置けません。");
}
}else{
printf("既に駒が置かれています。");
}
このプログラムの最後の行、
printf("この場所には置けません。");
printf("既に駒が置かれています。");
の部分に以下のプログラムを入れます。
z++;
if(z==1000){
printf("中学生は迷っているようです。\n");
}else if(z==2000){//数値に特別な意味はない
printf("中学生はパスを選択しました。\n");
z=0;
break;//手番の終了
}
これにより置かない状況に陥るたびにzが加算され、1000回を超えたあたりで迷いだし、2000回で諦めてパスを選択します。
一応「z」は裏返しの処理には使われていないので問題なく使えます。ここ以外でも「X」「Y」を使いまわしていますが、いたずらに文字を増やしたくなかっただけです。
ゲーム終了の処理
例の如く、と言いたいところですが、64手も打つのが面倒だったので画像はありません。
ゲーム終了の処理は大きく分けて二つあり、
一つは、どちらが勝ったか
もう一つは、ゲームは終わったか
です。一つ目からプログラムを見ていきます。
X=0;//文字のリセット
Y=0;
for(i=0;i<64;i++){//盤面の駒を全て足す
X+=ban[i];
}
if(X==c){//置かれたコマが全て自分の駒か確認
Y=1;
}else if(X==-c){//置かれたコマがAIの駒か確認
Y=2;
}
//全て同じ色の駒の時の処理
if(Y>0){
if(Y==2){//全てAIの駒の時の処理
switch (a)
{
case 1:
printf("\n全て小学生の駒になりました。あなたは小学生以下です。");
break;
case 2:
printf("\n全て中学生の駒になりました。完敗です。");
break;
case 3:
printf("\n全て高校生の駒になりました。あなたの負けです。");
break;
default:
printf("\n全て大学生の駒になりました。乾杯です。");
break;
}
}else{//全て自分の駒の時の処理
printf("\nあなたの完全勝利です。おめでとうございます。");
}
break;//ゲーム終了のためループ離脱
}
コメントは自由に変更できるのでテキトーに書きます。
ここにきて自分の駒を1
、AIの駒を-1
、空欄を0
にしていた理由が出てきます。すべてを足すことにより駒が多い方の符号になるという寸法ですね。
加えて全て同じ色になった場合、全ての駒の数値の合計の絶対値
が駒の累計である「c」と同値になります。めったにありませんが(試運転中3回起こりましたが…)、盤面が埋まる前に勝敗がついたときに対応させます。
最後はゲームが終わったか、です。
プログラムとしては盤面が埋まったか、になります。
if(c==64){//盤面が埋まったか確認
if(X>0){
printf("\n%d差で勝利!\n明日はきっといいことがあります。\n",X);
}else if(X==0){
printf("\n引き分け!\nオセロに運を使い果たしてしまったのでは?\n",X);
}else{
printf("\n%d差で負け!\nどうして負けたか明日までに考えておいてください。\n",X);
}
break;//ゲーム終了のためループ離脱
}
因みに「c」の初期値は4としています。これにより、64になった時、盤面が埋まったことがわかります。勝敗は既に足しておいたban[x]の合計である「X」より判断します。引き分けはめったにありませんが一応作っております。こちらもコメントはテキトーです。
以上でAIとオセロのプログラムを終わらせてもらいます。最後まで読んでくださりありがとうございました。
番外編
ということで他につくった学生のAIについて触れます。
また、一応プログラムが連なった状態のものを最後に載せておきます。Cygwinではバグはなく遊べましたが他では知りません。
AIのコンセプトのみ書き、プログラムについては触れません。最後に乗せたプログラムに組み込んでありますし、細かくコメントを残したつもりなのでおまけ程度に考えてもらえると幸いです。正直疲れたのでここらへんで終わりたいのが本音です。
説明はふざけたので読まなくてもいいです。ただ、そういうコンセプトのプログラムと思い、流していただけたらと思います。必要かわからないですが、つまらなかったので中学生のプログラムは変更しています。
小学生AIの説明
彼らはまだオセロという概念を知らず、大人の真似をいて盤上に駒を置くことを生きがいにしています。彼らが最も厄介なのは挟むことのできない駒を量産することです。正々堂々ルールに則った戦い方をする自分と、無秩序にしょうりに固執する彼ら、どちらが強いのか是非試してみてください。
作者コメ ぶっちゃけ弱いです。序盤は0でパスしてください。
中学生AIの説明
彼らは常識を搭載した小学生です。空っぽの頭で成長した体を動かすゾンビにほかなりません。彼らは終盤に駒を置けなくなったときに本領を発揮します。相手の駒の上に自分の駒を置いてきます。乗っ取るさまはゾンビのようですね。
作者コメ 乗っ取りが割と強かったです。乗っ取り際、盤面に駒は増えていないので「c」の数を増やさないように注意しました。乗っ取りがなければAIの雛形です。
高校生AIの説明
彼らは人生で最も常識人であった時代です。普通のオセロを展開されて間違いなく動揺するでしょう。世には常識をもったAIがほとんどなので、高校生AIは面白みに欠けると思います。もし高校生ともっと遊びたいならば、是非ほかのサイトで楽しんでいただけたら幸いです。
作者コメ 一番多くひっくり返すマスに駒を置くようにプログラムしています。強いというよりうざかったです。一番普通なのに一番プログラムに時間がかかりました。
大学生AI
彼らは馬鹿であり、大雑把です。そのため中盤で囲碁の駒を置いてきます。この駒は通称「不死の駒」と呼ばれ、忠誠を誓った主人にのみ従属します。裏返っても色を変えない裏表がない誠実さは、主人から零れ落ちたのかもしれません。
作者コメ 囲碁の駒が最もバグを起こしました。序盤だと強すぎたので、10手経過後に置くようプログラムしました。他にも一定確率で置かせようかと思いましたが、数が増えると勝ち目がなくなったので自重しました。囲碁の駒を置くこと以外は中学生です。深い意味はありません。
以下、作成したプログラムの全体像です。800行でした。
オセロプログラム
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
int main()
{
int a,b,c,d=-1,h,H,i,j=0,k,K,l=0,p=0,m,n,r,R=0,x,y,t,T,V,W,X,Y,Z,z=0;
char ban[64];
//対戦相手選択画面
printf("対戦相手の番号を選択してください。\n1:小学生\n2:中学生\n3:高校生\n");
scanf("%d", &a); //以後の対戦相手指標
if (a == 1){
printf("小学生と対戦します。\n\n");
}else if (a == 2){
printf("中学生と対戦します。\n\n");
}else if (a == 3){
printf("高校生と対戦します。\n\n");
}else{
printf("大学生が乱入してきました。\n\n");
}
//駒の定義
//盤面のリセット
for (i = 0; i <64 ; i++){
ban[i] = 0;
}
//初期駒の配置
ban[27] = -1;
ban[28] = 1;
ban[35] = 1;
ban[36] = -1;
c=4;//駒のカウント
//ゲーム終了までのループ処理
for(b=0;;b++){
//盤の読み込み関数
printf("\n \\ ");
for (i = 1; i < 9; i++){
printf(" %d ", i);
}
j=0;
for (i = 0; i < 64; i++){
if (i % 8 == 0){
j++;
printf("\n %d ", j);
}
if (ban[i] == 0){
printf(" + ");
}else if (ban[i] > 0){
printf(" ● ");//1、黒、プレイヤー、先行
}else if(R>9&i==d){
printf(" ◦ ");//-1、永遠の白、大学生の囲碁の駒
}else{
printf(" ○ ");//-1、白、AI、後攻
}
}
l=0;
//ゲーム終了の処理
X=0;//文字のリセット
Y=0;
for(i=0;i<64;i++){//盤面の駒を全て足す
X+=ban[i];
}
if(X==c){//置かれたコマが全て自分の駒か確認
Y=1;
}else if(X==-c){//置かれたコマがAIの駒か確認
Y=2;
}
//全て同じ色の駒の時の処理
if(Y>0){
if(Y==2){//全てAIの駒の時の処理
switch (a)
{
case 1:
printf("\n全て小学生の駒になりました。あなたは小学生以下です。");
break;
case 2:
printf("\n全て中学生の駒になりました。完敗です。");
break;
case 3:
printf("\n全て高校生の駒になりました。あなたの負けです。");
break;
default:
printf("\n全て大学生の駒になりました。乾杯です。");
break;
}
}else{//全て自分の駒の時の処理
printf("\nあなたの完全勝利です。おめでとうございます。");
}
break;//ゲーム終了のためループ離脱
}
if(c==64){//盤面が埋まったか確認
if(X>0){
printf("\n%d差で勝利!\n明日はきっといいことがあります。\n",X);
}else if(X==0){
printf("\n引き分け!\nオセロに運を使い果たしてしまったのでは?\n",X);
}else{
printf("\n%d差で負け!\nどうして負けたか明日までに考えておいてください。\n",X);
}
break;//ゲーム終了のためループ離脱
}
//駒入力
if(b%2==0){//どちらの手番かの処理
//プレイヤーの手番処理
printf("\nあなたの手番です。あなたの色は「●」です。");
for(;;){
printf("\n駒を置く縦の数字と横の数字を入れてください。\n");
//縦の数字要求ループ
for (;;){
//ループ離脱処理
if(z>0){
z=0;
break;
}
printf("パスをする場合0を入れてください。\n縦");
scanf("%d", &y);
if (y<9&y>0){
//横の数字要求ループ
for(;;){
printf("縦の数字を間違えた場合は0を打ち込んでください。\n横");
scanf("%d", &x);
if(x<9&x>0){
z++;
break;
}else if(x==0){
printf("縦の数字から入力し直してください。");
break;
}else{
printf("1~8の数字を打ち込んでください。\n");
}
}
}else if(y==0){//パスの処理
z++;
break;
}else{
printf("1~8の数字を打ち込んでください。");
}
}
//パスの処理
if(z>0){
printf("\nあなたはパスを選択しました。\n");
z=0;
break;
}
//入力数字をban[]の中に入れられるxに変換
X=10*y;
Y=2*y;
y=X-Y;
y-=8;
x--;
x+=y;
//駒の配置
//空いているか
if(ban[x]==0){
//8方向にひっくり返して成功した場合駒の配置をする。
//縦軸と横軸の確認
Y=x/8;//縦軸の座標
X=x%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//例外を除外
}else{
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[x+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[x+H+K]=-1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[x+H+K]==-1){//隣接が敵の駒
ban[x+H+K]=1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
if(T==1){
break;
}else{
l++;
break;
}
}
}
}
}
}
}
if(l>0){//駒をひっくり返せたか
ban[x]=1;//今の場所に駒を配置
c++;//駒の累計加算
break;//手番を終わらせる
}else if(l==0){
printf("この場所には置けません。もう一度");
}
}else{
printf("既に駒が置かれています。もう一度");
}
}
if(R>9){//大学生の置いた囲碁の駒
ban[d]=-1;
}
//AIの手番処理
}else{
switch (a){
//小学生AI
case 1:
printf("\n小学生の手番です。\n");
srand((unsigned int)time(NULL));
for(;;){//駒が置けるまでループ
x=rand()%64;
if(ban[x]==0){
c++;
//8方向にひっくり返して成功した場合駒の配置をする。
//縦軸と横軸の確認
Y=x/8;//縦軸の座標
X=x%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//例外を除外
}else{
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[x+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[x+H+K]=1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[x+H+K]==1){//隣接が敵の駒
ban[x+H+K]=-1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
break;//挟めていなくても置く
}
}
}
}
}
}
ban[x]=-1;//駒が無ければそこに置く
z=0;//パスカウントのリセット
break;//駒がおけたのでループ離脱
}else{
if(z==10){//10回置ける場所を探しパスを選択する。
z=0;
printf("小学生は置ける場所を見つけられなかったようです。\n");
break;
}
z++;
}
}
break;//小学生AIの終了
//中学生AI
case 2:
printf("\n中学生の手番です。\n");
srand((unsigned int)time(NULL));
for(;;){
x=rand()%64;
if(ban[x]==0|r==1){//rはどこも置けない場合どこでも駒を置く処理
//8方向にひっくり返して成功した場合駒の配置をする。
//縦軸と横軸の確認
Y=x/8;//縦軸の座標
X=x%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//中心を除外
}else{
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[x+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[x+H+K]=1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[x+H+K]==1){//隣接が敵の駒
ban[x+H+K]=-1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
if(T==1){
break;
}else{
l++;
break;
}
}
}
}
}
}
}
z++;
if(l>0|r==1){//駒をひっくり返せたか
if(ban[x]==0){//駒の上に駒を置いていないかの確認
c++;//駒の累計加算
}
ban[x]=-1;//今の場所に駒を配置
z=0;
r=0;
break;//駒を置いたのでループ離脱
}else if(l==0){
if(z==1000){
printf("中学生は迷っているようです。\n");
}else if(z==2000){//数値に特別な意味はない
printf("中学生は無理矢理駒を置きました。\n");
r=1;//置けているかいないかの確認を突破する
z=0;
}
}
//駒が置けない場合
}else{
z++;
if(z==1000){
printf("中学生は迷っているようです。\n");
}else if(z==2000){//数値に特別な意味はない
printf("中学生は無理矢理駒を置きました。\n");
r=1;//置けているかいないかの確認を突破する
z=0;
}
}
}
break;//中学生AI終了
//高校生AI
case 3:
printf("\n高校生の手番です。\n");
n=0;//ひっくり返せる数のカウント
m=0;//最大値のリセット
srand((unsigned int)time(NULL));
for(;;){
x=rand()%64;
if(ban[x]==0){
//8方向にひっくり返して成功した場合駒の配置をする。
//縦軸と横軸の確認
Y=x/8;//縦軸の座標
X=x%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//中心を除外
}else{
//ひっくり返せる数のカウント
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
if(ban[x+H+K]==0){//隣接がない
break;
//ひっくり返せる駒のカウント
}else if(ban[x+H+K]==1){//隣接が敵の駒
T++;//ひっくり返した数
//挟めているかの確認
}else{//隣接が自分の駒
if(T==1){
break;
}else{
l++;
n=n+T-1;//ひっくり返した数のカウント
break;
}
}
}
}
}
}
}
z++;
if(l>0){//駒をひっくり返せたか
if(m<n){//ひっくり返した数の最大値と今回ひっくり返せた数の比較
m=n;
r=x;//座標の記憶
}
if(z>10000){//適当な試行回数をこなしたか確認
//縦軸と横軸の確認
Y=r/8;//縦軸の座標
X=r%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//中心を除外
}else{
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[r+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[r+H+K]=1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[r+H+K]==1){//隣接が敵の駒
ban[r+H+K]=-1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
break;
}
}
}
}
}
}
ban[r]=-1;//今の場所に駒を配置
c++;//駒の累計加算
z=0;
break;//駒を置いたのでループ離脱
}
}else if(l==0){//ひっくり返せなかった
if(z==100000){//数値に特別な意味はない
printf("高校生はパスを選択しました。\n");
z=0;
break;//パスしたためループ離脱
}
}
n=0;
//駒が置けない場合
}else{
z++;
if(z==100000){//数値に特別な意味はない
printf("高校生はパスを選択しました。\n");
z=0;
break;//パスしたためループ離脱
}
}
}
break;//高校生AI終了
//大学生AI
default :
printf("\n大学生の手番です。\n");
srand((unsigned int)time(NULL));
for(;;){
x=rand()%64;
if(ban[x]==0|r==1){//rはどこも置けない場合どこでも駒を置く処理
//8方向にひっくり返して成功した場合駒の配置をする。
//縦軸と横軸の確認
Y=x/8;//縦軸の座標
X=x%8;//横軸の座標
t=0;//ひっくり返しが成功した数のリセット
//左右の仕分け
for(h=-1;h<2;h++){
V=0;//h=0の時にバグらないため
//横軸の範囲の調整
if(h==-1|h==1){
V=X;
if(h==-1){//数値の方向を整理する処理
V=X-7;
V=-V;
}
}
//物理的にひっくり返せない左右の場所の除外
if(h==1&X>=6){
}else if(h==-1&X<=1){
}else{
//上下の仕分け
for(k=-1;k<2;k++){
W=0;//k=0の時にバグらないため
T=1;//ひっくり返した数の定義
Z=V;
//縦軸の範囲の調整
if(k==-1|k==1){
W=Y;
if(k==-1){//数値の方向を整理する処理
W=Y-7;
W=-W;
}
if(V<W){//数値がギリギリな方に合わせる
Z=W;
}
}
//物理的にひっくり返せない上下の場所を除外
if(k==-1&Y<=1){
}else if(k==1&Y>=6){
}else if(h==0&k==0){//中心を除外
}else{
//裏返す処理
for(i=1;i<8-Z;i++){
K=k*i*8;//上下の変数
H=i*h;//左右の変数
//もとに戻す処理
if(ban[x+H+K]==0|p==1){//隣接がない
p=0;//端まで敵駒だった時のフラグリセット
if(T==1){
break;
}else{
t=T-1;
for(j=1;j<T;j++){
K=t*k*8;
H=t*h;
ban[x+H+K]=1;
t--;
}
break;
}
//ひっくり返す処理
}else if(ban[x+H+K]==1){//隣接が敵の駒
ban[x+H+K]=-1;
T++;//ひっくり返した数
if(i==7-Z){//端まで敵の駒だった時の処理
i--;
p=1;
}
//挟めているかの確認
}else{//隣接が自分の駒
if(T==1){
break;
}else{
l++;
break;
}
}
}
}
}
}
}
z++;
if(l>0|r==1){//駒をひっくり返せたか
R++;
if(R==10){//囲碁の駒を置く処理
d=x;//置く場所の記憶
printf("どこからか囲碁の駒を持ってきました。ひっくり返しても色が同じです。\n");
}
if(ban[x]==0){//駒の上に駒を置いていないかの確認
c++;//駒の累計加算
}
ban[x]=-1;//今の場所に駒を配置
z=0;
r=0;
break;//駒を置いたのでループ離脱
}else if(l==0){
if(z==5000){
printf("大学生はトイレに行っています。\n");
}else if(z==10000+c){//数値に特別な意味はない
printf("酔った勢いで適当に駒を置きました。\n");
r=1;//置けているかいないかの確認を突破する
z=0;
}
}
//駒が置けない場合
}else{
z++;
if(z==5000){
printf("お酒を飲み過ぎて気持ち悪そうです。\n");
}else if(z==10000+c){//数値に特別な意味はない
printf("酔っていて正常な判断ができないようです。\n");
r=1;//置けているかいないかの確認を突破する
z=0;
}
}
}
if(R>9){//大学生の置いた囲碁の駒
ban[d]=-1;
}
break;//大学生AI終了
}
}
}
}
関数を使いたかったのですが、エラーを吐きまくって面倒になりました。製作期間は伏せ、疲れたとだけ残して終わらせてもらいます。