シミュレーションゲームの移動範囲を調べるプログラムの2回目です。
4.移動範囲を調べる
ここが再帰呼び出し関数になります。
これまでと違い、移動量が関係してきますので、少し複雑になります。
移動範囲チェック用配列には、残り歩数が入るように作ります。
イメージとしては、↓のような感じです。
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 0 1 0 9 9 9 9 9 9 9 9 9 0 1 2 1 0 9 9 9 9 9 9 9 0 1 2 3 2 1 0 9 9 9 9 9 0 1 2 3 4 3 2 1 0 9 9 9 0 1 2 3 4 5 4 3 2 1 0 9 9 9 0 1 2 3 4 3 2 1 0 9 9 9 9 9 0 1 2 3 2 1 0 9 9 9 9 9 9 9 0 1 2 1 0 9 9 9 9 9 9 9 9 9 0 1 0 9 9 9 9 9 9 9 9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
再帰呼び出しを行う関数には「残り歩数」を渡すようにします。
自分自身を呼び出す際に、移動先の移動量を引いた歩数を渡し、起点に登録します。
※0のところまで移動可能です。
関数名 CheckMoveArea 戻り値 なし 引 数 マップ、移動範囲、X座標、Y座標、残り歩数 機 能 移動範囲の調査 |
void CheckMoveArea(const int map[ROW][COL], int moveArea[ROW][COL], const int x, const int y, const int step) { int remain; if (moveArea[y][x] != IMPASSABLE && moveArea[y][x] > step) { return; } moveArea[y][x] = step; if (y - 1 >= 0) { remain = step - map[y - 1][x] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x, y - 1, remain); } } if (x + 1 < COL) { remain = step - map[y][x + 1] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x + 1, y, remain); } } if (y + 1 < ROW) { remain = step - map[y + 1][x] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x, y + 1, remain); } } if (x - 1 >= 0) { remain = step - map[y][x - 1] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x - 1, y, remain); } } } |
まず、最初のif文を見ます。
if (moveArea[y][x] != IMPASSABLE && moveArea[y][x] > step) { return; } |
移動先が「通行不可」では無く、残り歩数よりも大きければ関数から抜けるという事ですね。
これは、すでにチェック済みの個所に対する処理を指しています。
IMPASSABLEはmoveArea配列の初期値ですから、未チェックの部分です。
IMPASSABLEでは無いということは、すでにチェック済みのマスという事です。
その中で、残り歩数よりも大きいマスは無視すると書いてあります。
細かく書くと非常に長くなるので、ざっくりとした説明で済ませます。
再帰呼び出しを続けているとチェック済みのマスを再度チェックする事があります。
普通はチェック済みのマスは再チェックしないのですが、今回は違います。
地形によって移動量が異なりますので、経路によっては行ける個所が出てくる可能性があります。
↓の状況で、移動範囲のチェックを開始します。
※実行手順は「塗りつぶし」の章を見てください。
■■■■■□□□□□□□□ ■■■■□□□□□□□□□ ■■□□□□□□□□△□□ ■□□□□□□□□□□□□ □□□□□□□□□□□□□ □□□□□□□□□□□□□ □□□□□□P□□□□□□ □□□□□□□□□□□□□ □□□□□□□□□□□□■ ××××□□□□□■■■■ □□□□□□□□□■△△■ □□□×□□□□□□△■□ □□□×□□□□□△■■□
途中までの状況を書きます。
9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 0 9 9 9 9 9 9
9 9 9 9 9 0 1 0 9 9 9 9 9
9 9 9 9 0 1 2 1 0 9 9 9 9
9 9 9 9 9 0 3 0 9 9 9 9 9
9 9 9 9 9 9 4 9 9 9 9 9 9
9 9 9 9 9 9 5 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9
この起点に戻ってきた時には、「上」のみチェック済みです。
1つ右を見ると「0」が入っていますから、チェック済みのマスですね。
しかし、まだ歩数は3歩残っています。
このように、すでにチェック済みのマスよりも残り歩数の方が大きければ歩数を上書きして続けます。
もう1つ、再帰呼び出しの条件を見てみましょう。
if (y - 1 >= 0) { remain = step - map[y - 1][x] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x, y - 1, remain); } } |
残り歩数を計算する式は、
remain = step - map[y - 1][x] - 1; |
となっています。
マップには↓のデータが入っています。
草原 0 砂地 1 岩場 2 通行不可 9
しかし、移動量という考え方では、
草原 1 砂地 2 岩場 3 通行不可 9
マップ用データとは1ずれています。
マップ用データをそのまま扱った場合、
remain = step - map[y - 1][x]; |
としたのでは、草原(0)では歩数に変化が無いことになってしまいます。
※もちろんマップデータを移動量に合わせれば−1は不要になります。
※その時には表示のプログラムを変更することで対応出来ます。
<sample program 155-01>
int main(void)
{
int map[ROW][COL] = {
{ 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 9, 9, 9, 9, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1 },
{ 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 2, 1, 0 },
{ 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 1, 1, 0 },
};
int moveArea[ROW][COL];
ShowMap(map, POSITION_X, POSITION_Y);
ClearMoveArea(moveArea);
CheckMoveArea(map, moveArea, POSITION_X, POSITION_Y, STEP_MAX);
ShowMoveArea(moveArea);
return 0;
}
|
<実行結果>
■■■■■□□□□□□□□ ■■■■□□□□□□□□□ ■■□□□□□□□□△□□ ■□□□□□□□□□□□□ □□□□□□□□□□□□□ □□□□□□□□□□□□□ □□□□□□P□□□□□□ □□□□□□□□□□□□□ □□□□□□□□□□□□■ ××××□□□□□■■■■ □□□□□□□□□■△△■ □□□×□□□□□□△■□ □□□×□□□□□△■■□ 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 0 1 0 9 9 9 9 9 9 9 9 9 0 1 2 1 0 9 9 9 9 9 9 9 0 1 2 3 2 1 0 9 9 9 9 9 0 1 2 3 4 3 2 1 0 9 9 9 0 1 2 3 4 5 4 3 2 1 0 9 9 9 0 1 2 3 4 3 2 1 0 9 9 9 9 9 0 1 2 3 2 1 0 9 9 9 9 9 9 9 0 1 2 1 0 9 9 9 9 9 9 9 9 9 0 1 0 9 9 9 9 9 9 9 9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 続行するには何かキーを押してください・・・
出来ました!
ゲームであれば、この情報を元にマップに移動範囲を重ねて表示するでしょう。
コンソールでは難しいので、「おまけ」程度に移動範囲を表示してみます。
5.結果の表示
関数名 ShowResult 戻り値 なし 引 数 マップ、移動範囲、X座標、Y座標 機 能 移動範囲の調査 |
void ShowResult(const int map[ROW][COL], const int moveArea[ROW][COL], const int x, const int y) { const char PLAYER[3] = "P"; const char MOVE_AREA[3] = "・"; const char GRAPHIC[MAP_KIND][3] = { "□", "■", "△", "×" }; int i; int j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (moveArea[i][j] != IMPASSABLE) { if (i == y &&j == x) { printf(PLAYER); } else { printf(MOVE_AREA); } } else { if (map[i][j] < IMPASSABLE) { printf("%s", GRAPHIC[map[i][j]]); } else { printf("%s", GRAPHIC[MAP_KIND - 1]); } } } printf("\n"); } printf("\n"); } |
これを合わせて、プログラム全体を書きます。
<sample program 155-02>
#include <stdio.h> #define ROW 13 #define COL 13 #define MAP_KIND 4 #define POSITION_X 6 #define POSITION_Y 6 #define STEP_MAX 5 enum { GRASS, SAND, ROCK, IMPASSABLE = 9, }; void ShowMap(const int map[ROW][COL], const int x, const int y); void ClearMoveArea(int moveArea[ROW][COL]); void ShowMoveArea(const int moveArea[ROW][COL]); void CheckMoveArea(const int map[ROW][COL], int moveArea[ROW][COL], const int x, const int y, const int step); void ShowResult(const int map[ROW][COL], const int moveArea[ROW][COL], const int x, const int y); int main(void) { int map[ROW][COL] = { { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, { 9, 9, 9, 9, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1 }, { 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 2, 1, 0 }, { 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 1, 1, 0 }, }; int moveArea[ROW][COL]; ShowMap(map, POSITION_X, POSITION_Y); ClearMoveArea(moveArea); CheckMoveArea(map, moveArea, POSITION_X, POSITION_Y, STEP_MAX); ShowMoveArea(moveArea); ShowResult(map, moveArea, POSITION_X, POSITION_Y); return 0; } void ShowMap(const int map[ROW][COL], const int x, const int y) { const char PLAYER[3] = "P"; const char GRAPHIC[MAP_KIND][3] = { "□", "■", "△", "×" }; int i; int j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (i == y && j == x) { printf(PLAYER); } else { if (map[i][j] < IMPASSABLE) { printf("%s", GRAPHIC[map[i][j]]); } else { printf("%s", GRAPHIC[MAP_KIND - 1]); } } } printf("\n"); } printf("\n"); } void ClearMoveArea(int moveArea[ROW][COL]) { int i; int j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { moveArea[i][j] = IMPASSABLE; } } } void ShowMoveArea(const int moveArea[ROW][COL]) { int i; int j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { printf("%2d", moveArea[i][j]); } printf("\n"); } printf("\n"); } void CheckMoveArea(const int map[ROW][COL], int moveArea[ROW][COL], const int x, const int y, const int step) { int remain; if (moveArea[y][x] != IMPASSABLE && moveArea[y][x] > step) { return; } moveArea[y][x] = step; if (y - 1 >= 0) { remain = step - map[y - 1][x] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x, y - 1, remain); } } if (x + 1 < COL) { remain = step - map[y][x + 1] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x + 1, y, remain); } } if (y + 1 < ROW) { remain = step - map[y + 1][x] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x, y + 1, remain); } } if (x - 1 >= 0) { remain = step - map[y][x - 1] - 1; if (remain >= 0) { CheckMoveArea(map, moveArea, x - 1, y, remain); } } } void ShowResult(const int map[ROW][COL], const int moveArea[ROW][COL], const int x, const int y) { const char PLAYER[3] = "P"; const char MOVE_AREA[3] = "・"; const char GRAPHIC[MAP_KIND][3] = { "□", "■", "△", "×" }; int i; int j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (moveArea[i][j] != IMPASSABLE) { if (i == y &&j == x) { printf(PLAYER); } else { printf(MOVE_AREA); } } else { if (map[i][j] < IMPASSABLE) { printf("%s", GRAPHIC[map[i][j]]); } else { printf("%s", GRAPHIC[MAP_KIND - 1]); } } } printf("\n"); } printf("\n"); } |
<実行結果>
■■■■■□□□□□□□□ ■■■■□□□□□□□□□ ■■□□□□□□□□△□□ ■□□□□□□□□□□□□ □□□□□□□□□□□□□ □□□□□□□□□□□□□ □□□□□□P□□□□□□ □□□□□□□□□□□□□ □□□□□□□□□□□□■ ××××□□□□□■■■■ □□□□□□□□□■△△■ □□□×□□□□□□△■□ □□□×□□□□□△■■□ 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 0 1 0 9 9 9 9 9 9 9 9 9 0 1 2 1 0 9 9 9 9 9 9 9 0 1 2 3 2 1 0 9 9 9 9 9 0 1 2 3 4 3 2 1 0 9 9 9 0 1 2 3 4 5 4 3 2 1 0 9 9 9 0 1 2 3 4 3 2 1 0 9 9 9 9 9 0 1 2 3 2 1 0 9 9 9 9 9 9 9 0 1 2 1 0 9 9 9 9 9 9 9 9 9 0 1 0 9 9 9 9 9 9 9 9 9 9 9 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 ■■■■■□□□□□□□□ ■■■■□□・□□□□□□ ■■□□□・・・□□△□□ ■□□□・・・・・□□□□ □□□・・・・・・・□□□ □□・・・・・・・・・□□ □・・・・・P・・・・・□ □□・・・・・・・・・□□ □□□・・・・・・・□□■ ××××・・・・・■■■■ □□□□□・・・□■△△■ □□□×□□・□□□△■□ □□□×□□□□□△■■□ 続行するには何かキーを押してください・・・
プレイヤーユニットの初期座標を色々変えながら試してください。
次が再帰呼び出しラストの題材です。