C#で書いてみます。
>dotnet run ######## # .. p # # oo # # # ######## a:left s:right w:up z:down. command?
Program.cs
using System; class Program { //#壁 _空間 .ゴール oブロック p人 const string gStageData = "########\n" + "# .. p #\n" + "# oo #\n" + "# #\n" + "########\0"; const int gStageWidth = 8; const int gStageHeight = 5; enum OBJ { OBJ_SPACE, OBJ_WALL, OBJ_GOAL, OBJ_BLOCK, OBJ_BLOCK_ON_GOAL, OBJ_MAN, OBJ_MAN_ON_GOAL, OBJ_UNKNOWN, }; static void Main(string[] args) { string input; OBJ[] state = new OBJ[gStageWidth * gStageHeight]; /* 状態配列確保 */ initialize(state, gStageWidth, gStageData); /* ステージ初期化 */ /* メインループ */ while (true) { /* まず描画 */ draw(state, gStageWidth, gStageHeight); /* クリアチェック */ if (checkClear(state, gStageWidth, gStageHeight)) { break; /* クリアチェック */ } /* 入力取得 */ Console.Write("a:left s:right w:up z:down. command?"); /* 操作説明 */ input=Console.ReadLine(); Console.Write("\n"); /* 更新 */ update(state, input[0], gStageWidth, gStageHeight); } /* 祝いのメッセージ */ Console.WriteLine("Congratulation's! you won."); return; } /* ---------------------以下関数定義------------------------------------------ */ static void initialize(OBJ[] state, int width, string stageData) { int d = 0; /* 読み込みポインタ */ int x = 0; int y = 0; while (stageData[d] != '\0') { /* NULL文字でない間 */ OBJ t; /* 特に意味はないが使う回数が多い変数に私は良くtを使う。temporaryの略。たぶんよくない習慣だが、無駄に長い名前にして読みにくいのも困り物だろう。 */ switch (stageData[d]) { case '#': t = OBJ.OBJ_WALL; break; case ' ': t = OBJ.OBJ_SPACE; break; case 'o': t = OBJ.OBJ_BLOCK; break; case 'O': t = OBJ.OBJ_BLOCK_ON_GOAL; break; case '.': t = OBJ.OBJ_GOAL; break; case 'p': t = OBJ.OBJ_MAN; break; case 'P': t = OBJ.OBJ_MAN_ON_GOAL; break; case '\n': x = 0; ++y; t = OBJ.OBJ_UNKNOWN; break; /* 改行処理 */ default: t = OBJ.OBJ_UNKNOWN; break; } ++d; if (t != OBJ.OBJ_UNKNOWN) { /* 知らない文字なら無視するのでこのif文がある */ state[y * width + x] = t; /* 書き込み */ ++x; } } } static void draw(OBJ[] state, int width, int height) { int x, y, t; string font = " #.oOpP"; /* Object列挙の順 */ for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { OBJ o = state[y * width + x]; switch (o) { case OBJ.OBJ_SPACE: t = 0; break; case OBJ.OBJ_WALL: t = 1; break; case OBJ.OBJ_GOAL: t = 2; break; case OBJ.OBJ_BLOCK: t = 3; break; case OBJ.OBJ_BLOCK_ON_GOAL: t = 4; break; case OBJ.OBJ_MAN_ON_GOAL: t = 5; break; case OBJ.OBJ_MAN: t = 6; break; default: t = 0; break; } Console.Write(font[t]); } Console.Write("\n"); } } /* 第一引数はほかの関数ではstateとしているが、あまりに頻繁に使うので 短いsで済ませている。w,hもそれぞれwidth,heightである。 */ static void update(OBJ[] s, char input, int w, int h) { /* 移動差分に変換(dはdifferenceでもdeltaでもお好きな方の略だと思って欲しい) */ int dx = 0; int dy = 0; int i = -1; int x, y, tx, ty, p, tp, tx2, ty2, tp2; switch (input) { case 'a': dx = -1; break; case 's': dx = 1; break; case 'w': dy = -1; break; case 'z': dy = 1; break; } /* 人座標を検索 */ for (i = 0; i < w * h; ++i) { if (s[i] == OBJ.OBJ_MAN || s[i] == OBJ.OBJ_MAN_ON_GOAL) { break; } } x = i % w; /* xは幅で割ったあまり */ y = i / w; /* yは幅で割った商 */ /* 移動後座標(tに意味はない。ごめんなさい) */ tx = x + dx; ty = y + dy; /* 座標の最大最小チェック。外れていれば不許可 */ if (tx < 0 || ty < 0 || tx >= w || ty >= h) { return; } /* A.その方向が空白またはゴール。人が移動。 */ p = y * w + x; /* 人位置 */ tp = ty * w + tx; /* ターゲット位置(TargetPosition) */ if (s[tp] == OBJ.OBJ_SPACE || s[tp] == OBJ.OBJ_GOAL) { s[tp] = (s[tp] == OBJ.OBJ_GOAL) ? OBJ.OBJ_MAN_ON_GOAL : OBJ.OBJ_MAN; /* ゴールならゴール上の人に */ s[p] = (s[p] == OBJ.OBJ_MAN_ON_GOAL) ? OBJ.OBJ_GOAL : OBJ.OBJ_SPACE; /* もともとゴール上ならゴールに */ /* B.その方向が箱。その方向の次のマスが空白またはゴールであれば移動。 */ } else if (s[tp] == OBJ.OBJ_BLOCK || s[tp] == OBJ.OBJ_BLOCK_ON_GOAL) { /* 2マス先が範囲内かチェック */ tx2 = tx + dx; ty2 = ty + dy; if (tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h) { /* 押せない */ return; } tp2 = (ty + dy) * w + (tx + dx); /* 2マス先 */ if (s[tp2] == OBJ.OBJ_SPACE || s[tp2] == OBJ.OBJ_GOAL) { /* 順次入れ替え */ s[tp2] = (s[tp2] == OBJ.OBJ_GOAL) ? OBJ.OBJ_BLOCK_ON_GOAL : OBJ.OBJ_BLOCK; s[tp] = (s[tp] == OBJ.OBJ_BLOCK_ON_GOAL) ? OBJ.OBJ_MAN_ON_GOAL : OBJ.OBJ_MAN; s[p] = (s[p] == OBJ.OBJ_MAN_ON_GOAL) ? OBJ.OBJ_GOAL : OBJ.OBJ_SPACE; } } } /* ブロックのみがなければクリアしている。 */ static bool checkClear(OBJ[] s, int width, int height) { int i; for (i = 0; i < width * height; ++i) { if (s[i] == OBJ.OBJ_BLOCK) { return false; } } return true; } }