前提
NxNマスのマルバツゲームを作っています。
プレイヤー同士で対戦します。
実感としては90%ぐらいで来たと思います。
実現したいこと
詰まっている点は2つです。
- 判定方法のところのコードが間違っているのですが、それがなぜなのか全く分かりません。
- 勝負がついた時に、どのようにしてループを抜け出すのか(一応自分が終わって欲しいと思うところにbreakとは書いているのですが、下のパターン1、2の様に判定方法が間違っているせいなのかループから抜け出してくれません。)
発生している問題・エラーメッセージ
パターン1 OX_ XXO OXO x座標を0〜2から入力してください: //この様になっても終わらない。続いてしまう。さらに続けると OXO XXO OXO 引き分けです //となってしまう。 ____________________ パターン2.0 OXO XO_ OX_ //この様になっても終わらない パターン2.1 OXO XOX OXO Oの勝ちです! //パターン2.0をこの状態まで続けるとなぜか終わる。
該当のソースコード
C言語
1#include <stdio.h> 2 3int main() { 4 5 int n; 6 int while_count = 0;//全体をカウントする。これの偶奇でプレイヤー1、2を判断する。 7 printf("マスの数を入力してください:"); 8 scanf("%d", &n); 9 10 //ここからボードを作ってく 11 int x = n, y = n; 12 char board[y][x]; 13 int i, j; 14 15 for (i = 0; i < n ; i++){ 16 printf("\n"); 17 for(j = 0; j < n ; j++){ 18 printf("_ "); 19 board[i][j] = '_'; 20 } 21 } 22 printf("\n"); 23 24 /* 25 whileの中でやること 26 1.◯を入力する(◯スタート。偶数回目であれば◯、奇数回目であればXを入力。1回目からスタート?それとも0回目?調べる) 27 2.◯の場所が(A)範囲内で(B)空白なのかどうか確認 28 3.◯を記入して、ボードを表示 29 4.勝敗を判断 30 5.ターンを進める 31 6.1に戻る 32 */ 33 while(1){ 34 //1.入力する+2.入力チェック 35 do{ 36 printf("x座標を0〜%dから入力してください:", n - 1); 37 scanf("%d", &x); 38 printf("y座標を0〜%dから入力してください:", n - 1); 39 scanf("%d", &y); 40 41 if(x < 0 || x > n - 1 || y < 0 || y > n - 1){ 42 printf("入力された値が不正です。\n"); 43 }else if(board[y][x] != '_'){ 44 printf("そのマスは埋まってます。\n"); 45 } 46 }while((x < 0 || x > n - 1 || y < 0 || y > n - 1) || (board[y][x] != '_')); 47 48 //3.◯xを記入して、 49 if(while_count % 2 == 0){ 50 board[y][x] = 'O'; 51 }else{ 52 board[y][x] = 'X'; 53 } 54 55 //ボードを表示 56 for (i = 0; i < n ; i++){ 57 printf("\n"); 58 for(j = 0; j < n ; j++){ 59 printf("%c", board[i][j]); 60 } 61 } 62 printf("\n"); 63 64 //4勝敗の判断。縦横斜めが同じかどうかをループで判断引き分け(全マス埋まるも考える)。 65 char kigou; //if文を使って偶数回目であればkigou='O',奇数回目であればkigou='X'を代入 66 if(while_count % 2 == 0){ 67 kigou = 'O'; 68 }else{ 69 kigou = 'X'; 70 } 71 72 int count1 = 0; 73 for(y = 0; y < n; y++){//横方向の行をn行分チェック。countで数えてその回数とnが同じであれば勝ちになるはず。 74 count1 = 0; 75 for(x = 0; x < n; x++){ 76 if(board[y][x] == kigou){ 77 printf("count1 %d\n", count1); 78 count1++; 79 } 80 } 81 if(count1 == n){ 82 break; 83 } 84 } 85 if(count1 == n){ 86 printf("%cの勝ちです!\n", kigou); 87 break; 88 } 89 90 int count2 = 0; 91 for(x = 0; x < n; x++){//縦方向の行をn列分チェック。countで数えてその回数とnが同じであれば勝ちになるはず。 92 count2 = 0; 93 for(y = 0; y < n; y++){ 94 if(board[y][x] == kigou){ 95 printf("count2 %d\n", count2); 96 count2++; 97 } 98 } 99 if(count2 == n){ 100 break; 101 } 102 } 103 if(count2 == n){ 104 printf("%cの勝ちです!\n", kigou); 105 break; 106 } 107 108 109 int count3 = 0; 110 int a; 111 for(a = 0; a < n; a++){//左上から右下に向かってチェックする。countで数えてその回数とnが同じであれば勝ちになるはず。 112 if(board[a][a] == kigou){ 113 printf("count3 %d\n", count3); 114 count3++; 115 } 116 } 117 if(count3 == n){ 118 printf("%cの勝ちです!\n", kigou); 119 break; 120 } 121 122 int count4 = 0; 123 for (a = 0; a < n; a++){//右上から左下に向かってチェックする。countで数えてその回数とnが同じであれば勝ちになるはず。 124 if(board[n - 1 - a][a] == kigou){ 125 printf("count4 %d\n", count4); 126 count4++; 127 } 128 } 129 if(count4 == n){ 130 printf("%cの勝ちです!\n", kigou); 131 break; 132 } 133 134 if(while_count == n*n - 1){// 135 printf("引き分けです"); 136 break; 137 } 138 //5.ターンを進める 139 printf("\n%d周目終わり\n", while_count); 140 while_count++; 141 printf("\n%d周目始まる\n", while_count); 142 } 143 printf("\n"); 144 return 0; 145} 146 147
試したこと
判定方法に問題があると考えてので、76行目から125行目を変えたりして試しました。ただ自分が判定方法の何が問題なのか理解できていなかったため、何も分からなかったのが本音です。
判定方法の考え方としては、例えば76行目から86行目が横方向への判定方法なのですが、y=0(0行目)の時、二つ目のforでxを0からn-1(3x3のゲームであれば、nは2)まで動かし、if文の中でkigouが何度boardと同じなのかを数え、countの数とnが同じになれば”勝ち”となる様に考えました。
しかし実際には勝っていても終わらなかったり、引き分けと判定してしまうのです。
補足情報(FW/ツールのバージョンなど)
Macbook Air M1 2020 Ver 12.6
Xcode を使っています。
ここにより詳細な情報を記載してください。
>一応自分が終わって欲しいと思うところにbreakとは書いているのですが
コンピュータに『思い』を伝えても意味はありません。プログラムの書いてある通りにしか動きません。
書いた break が考えている動作を実現するためになっているのかは、書いた側が分かっていなければなりません。
>3時間ぐらい色々試しましたが
何を試したのでしょう。
調査したのでしたら、何処まで分かったのかを教えて下さい。
判定方法に問題があると考えてので、76行目から125行目を変えたりして試しました。ただ自分が判定方法の何が問題なのか理解できていなかったため、何も分からなかったのが本音です。
判定方法の考え方としては、例えば76行目から86行目が横方向への判定方法なのですが、y=0(0行目)の時、二つ目のforでxを0からn-1(3x3のゲームであれば、nは2)まで動かし、if文の中でkigouが何度boardと同じなのかを数え、countの数とnが同じになれば”勝ち”となる様に考えました。
しかし実際には勝っていても終わらなかったり、引き分けと判定してしまうのです。
勝ちとしての判断そのものがされているかどうかが怪しいのであれば、説明されている通りに本当に各行が実行されているのか、一行毎に行番号の表示を入れて実行してみては如何でしょうか。
もう締切すぎてるんじゃないのかしら。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13272434668
> 2022/12/14 11:21
> 課題の提出期限が12時間後で
いきなりC言語で考えようとしてませんか。それは結構難しいことなんだから、まず日本語で判定条件を「ちゃんと」記述してみて、それをCに翻訳するという手順でやってみたらどうかと思うのですが。
ご指摘を頂いた通り、コードを修正した所コードが完成しました。
ループの中のcountをprintfすることで色々な間違いに気づくことが出来ました。
ただ一つだけ、コードが完成しても意味がわからなかった点があったので重ねて質問したいです。
78、93行目のcount1, count2をループの外ではなく一つ目のループの中に初期化しておいたら、何故かコードが正常に動きました。それまでは、例えば◯の数が3個さえあれば、たとえそれが連続でなくとも勝敗が決まってしまい、正常に動かなかったのですが、count1, count2を一つ目のループの中に置いた途端、コードが正常に動く様ななりました。これの意味が分かりません。教えてもらえると嬉しいです。
アホな質問かもしれませんが15時間ほどこのコードと睨めっこしていたのでもう本当に頭が働きません。
お願いします。
>15時間ほどこのコードと睨めっこしていた
仕方がない状況とは察しますが、長時間グダクダな状態で続けるよりも、適度に休憩・リフレッシュしてからやった方が結果的に効率が良いことはほぼ常識です。
>count1, count2を一つ目のループの中に置いた途端、コードが正常に動く
そうすることで動くと理解してからやったのではないということでしょうか。
だとすれば、プログラムの作り方を間違っています。家を建ててからどうして建ってるのか分からないなんていう施工者がいるでしょうか。
count1 や count2 は 外側のループが示す各行(もしくは各列)内のカウントをするのですから、1つの行(もしくは列)のカウントが終わって次の行(もしくは列)のカウントを始める時にはまず 0 にしなければ、前までのカウントに更に加算していくことになるのは当然でしょう。
ループが終われば使った変数が勝手に 0 になったりはしません。変数は、代入するコードを書かない限り変化しません。
またついでですが、現在の質問のコードはまだバグがあります。
また、判定部分ではありませんが『そのマスは埋まってます。』が埋まってなくても表示されるようですが、それで良いのでしょうか。
>適度に休憩・リフレッシュしてからやった方が結果的に効率が良いことはほぼ常識です。
無茶苦茶反省しています。
>count1 や count2 は 外側のループでが示す
ごもっともです。気づきませんでした。
>現在の質問のコードはまだバグがあります。
すみません。全く分かりません。Xcodeを使っているのですが、エラーや警告は一切出ていませんし、ゲームは正常に動いてました。
どの点なのか教えてもらえないでしょうか?本当にすみません。
バグというのは、コンパイルエラー等とは違ってツールで簡単に見付かるものではありません。だからこそ時には銀行のシステムが長時間止まるような事態も発生するのです。
count1 や 2 の判定が 2 重ループの外にあります。両カウンタはそれぞれの 2 重ループの内側で何度も 0 になっては加算を繰り返しており、 2 重ループが終わったときに設定されているのは 2 重ループの最後のループの結果だけです。
それを判定するということは、最後以外の行や列は判断出来ていないということです。
>『そのマスは埋まってます。』
これも色々と試してみましたが、全く分かりません。どの様に数字を入力したらそうなるかだけでも教えてもらえないでしょうか?本当にお願いします。
そちらでは出ていないでしょうか。だとしたら質問に提示されているコードはそちらとは違うのでしょうか。
範囲外を入力すると『入力された値が不正です』になりますが範囲内を入力すると『そのマスは埋まってます』になって、でも実際は埋まってなければ先に進みます。
原因は、埋まっていますの printf が範囲チェックの if の else にあるからでしょう。
本当は else if で、埋まっていたら printf を実行となるのではないでしょうか。
「そのマスは埋まってます。」はこれでいいでしょうか?何度も試しましたが何故か自分のコードではそのようなエラーは出ませんでした。因みに具体的に何と入力しましたか?
またcount1, count2を一つ目のループの後に入れてみましたが、まだ正常に動かないので考え直します。
この場合breakはどこに書けばいいのでしょうか?
そのマスは埋まっていますのメッセージは直ったようです。やはりそちらの最新のコードと質問のコードに解離があったのですね。
count の問題は、 判定の if を中に入れてしまうと、ターンの while を抜けるためだった break が 2 重ループの外側のループを抜けるものに役割が変わってしまうためです。
解決方法の1つは、中と外の両方に同じ判定の if ( とその中の break) を置き、ただ printf だけはどちらかにだけとすることです。
内側のループの結果でその行または列のカウンタが n であることが判った時点で外側のループを抜けてしまえば、カウンタはそのままなのでその外の判定に再び引っ掛かってその break で while を抜けるはずです。
> 15時間ほどこのコードと睨めっこしていた
なんか本当に「睨めっこ」(とロシアンルーレット的にコードを変更してみるとか)しかしてないんじゃないかと想像してしまったり。
コードを見て何しているかわかる人は睨めっこでもいいんだけれど、よほどのベテランか天才にしか無理と割り切って、睨めっこはある程度で諦めて視点を変えてみないと...
・先に日本語で書いてそれを翻訳、というのを紹介したけれど、その逆をやってみる(一つ一つの動作を日本語に(頭の中でなく)書き下して、それを通しで読んでみる)とか、
・コンピュータになったつもりで、変数の変化を一つ一つ書き出してみるとか
あと、夜は寝たほうがいい... 頑張った時間と思考力の衰えの損得を秤にかけるとプラスになることは多くはないです。
>ロシアンルーレット的にコードを変更してみる
本当にこの様なことをやっていました。
>日本語で書いてそれを翻訳
アドバイス通り書いて、流れを理解を深めようとしてみました。願望で書いていた箇所のコードなどが結構あることに気がつきました。
>夜は寝たほうがいい
その通りだと思います。無茶苦茶反省しています。
何故 count3 と count4 の判定の if から break を消してしまったのでしょうか。
break が無ければ、斜めで勝ちが決まった時に while を抜けない=ゲームが続いてしまいます。
>無茶苦茶反省しています
長時間労働はしないというのは実際には結果論的な所で、納期が迫っているのに手を休めて間に合わなかったらというプレッシャーの真っ最中に1人でその判断が出来るかというと実はかなり難しいです。
どちらの判断をしても結果間に合った後なら「休憩[取っていれば/取っていて]良かったなぁ」と言える訳で、間に合わなかったら「[取らなかったから/取ったから]悪かったのか」という後悔になってしまうものです。
プロジェクト全体の責任を取れる立場の人間が労働基準とか倫理とか効率とかで納品先にも納得して貰って、上からの指示として『休めさせられる』ことになればプレッシャーは大分減るでしょうが…日本人の良くない傾向ですよね。
回答1件
あなたの回答
tips
プレビュー