using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
const int UB_X = 5;
const int UB_Y = 11;
const char FillChar = '*'; //埋まってるスペースの文字
struct JyoutaiDef
{
internal int CurrY;
internal char[,] HaitiArr; //円盤の配置
internal List<char> UsedEnbanList; //使用済の円盤リスト
}
//回転させた円盤のDictionary
static Dictionary<char, List<int[]>> KaitenEnbanDict = new Dictionary<char, List<int[]>>();
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
for (char wkChar = 'A'; wkChar <= 'L'; wkChar++) {
DeriveKaitenEnbanDict(wkChar);
}
var stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.CurrY = 0;
foreach (var AnyPair in KaitenEnbanDict) {
foreach (int[] EachAnaArr in AnyPair.Value) {
WillPush.HaitiArr = new char[UB_X + 1, UB_Y + 1];
for (int X = 0; X <= UB_X; X++)
for (int Y = 0; Y <= UB_Y; Y++) WillPush.HaitiArr[X, Y] = ' ';
EnbanSet(WillPush.HaitiArr, AnyPair.Key, EachAnaArr, WillPush.CurrY);
WillPush.UsedEnbanList = new List<char>() { AnyPair.Key };
stk.Push(WillPush);
}
}
int AnswerCnt = 0;
while (stk.Count > 0) {
JyoutaiDef Popped = stk.Pop();
if (Popped.CurrY == UB_Y) {
Console.WriteLine("解{0,2}を発見。経過時間={1}", ++AnswerCnt, sw.Elapsed);
PrintAnswer(Popped.HaitiArr);
continue;
}
foreach (var AnyPair in KaitenEnbanDict) {
if (Popped.UsedEnbanList.Contains(AnyPair.Key)) continue;
WillPush.CurrY = Popped.CurrY + 1;
//回転と鏡像解除外で、Fは上半分固定とする
if (AnyPair.Key == 'F' && WillPush.CurrY > UB_Y / 2) {
continue;
}
foreach (int[] EachAnaArr in AnyPair.Value) {
WillPush.HaitiArr = (char[,])Popped.HaitiArr.Clone();
EnbanSet(WillPush.HaitiArr, AnyPair.Key, EachAnaArr, WillPush.CurrY);
if (IsValid(WillPush.CurrY, WillPush.HaitiArr) == false) {
continue;
}
WillPush.UsedEnbanList = new List<char>(Popped.UsedEnbanList);
WillPush.UsedEnbanList.Add(AnyPair.Key);
stk.Push(WillPush);
}
}
}
}
//回転させた円盤のDictionaryを求める
static void DeriveKaitenEnbanDict(char pKey)
{
int[] BaseAnaArr;
if (pKey == 'A') BaseAnaArr = new int[] { 0 };
else if (pKey == 'B') BaseAnaArr = new int[] { 0, 1 };
else if (pKey == 'C') BaseAnaArr = new int[] { 0, 2 };
else if (pKey == 'D') BaseAnaArr = new int[] { 0, 3 };
else if (pKey == 'E') BaseAnaArr = new int[] { 0, 1, 2 };
else if (pKey == 'F') BaseAnaArr = new int[] { 0, 1, 3 };
else if (pKey == 'G') BaseAnaArr = new int[] { 0, 2, 4 };
else if (pKey == 'H') BaseAnaArr = new int[] { 0, 1, 2, 3 };
else if (pKey == 'I') BaseAnaArr = new int[] { 0, 1, 2, 4 };
else if (pKey == 'J') BaseAnaArr = new int[] { 0, 1, 3, 4 };
else if (pKey == 'K') BaseAnaArr = new int[] { 0, 1, 2, 3, 4 };
else BaseAnaArr = new int[] { 0, 1, 2, 3, 4, 5 };
var AnaArrList = new List<int[]>();
//始点変更の分
Action<int[]> AddSitenhenkouHaiti = pAnaArr =>
{
for (int pKasanVal = 0; pKasanVal <= UB_X; pKasanVal++) {
int[] WillAdd = (int[])pAnaArr.Clone();
for (int I = 0; I <= WillAdd.GetUpperBound(0); I++) {
WillAdd[I] += pKasanVal;
if (WillAdd[I] > UB_X) WillAdd[I] -= (UB_X + 1);
}
Array.Sort<int>(WillAdd);
AnaArrList.Add(WillAdd);
}
};
//始点変更の分
AddSitenhenkouHaiti(BaseAnaArr);
//回転と始点変更の分
for (int I = 0; I <= BaseAnaArr.GetUpperBound(0); I++) {
if (BaseAnaArr[I] == 1) BaseAnaArr[I] = 5;
else if (BaseAnaArr[I] == 2) BaseAnaArr[I] = 4;
else if (BaseAnaArr[I] == 4) BaseAnaArr[I] = 2;
else if (BaseAnaArr[I] == 5) BaseAnaArr[I] = 1;
}
AddSitenhenkouHaiti(BaseAnaArr);
//穴配置のDistinct
for (int I = AnaArrList.Count - 1; 0 <= I; I--) {
bool HasDuplicate = false;
for (int J = 0; J <= I - 1; J++) {
if (AnaArrList[I].SequenceEqual(AnaArrList[J])) {
HasDuplicate = true;
break;
}
}
if (HasDuplicate) AnaArrList.RemoveAt(I);
}
//回転と鏡像解除外で、Fは固定とする
if (pKey == 'F') AnaArrList.RemoveRange(1, AnaArrList.Count - 1);
//AnaArrList
KaitenEnbanDict[pKey] = AnaArrList;
}
//円盤を配置
static void EnbanSet(char[,] pHaitiArr, char pKey, int[] pAnaArr, int pTargetY)
{
for (int I = 0; I <= pHaitiArr.GetUpperBound(0); I++)
pHaitiArr[I, pTargetY] = (pAnaArr.Contains(I) ? pKey : FillChar);
}
//現在が有効な状態か?
static bool IsValid(int pCurrY, char[,] pHaitiArr)
{
//枝切り条件1 長さが3未満の穴が存在したら枝切り
//枝切り条件2 長さが4以上の穴が存在したら枝切り
for (int X = 0; X <= UB_X; X++) {
bool IsAna = false;
int AnaLength = 0;
for (int Y = 0; Y <= pCurrY; Y++) {
if (pHaitiArr[X, Y] == FillChar) { //アルファベットでない場合
if (IsAna && AnaLength < 3) return false; //枝切り条件1
IsAna = false;
AnaLength = 0;
}
else {//アルファベットの場合
IsAna = true;
if (++AnaLength > 3) return false; //枝切り条件2
}
if (Y == UB_Y)
if (IsAna && AnaLength < 3) return false; //枝切り条件1
}
}
return true;
}
//解を出力
static void PrintAnswer(char[,] pHaitiArr)
{
var sb = new System.Text.StringBuilder();
for (int Y = 0; Y <= UB_Y; Y++) {
for (int X = 0; X <= UB_X; X++) {
sb.Append(pHaitiArr[X, Y]);
}
sb.AppendLine();
}
Console.WriteLine(sb.ToString());
}
}