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;
}
}