トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
21-04 Cannibal Monsters
問題
SmartGamesのCannibal Monstersを解きます。
各モンスターは上下左右にモンスターを食べる移動(チェスのルークの駒を取る移動)のみ可です。
最後に、モンスターが1匹だけになればクリアです。
初期配置のモンスターごとに食べれるモンスターは下記のようになります。
●赤は青を食べれる
●青は緑を食べれる
●緑は赤を食べれる
モンスターAがモンスターBを食べると、
次に食べれるモンスターは、モンスターBが食べれることのできたモンスターとなります。
Q01 (Starterの01問目)
□□□□
□赤青□
□□□□
□□□□
Q02 (Juniorの01問目)
□□□□
□青赤赤
□青□□
□緑□□
Q03 (Expertの01問目)
緑赤赤青
緑□□緑
赤□□青
□□赤青
Q04 (Masterの01問目)
青□赤赤
□赤赤緑
青緑□□
緑青□□
Q05 (Masterの10問目)
□赤緑青
□緑赤青
青□赤青
緑赤□□
Q06 (Masterの11問目)
□緑赤□
赤青□緑
□□青緑
青赤赤□
Q07 (Masterの12問目)
青赤緑青
青□赤□
緑赤緑□
青□□緑
ソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static int mQuestionNo = 2;
static string[,] mQuestionArr; //問題の初期盤面
const int UB = 3;
//問題を定義
static void QuestionDef()
{
string[] wkStrArr = null;
if (mQuestionNo == 1) {
wkStrArr = new string[] {"□□□□",
"□赤青□",
"□□□□",
"□□□□"};
}
if (mQuestionNo == 2) {
wkStrArr = new string[] {"□□□□",
"□青赤赤",
"□青□□",
"□緑□□"};
}
if (mQuestionNo == 3) {
wkStrArr = new string[] {"緑赤赤青",
"緑□□緑",
"赤□□青",
"□□赤青"};
}
if (mQuestionNo == 4) {
wkStrArr = new string[] {"青□赤赤",
"□赤赤緑",
"青緑□□",
"緑青□□"};
}
if (mQuestionNo == 5) {
wkStrArr = new string[] {"□赤緑青",
"□緑赤青",
"青□赤青",
"緑赤□□"};
}
if (mQuestionNo == 6) {
wkStrArr = new string[] {"□緑赤□",
"赤青□緑",
"□□青緑",
"青赤赤□"};
}
if (mQuestionNo == 7) {
wkStrArr = new string[] {"青赤緑青",
"青□赤□",
"緑赤緑□",
"青□□緑"};
}
mQuestionArr = new string[UB + 1, UB + 1];
for (int Y = 0; Y <= UB; Y++) {
for (int X = 0; X <= UB; X++) {
char CurrChar = wkStrArr[X][Y];
if (CurrChar == '赤') mQuestionArr[Y, X] = "RB";
if (CurrChar == '青') mQuestionArr[Y, X] = "BG";
if (CurrChar == '緑') mQuestionArr[Y, X] = "GR";
if (CurrChar == '□') mQuestionArr[Y, X] = "**";
}
}
}
struct JyoutaiDef
{
internal int Level;
internal string[,] BanArr;
internal List<string[,]> Log;
}
static void Main()
{
//問題を定義
QuestionDef();
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.Level = 0;
WillPush.BanArr = mQuestionArr;
WillPush.Log = new List<string[,]>() { WillPush.BanArr };
Stk.Push(WillPush);
var VisitedSet = new HashSet<ulong>();
VisitedSet.Add(GetHash(WillPush.BanArr));
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
//クリア判定
if (IsClear(Popped.BanArr)) {
Console.WriteLine("解を発見");
for (int I = 0; I <= Popped.Log.Count - 1; I++) {
Console.WriteLine("レベル{0}", I);
PrintBan(Popped.Log[I]);
}
break;
}
for (int LoopX = 0; LoopX <= UB; LoopX++) {
for (int LoopY = 0; LoopY <= UB; LoopY++) {
if (Popped.BanArr[LoopX, LoopY] == "**") continue;
List<string[,]> MovedBanArrList =
DeriveMovedBanArrList(Popped.BanArr, LoopX, LoopY);
foreach (string[,] EachBanArr in MovedBanArrList) {
WillPush.Level = Popped.Level + 1;
WillPush.BanArr = EachBanArr;
ulong CurrHash = GetHash(WillPush.BanArr);
if (VisitedSet.Add(CurrHash) == false)
continue;
WillPush.Log = new List<string[,]>(Popped.Log) { WillPush.BanArr };
Stk.Push(WillPush);
}
}
}
}
}
//クリア判定
static bool IsClear(string[,] pBanArr)
{
int MonsterCnt = 0;
for (int Y = 0; Y <= UB; Y++)
for (int X = 0; X <= UB; X++)
if (pBanArr[X, Y] != "**")
MonsterCnt++;
return MonsterCnt == 1;
}
//移動後の盤情報の配列を返す
static List<string[,]> DeriveMovedBanArrList(string[,] pBanArr, int pFromX, int pFromY)
{
var WillReturn = new List<string[,]>();
Func<int, int, bool> MovePiece = (pToX, pToY) =>
{
if (pToX < 0 || UB < pToX) return false;
if (pToY < 0 || UB < pToY) return false;
if (pBanArr[pToX, pToY] == "**") return false;
//食べれる色でない場合
if (pBanArr[pFromX, pFromY][1] != pBanArr[pToX, pToY][0])
return true;
//食べれる色の場合
char[] NewMonster = new char[2];
NewMonster[0] = pBanArr[pFromX, pFromY][0];
NewMonster[1] = pBanArr[pToX, pToY][1];
string[,] wkArr = (string[,])pBanArr.Clone();
wkArr[pToX, pToY] = new string(NewMonster);
wkArr[pFromX, pFromY] = "**";
WillReturn.Add(wkArr);
return true;
};
Action<int, int> HeniLoopAct = (pHeniX, pHeniY) =>
{
int CurrX = pFromX + pHeniX;
int CurrY = pFromY + pHeniY;
while (true) {
if (CurrX < 0 || UB < CurrX) break;
if (CurrY < 0 || UB < CurrY) break;
if (MovePiece(CurrX, CurrY)) break;
CurrX += pHeniX;
CurrY += pHeniY;
}
};
HeniLoopAct(0, -1);
HeniLoopAct(0, 1);
HeniLoopAct(-1, 0);
HeniLoopAct(1, 0);
return WillReturn;
}
//盤面をハッシュ値に変換
static ulong GetHash(string[,] pBanArr)
{
ulong WillReturn = 0UL;
for (int X = 0; X <= UB; X++) {
for (int Y = 0; Y <= UB; Y++) {
if (pBanArr[X, Y] == "**") {
WillReturn *= 16;
continue;
}
foreach (char EachChar in pBanArr[X, Y]) {
WillReturn *= 4;
if (EachChar == 'R') WillReturn += 1;
if (EachChar == 'G') WillReturn += 2;
if (EachChar == 'B') WillReturn += 3;
}
}
}
//4の32乗=2の64乗なのでオーバーフローしない
return WillReturn;
}
//盤面を出力
static void PrintBan(string[,] pBanArr)
{
var sb = new System.Text.StringBuilder();
for (int Y = 0; Y <= UB; Y++) {
for (int X = 0; X <= UB; X++) {
sb.Append(pBanArr[X, Y]);
if (X < UB) sb.Append(",");
}
sb.AppendLine();
}
Console.WriteLine(sb.ToString());
}
}
実行結果
解を発見
レベル0
**,**,**,**
**,BG,RB,RB
**,BG,**,**
**,GR,**,**
レベル1
**,**,**,**
**,RG,**,RB
**,BG,**,**
**,GR,**,**
レベル2
**,**,**,**
**,RG,**,RB
**,**,**,**
**,BR,**,**
レベル3
**,**,**,**
**,BG,**,RB
**,**,**,**
**,**,**,**
レベル4
**,**,**,**
**,RG,**,**
**,**,**,**
**,**,**,**
解説
再訪防止の深さ優先探索で解いてます。
盤面表示は
本体の色,食べれる色
という表示としてます。