using System;
using System.Collections.Generic;
class Program
{
const int UB = 3 - 1;
struct JyoutaiDef
{
internal int Level;
internal char[,] BanArr;
internal List<char[,]> BanArrLogList;
}
static void Main()
{
char[,] wkArr = {{'銅','飛','銅'},
{'銅',' ' ,'銅'},
{'歩','銀','歩'}};
var Stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.Level = 0;
WillPush.BanArr = new char[UB + 1, UB + 1];
for (int X = 0; X <= UB; X++) {
for (int Y = 0; Y <= UB; Y++) {
WillPush.BanArr[X, Y] = wkArr[Y, X];
}
}
WillPush.BanArrLogList = new List<char[,]>() { WillPush.BanArr };
Stk.Push(WillPush);
int AnswerMinLevel = int.MaxValue;
//盤面に対する最少手数のDict
var SortedMinTesuuDict = new SortedDictionary<uint, int>();
while (Stk.Count > 0) {
JyoutaiDef Popped = Stk.Pop();
if (Popped.Level > AnswerMinLevel) continue;
if (IsGoal(Popped.BanArr)) {
AnswerMinLevel = Popped.Level;
Console.WriteLine("{0}手の解候補を発見", Popped.Level);
for (int I = 0; I <= Popped.BanArrLogList.Count - 1; I++) {
Console.WriteLine("{0}手目", I);
PrintBan(Popped.BanArrLogList[I]);
}
continue;
}
//空白マスを探す
int SpaceX = -1, SpaceY = -1;
Action SearchSpaceMasu = () =>
{
for (int X = 0; X <= UB; X++) {
for (int Y = 0; Y <= UB; Y++) {
if (Popped.BanArr[X, Y] == ' ') {
SpaceX = X; SpaceY = Y;
return;
}
}
}
};
SearchSpaceMasu();
WillPush.Level = Popped.Level + 1;
foreach (PointDef EachPoint in DeriveFromMasuList(Popped.BanArr, SpaceX, SpaceY)) {
WillPush.BanArr = (char[,])Popped.BanArr.Clone();
char MoveKoma = Popped.BanArr[EachPoint.X, EachPoint.Y];
WillPush.BanArr[SpaceX, SpaceY] = MoveKoma;
WillPush.BanArr[EachPoint.X, EachPoint.Y] = ' ';
//当該盤面に、少ない手数か等しい手数で、到達済なら枝切り
uint BanUint = BanToUint(WillPush.BanArr);
int MinTesuu;
if (SortedMinTesuuDict.TryGetValue(BanUint, out MinTesuu)) {
if (MinTesuu <= WillPush.Level) continue;
}
SortedMinTesuuDict[BanUint] = WillPush.Level;
WillPush.BanArrLogList = new List<char[,]>(Popped.BanArrLogList) { WillPush.BanArr };
Stk.Push(WillPush);
}
}
}
struct PointDef
{
internal int X;
internal int Y;
}
//空白マスを引数として、その空白に移動可能な駒の座標のリストを返す
static List<PointDef> DeriveFromMasuList(char[,] pBanArr, int pSpaceX, int pSpaceY)
{
var WillReturnList = new List<PointDef>();
//移動可能な駒と、移動元座標を引数として、
//条件を満たしてたらAdd
Action<char, int, int> CheckAndAdd = (pKoma, pFromX, pFromY) =>
{
if (pFromX < 0 || UB < pFromX) return;
if (pFromY < 0 || UB < pFromY) return;
if (pBanArr[pFromX, pFromY] == pKoma)
WillReturnList.Add(new PointDef() { X = pFromX, Y = pFromY });
};
//空白の上の駒が移動
CheckAndAdd('銅', pSpaceX, pSpaceY - 1);
CheckAndAdd('飛', pSpaceX, pSpaceY - 1);
//空白の右上の駒が移動
CheckAndAdd('銀', pSpaceX + 1, pSpaceY - 1);
//空白の右の駒が移動
CheckAndAdd('飛', pSpaceX + 1, pSpaceY);
//空白の右下の駒が移動
CheckAndAdd('銅', pSpaceX + 1, pSpaceY + 1);
CheckAndAdd('銀', pSpaceX + 1, pSpaceY + 1);
//空白の下の駒が移動
CheckAndAdd('歩', pSpaceX, pSpaceY + 1);
CheckAndAdd('銅', pSpaceX, pSpaceY + 1);
CheckAndAdd('銀', pSpaceX, pSpaceY + 1);
CheckAndAdd('飛', pSpaceX, pSpaceY + 1);
//空白の左下の駒が移動
CheckAndAdd('銅', pSpaceX - 1, pSpaceY + 1);
CheckAndAdd('銀', pSpaceX - 1, pSpaceY + 1);
//空白の左の駒が移動
CheckAndAdd('飛', pSpaceX - 1, pSpaceY);
//空白の左上の駒が移動
CheckAndAdd('銀', pSpaceX - 1, pSpaceY - 1);
return WillReturnList;
}
//ゴールに到達したかを判定
static bool IsGoal(char[,] pBanArr)
{
char[,] GoalArr = new char[,] {{'歩','銀','歩'},
{'銅',' ' ,'銅'},
{'銅','飛','銅'}};
for (int X = 0; X <= UB; X++) {
for (int Y = 0; Y <= UB; Y++) {
if (pBanArr[X, Y] != GoalArr[Y, X])
return false;
}
}
return true;
}
//盤面を符号なしInt型で表現
static uint BanToUint(char[,] pBanArr)
{
var sb = new System.Text.StringBuilder();
for (int X = 0; X <= UB; X++) {
for (int Y = 0; Y <= UB; Y++) {
if (pBanArr[X, Y] == '歩') sb.Append(1);
else if (pBanArr[X, Y] == '銅') sb.Append(2);
else if (pBanArr[X, Y] == '銀') sb.Append(3);
else if (pBanArr[X, Y] == '飛') sb.Append(4);
else sb.Append(0);
}
}
return Convert.ToUInt32(sb.ToString(), 8);
}
//盤面を表示
static void PrintBan(char[,] pBanArr)
{
var sb = new System.Text.StringBuilder();
for (int Y = 0; Y <= UB; Y++) {
for (int X = 0; X <= UB; X++) {
if (pBanArr[X, Y] == ' ') sb.Append(" ");
else sb.Append(pBanArr[X, Y]);
}
sb.AppendLine();
}
Console.WriteLine(sb.ToString());
}
}