それでは二次元配列を使ったミニゲームを作りましょう。
今回作るのは宝探しゲームです。
二次元配列で作ったフィールドの中に一か所だけ「当たり」を作ります。
その場所をプレイヤーが当てるゲームです。
フィールドのイメージは、
field 0 1 2 3 4 5 6 7 8 9 +−+−+−+−+−+−+−+−+−+−+ 0| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 1| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 2| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 3| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 4| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 5| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 6| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 7| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 8| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+ 9| | | | | | | | | | | +−+−+−+−+−+−+−+−+−+−+
このようなイメージです。
まずは #define で ROW と COL を定義し、int型の二次元配列 field を作りましょう。
確認のため、作った後で解答例を見てください。
解答例です。
<sample program 093-01>
#include <stdio.h> #define ROW 10 #define COL 10 int main(void) { int field[ROW][COL]; return 0; } |
次に、この field の要素全てに0を入れます。
0は「はずれ」を意味しますので、#defineで「NG」という名前を定義しておきましょう。
二重ループを準備し、fieldの要素を「NG」で埋めてください。
解答例です。
<sample program 093-02>
#include <stdio.h> #define ROW 10 #define COL 10 #define NG 0 int main(void) { int field[ROW][COL]; int i; int j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { field[i][j] = NG; } } return 0; } |
この辺までは難なく組んで欲しいですね。
次は、フィールドを表示しましょう。
これまでにやったことを踏まえて、このような形で表示してみてください。
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 続行するには何かキーを押してください・・・
解答例です。
<sample program 093-03>
#include <stdio.h>
#define ROW 10
#define COL 10
#define NG 0
int main(void)
{
int field[ROW][COL];
int i;
int j;
for (i = 0; i < ROW; i++) {
for (j = 0; j < COL; j++) {
field[i][j] = NG;
}
}
for (i = 0; i < ROW; i++) {
for (j = 0; j < COL; j++) {
printf("%d ", field[i][j]);
}
printf("\n");
}
return 0;
}
|
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 続行するには何かキーを押してください・・・
次は「宝」をセットしましょう。
もちろん乱数を使って設定します。
「宝」はデータとして1と定義しますので、#defineで「OK」と定義しましょう。
この「OK」をフィールドのどこかに設置します。
座標を表す変数としてint型で「x」「y」を追加します。
乱数で縦(Y座標)と横(X座標)を求め、field[Y座標][X座標]の位置に「OK」を入れます。
では、表示を行うループの前に一連のプログラムを追加しましょう。
乱数に必要なヘッダファイルのインクルードや種のセットも忘れずに!
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 続行するには何かキーを押してください・・・
解答例です。
<sample program 093-04>
#include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 10 #define COL 10 #define NG 0 #define OK 1 int main(void) { int field[ROW][COL]; int i; int j; int x; int y; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { field[i][j] = NG; } } srand( (unsigned int)time( NULL) ); y = rand() % ROW; x = rand() % COL; field[y][x] = OK; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { printf("%d ", field[i][j]); } printf("\n"); } return 0; } |
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 続行するには何かキーを押してください・・・
乱数はX座標、Y座標ともに0から9までの範囲が必要ですから ROW と COL を使って作ります。
実行するたびに「宝」の位置が変わっていることを確認してください。
プレイヤーはこの「宝」の場所を、座標を入力することで探します。
※今のところマウスは使えませんからね。
座標入力用にinputX、inputYという変数を追加します。
キーボードから座標を入力し、field[inputY][inputX]が「OK」であれば「当たり」です。
このプログラムを表示を行うループの後ろに作ってください。
今のところ、何度も入力出来るようループさせる必要はありません。
1回だけ入力出来れば良いです。
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 6 Y座標を入力してください(0〜9まで) 4 当たり! 続行するには何かキーを押してください・・・
解答例です。
<sample program 093-05>
#include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 10 #define COL 10 #define NG 0 #define OK 1 int main(void) { int field[ROW][COL]; int i; int j; int x; int y; int inputX; int inputY; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { field[i][j] = NG; } } srand( (unsigned int)time( NULL) ); y = rand() % ROW; x = rand() % COL; field[y][x] = OK; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { printf("%d ", field[i][j]); } printf("\n"); } printf("X座標を入力してください(0〜9まで)\n"); scanf("%d", &inputX); printf("Y座標を入力してください(0〜9まで)\n"); scanf("%d", &inputY); if (field[inputY][inputX] == OK) { printf("当たり!\n"); } return 0; } |
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 6 Y座標を入力してください(0〜9まで) 4 当たり! 続行するには何かキーを押してください・・・
フィールドが見えていますので、簡単に当てることが出来ますから、当たった時とはずれた時の両方確認しておきましょう。
次は「宝」を隠しましょう。
フィールドをそのまま正直に表示すると「宝」のありかが分かってしまいます。
そこで表示を工夫し、「宝」が設置されている場合でも「NG」の0が表示されるように改良します。
表示を行う二重ループの中身を↓のように変えましょう。
<sample program 093-06>
for (i = 0; i < ROW; i++) {
for (j = 0; j < COL; j++) {
if (field[i][j] == OK) {
printf("0 ");
}
else{
printf("%d ", field[i][j]);
}
}
printf("\n");
}
|
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 5 Y座標を入力してください(0〜9まで) 4 続行するには何かキーを押してください・・・
これでデータ上は「宝」でも表示は0にすることが出来ました。
結局全て0が表示されていますから、最初から全部0を表示するようにすれば良かったのでは・・・
と思われるかも知れませんが、先のことを考えて作っていますので大丈夫です。
今のままでは1回しか探索チャンスがありません。
繰り返しを使って何度もチャンスがあるように作り変えましょう。
とりあえず無限ループを組み込みますが、各自で適当な場所を考えて組み込んでください。
当たった場合はループを抜けて終了させることも忘れずに。
解答例です。
<sample program 093-07>
#include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 10 #define COL 10 #define NG 0 #define OK 1 int main(void) { int field[ROW][COL]; int i; int j; int x; int y; int inputX; int inputY; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { field[i][j] = NG; } } srand( (unsigned int)time( NULL) ); y = rand() % ROW; x = rand() % COL; field[y][x] = OK; for (;;) { for (i = 0; i < ROW; i++) { for ( j = 0; j < COL; j++) { if (field[i][j] == OK) { printf("0 "); } else{ printf("%d ", field[i][j]); } } printf("\n"); } printf("X座標を入力してください(0〜9まで)\n"); scanf("%d", &inputX); printf("Y座標を入力してください(0〜9まで)\n"); scanf("%d", &inputY); if (field[inputY][inputX] == OK) { printf("当たり!\n"); break; } } return 0; } |
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 5 Y座標を入力してください(0〜9まで) 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 2 Y座標を入力してください(0〜9まで) 3 当たり! 続行するには何かキーを押してください・・・
表示から当たりの判定までを無限ループで括りました。
[0][0]から[9][9]まで試せばいつかは必ず当たります。
いつかは当たるとは言え、すでに調べた場所かどうか分からなくなることがあります。
次はすでに調べた場所に目印を付けることにしましょう。
方法は次の通りです。
field[inputY][inputX]を調べ、はずれの場合field[inputY][inputX]の中身を変える
はずれの場合は「NG」が入っているはずですので、#defineを使って「CHECKED」を2で定義し、調べた所の要素を「CHECKED」にしてしまうのです。
<sample program 093-08>
#include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 10 #define COL 10 #define NG 0 #define OK 1 #define CHECKED 2 int main(void) { int field[ROW][COL]; int i; int j; int x; int y; int inputX; int inputY; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { field[i][j] = NG; } } srand( (unsigned int)time( NULL) ); y = rand() % ROW; x = rand() % COL; field[y][x] = OK; for (;;) { for (i = 0; i < ROW; i++) { for ( j = 0; j < COL; j++) { if (field[i][j] == OK) { printf("0 "); } else{ printf("%d ", field[i][j]); } } printf("\n"); } printf("X座標を入力してください(0〜9まで)\n"); scanf("%d", &inputX); printf("Y座標を入力してください(0〜9まで)\n"); scanf("%d", &inputY); if (field[inputY][inputX] == OK) { printf("当たり!\n"); break; } else { field[inputY][inputX] = CHECKED; } } return 0; } |
<実行結果>
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 5 Y座標を入力してください(0〜9まで) 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 2 Y座標を入力してください(0〜9まで) 3 当たり! 続行するには何かキーを押してください・・・
すでに調べた場所に2が入るようになりました。
少しは見やすくなりましたが、まだまだ親切な表示とは言えませんね。
X座標が4、Y座標が6というのがどこにあたるのか、ぱっと見ても分かりません。
表示をもっと工夫して添え字が分かるようにしましょう。
まず、簡単な横方向の添え字を付けます。
表示の箇所を↓のように変えてください。
<sample program 093-09>
printf("0 1 2 3 4 5 6 7 8 9 \n");
for (i = 0; i < ROW; i++) {
for ( j = 0; j < COL; j++) {
if (field[i][j] == OK) {
printf("0 ");
}
else{
printf("%d ", field[i][j]);
}
}
printf("\n");
}
|
<実行結果>
0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 5 Y座標を入力してください(0〜9まで) 4 当たり! 続行するには何かキーを押してください・・・
フィールドの上に横方向の添え字が付きました。
縦方向はこんなに簡単ではありません。
画面への文字表示の都合上、文字の表示は左から右へ1行表示し、改行で次の行へ移行します。
縦方向の添え字は、↓のように1行が表示されるより前に行頭に表示する必要があります。
0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0
1行分のデータを表示するのは二重ループの内側のループですので、内側のループが始まる前に添え字を表示します。
さらに、0から9まで数値を変えながら表示する必要があります。
1行ごとに変化する変数はiです。
以上のことを踏まえ、↓のように変更してみましょう。
※横方向の添え字の箇所も、位置調整のため空白を追加します。
<sample program 093-10>
printf(" 0 1 2 3 4 5 6 7 8 9 \n"); for (i = 0; i < ROW; i++) { printf("%d ", i); for ( j = 0; j < COL; j++) { if (field[i][j] == OK) { printf("0 "); } else{ printf("%d ", field[i][j]); } } printf("\n"); } |
<実行結果>
0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0 7 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0 0 0 X座標を入力してください(0〜9まで) 5 Y座標を入力してください(0〜9まで) 4 当たり! 続行するには何かキーを押してください・・・
さらに見やすくなりました。
今のところ、調べていない箇所は0、調べた箇所は2と数字で表示しています。
これでも分かるのですが、人間は数字よりも記号の方がもっと分かりやすいです。
さらに表示の箇所を改良しましょう。
調べていない箇所は■、しらべた箇所は□という記号に置き換えてみましょう。
当然「宝」は■に置き換えます。
※横方向の添え字の箇所も、位置調整のため空白を追加します。
<sample program 093-11>
printf(" 0 1 2 3 4 5 6 7 8 9 \n"); for (i = 0; i < ROW; i++) { printf("%d ", i); for ( j = 0; j < COL; j++) { if (field[i][j] == OK) { printf("■"); } else{ if (field[i][j] == NG) { printf("■"); } else { printf("□"); } } } printf("\n"); } |
<実行結果>
0 1 2 3 4 5 6 7 8 9 0 ■■■■■■■■■■ 1 ■■■■■■■■■■ 2 ■■■■■■■■■■ 3 ■■■■■■■■■■ 4 ■■■■■■■■■■ 5 ■■■■■■■■■■ 6 ■■■■■■■■■■ 7 ■■■■■■■■■■ 8 ■■■■■■■■■■ 9 ■■■■■■■■■■ X座標を入力してください(0〜9まで) 5 Y座標を入力してください(0〜9まで) 4 0 1 2 3 4 5 6 7 8 9 0 ■■■■■■■■■■ 1 ■■■■■■■■■■ 2 ■■■■■■■■■■ 3 ■■■■■■■■■■ 4 ■■■■■□■■■■ 5 ■■■■■■■■■■ 6 ■■■■■■■■■■ 7 ■■■■■■■■■■ 8 ■■■■■■■■■■ 9 ■■■■■■■■■■ X座標を入力してください(0〜9まで) 3 Y座標を入力してください(0〜9まで) 2 当たり! 続行するには何かキーを押してください・・・
ずいぶん雰囲気が変わりました。
まだまだ改良の余地はありますが、皆さんの宿題に取っておきましょう。
例えば、以下のような改良に挑戦してみてください。
・入力の際にマイナスの値やROWやCOLを超える値が入力されることを防ぐ(やりましたよね) ・ヒントが出る「列は合ってるよ!」とか「周囲3マス以内にあるよ!」など
前から何度も書いていますが、ここに書いてあることを丸写しして実行出来たとしても実力はつきません。
「なぜそうなるのか」をしっかり考えること、ここに書いてあるプログラムを元に自分で新しいプログラムを作ることをやって徐々に実力がついてくると思います。
ぜひ色々なプログラムにチャレンジしてみてください。
とは言え、パッと見て簡単そうな題材でもこれまでの知識では組めないケースもあります。
初心者の方は「テトリス」とか「マインスイーパー」など「比較的簡単に出来そうだ!」と考えがちですが結構難しいですよ。
まったく超えられない壁は挫折の元ですから、とりあえずは今までのプログラムの理解と改良をメインで考えてみてください。
※挑戦を妨げるつもりはありませんから、壊れない程度にぶつかってみてください。