トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
24-20 スリザーリンク
問題
ニコリのスリザーリンクを解きます。
例題と答え
1 点と点の間にタテヨコに線を引き、全体で1つの輪っかを作りましょう。
2 4つの点で作られた正方形の中にある数字は、その正方形の辺に引く線の数を表しています。
数字のない正方形には、何本の線を引くかわかりません。
3 線を交差させたり、枝分かれさせたりしてはいけません。
ソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
//4方向のビット定義
const int Bit0 = 1; //上方向
const int Bit1 = 2; //右方向
const int Bit2 = 4; //下方向
const int Bit3 = 8; //左方向
const int BitAllOn = Bit0 | Bit1 | Bit2 | Bit3;
struct PointDef
{
internal int X;
internal int Y;
}
//数値情報の構造体
struct NumInfoDef
{
internal int NumVal; //数値
internal PointDef Point; //座標
}
static char[,] QuestionArr;
static int UB_X;
static int UB_Y;
static NumInfoDef[] NumInfoArr; //数値情報の配列
//解となる、内側の島の座標
static List<PointDef> AnswerUtigawaShimaList = new List<PointDef>();
static System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
static void Main()
{
char[,] Q01Arr = {{' ','0',' ','1',' ',' ','1',' '},
{' ','3',' ',' ','2','3',' ','2'},
{' ',' ','0',' ',' ',' ',' ','0'},
{' ','3',' ',' ','0',' ',' ',' '},
{' ',' ',' ','3',' ',' ','0',' '},
{'1',' ',' ',' ',' ','3',' ',' '},
{'3',' ','1','3',' ',' ','3',' '},
{' ','0',' ',' ','3',' ','3',' '}};
QuestionArr = XYRev(Q01Arr);
UB_X = QuestionArr.GetUpperBound(0);
UB_Y = QuestionArr.GetUpperBound(1);
//第1フェーズ 数値情報を作成する
NumInfoArr = DeriveNumInfoArr();
//デバッグ用の数値情報の出力
//DebugPrintNumInfo();
//第2フェーズ 0のマス目の周りにバツ印を付ける
int[,] BatuArr = WriteBatuAroundZero();
//第3フェーズ 確定探索
int[,] ArrowArr = new int[UB_X + 1, UB_Y + 1];
ExecKakuteiTansaku(ArrowArr, BatuArr, true);
//Console.WriteLine("第3フェーズ 確定探索後の線の配置");
//PrintBitValueArr(ArrowArr);
//Console.WriteLine("第3フェーズ 確定探索後のバツ印の配置");
//PrintBitValueArr(BatuArr);
//第4フェーズ 深さ優先探索
ExecDFS(ArrowArr, BatuArr);
}
//X座標とY座標の入れ替え
static char[,] XYRev(char[,] pBaseArr)
{
int RevArrUB_X = pBaseArr.GetUpperBound(1);
int RevArrUB_Y = pBaseArr.GetUpperBound(0);
char[,] WillReturnArr = new char[RevArrUB_X + 1, RevArrUB_Y + 1];
for (int X = 0; X <= RevArrUB_X; X++) {
for (int Y = 0; Y <= RevArrUB_Y; Y++) {
WillReturnArr[X, Y] = pBaseArr[Y, X];
}
}
return WillReturnArr;
}
//第1フェーズ 数値情報を作成する
static NumInfoDef[] DeriveNumInfoArr()
{
var WillReturnList = new List<NumInfoDef>();
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
if (QuestionArr[LoopX, LoopY] == ' ')
continue;
NumInfoDef WillAdd;
WillAdd.NumVal = StrToDec(QuestionArr[LoopX, LoopY]);
WillAdd.Point = new PointDef() { X = LoopX, Y = LoopY };
WillReturnList.Add(WillAdd);
}
}
return WillReturnList.ToArray();
}
//char型を10進数に変換
static int StrToDec(char pStr)
{
if (pStr == ' ') return 0;
return (int)(pStr - '0');
}
//デバッグ用の数値情報の出力
static void DebugPrintNumInfo()
{
for (int I = 0; I <= NumInfoArr.GetUpperBound(0); I++) {
Console.WriteLine("■■■■■■■■■■■■■");
Console.WriteLine("{0}個目の数値情報", I + 1);
Console.WriteLine("NumVal={0}", NumInfoArr[I].NumVal);
Console.WriteLine("(X,Y)=({0},{1})", NumInfoArr[I].Point.X, NumInfoArr[I].Point.Y);
}
}
//ビット演算の結果が0より大きいかを返す
static bool IsBitTrue(int pInt)
{
return pInt > 0;
}
//ビット演算の結果が0かを返す
static bool IsBitFalse(int pInt)
{
return pInt == 0;
}
//第2フェーズ 0のマス目の周りにバツ印を付ける
static int[,] WriteBatuAroundZero()
{
int[,] BatuArr = new int[UB_X + 1, UB_Y + 1];
foreach (NumInfoDef EachNumInfo in NumInfoArr) {
if (EachNumInfo.NumVal != 0) continue;
int X = EachNumInfo.Point.X;
int Y = EachNumInfo.Point.Y;
TargetPointBitOn(X, Y, BitAllOn, BatuArr);
}
return BatuArr;
}
//指定座標の指定ビットを立てる、指定座標の隣接座標のビットも変更する
static void TargetPointBitOn(int pX, int pY, int pBit, int[,] pTargetArr)
{
if (pX < 0 || UB_X < pX) return;
if (pY < 0 || UB_Y < pY) return;
pTargetArr[pX, pY] |= pBit;
//上の座標のビットを立てる
if (IsBitTrue(pBit & Bit0) && pY - 1 >= 0) pTargetArr[pX, pY - 1] |= Bit2;
//右の座標のビットを立てる
if (IsBitTrue(pBit & Bit1) && pX + 1 <= UB_X) pTargetArr[pX + 1, pY] |= Bit3;
//下の座標のビットを立てる
if (IsBitTrue(pBit & Bit2) && pY + 1 <= UB_Y) pTargetArr[pX, pY + 1] |= Bit0;
//左の座標のビットを立てる
if (IsBitTrue(pBit & Bit3) && pX - 1 >= 0) pTargetArr[pX - 1, pY] |= Bit1;
}
//第3フェーズ 確定探索
static void ExecKakuteiTansaku(int[,] pArrowArr, int[,] pBatuArr, bool WillCallBatuKakuteiTansaku5)
{
bool FirstFlag = true;//1回だけ行う確定探索の制御用
//確定探索を行うAct
Action<int, int> KakuteiTansakuAct = (pX, pY) =>
{
//線を引く確定探索1 数値と比較して、引ける線が一意に決まる場合
ArrowKakuteiTansaku1(pX, pY, pArrowArr, pBatuArr);
//線を引く確定探索2 数値の3の8近傍に数値の3がある場合
if (FirstFlag) ArrowKakuteiTansaku2(pX, pY, pArrowArr);
//線を引く確定探索3 線の方向が一意に決まる場合
ArrowKakuteiTansaku3(pX, pY, pArrowArr, pBatuArr);
//線を引く確定探索4 数値の3の頂点に線が引かれている場合
ArrowKakuteiTansaku4(pX, pY, pArrowArr);
//バツ印を付ける確定探索1 既に数値の線を引いている場合
BatuKakuteiTansaku1(pX, pY, pArrowArr, pBatuArr);
//バツ印を付ける確定探索2 線を3本引いている場合
BatuKakuteiTansaku2(pX, pY, pArrowArr, pBatuArr);
//バツ印を付ける確定探索3 枝分かれが発生するので引けない場合
BatuKakuteiTansaku3(pX, pY, pArrowArr, pBatuArr);
//バツ印を付ける確定探索4 線を引くと、行き先が全てバツ印になる場合
BatuKakuteiTansaku4(pX, pY, pArrowArr, pBatuArr);
//バツ印を付ける確定探索6 数値の1の頂点に線とバツが隣接している場合
BatuKakuteiTansaku6(pX, pY, pArrowArr, pBatuArr);
//数値確定探索1 数値が1か3で頂点にバツ印2つが隣接している場合
SuutiKakuteiTansaku1(pX, pY, pArrowArr, pBatuArr);
//数値確定探索2 数値が2で頂点にバツ印2つが隣接している場合
SuutiKakuteiTansaku2(pX, pY, pArrowArr, pBatuArr);
};
while (true) {
int[,] PrevArrowArr = (int[,])pArrowArr.Clone();
int[,] PrevBatuArr = (int[,])pBatuArr.Clone();
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
KakuteiTansakuAct(LoopX, LoopY);
}
}
//確定探索で確定するマスがあったらContine
if (pArrowArr.Cast<int>().SequenceEqual(PrevArrowArr.Cast<int>()) == false) continue;
if (pBatuArr.Cast<int>().SequenceEqual(PrevBatuArr.Cast<int>()) == false) continue;
//バツ印を付ける確定探索5 線を引くと、孤立した島ができる場合
if (WillCallBatuKakuteiTansaku5) {
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
BatuKakuteiTansaku5(LoopX, LoopY, pArrowArr, pBatuArr);
}
}
}
//確定探索で確定するマスがなくなったらBreak
if (pArrowArr.Cast<int>().SequenceEqual(PrevArrowArr.Cast<int>()) == false) continue;
if (pBatuArr.Cast<int>().SequenceEqual(PrevBatuArr.Cast<int>()) == false) continue;
break;
}
}
//線を引く確定探索1 数値と比較して、引ける線が一意に決まる場合
static void ArrowKakuteiTansaku1(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
int wkInd = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd == -1) return; //数値マスでない場合
int wkNumVal = NumInfoArr[wkInd].NumVal;
//バツの数が不足している場合
int BatuCnt = DeriveTargetPointBitCnt(pX, pY, pBatuArr);
if (BatuCnt < 4 - wkNumVal) return;
//既に数値分の線が引かれている場合
int ArrowCnt = DeriveTargetPointBitCnt(pX, pY, pArrowArr);
if (wkNumVal == ArrowCnt) return;
//バツでない箇所は、全て線で確定
if (IsBitFalse(pBatuArr[pX, pY] & Bit0)) TargetPointBitOn(pX, pY, Bit0, pArrowArr);
if (IsBitFalse(pBatuArr[pX, pY] & Bit1)) TargetPointBitOn(pX, pY, Bit1, pArrowArr);
if (IsBitFalse(pBatuArr[pX, pY] & Bit2)) TargetPointBitOn(pX, pY, Bit2, pArrowArr);
if (IsBitFalse(pBatuArr[pX, pY] & Bit3)) TargetPointBitOn(pX, pY, Bit3, pArrowArr);
}
//指定座標に立っている、ビットの数を返す
static int DeriveTargetPointBitCnt(int pX, int pY, int[,] pTargetArr)
{
int WillReturnCnt = 0;
int CurrVal = pTargetArr[pX, pY];
WillReturnCnt += Math.Sign(CurrVal & Bit0);
WillReturnCnt += Math.Sign(CurrVal & Bit1);
WillReturnCnt += Math.Sign(CurrVal & Bit2);
WillReturnCnt += Math.Sign(CurrVal & Bit3);
return WillReturnCnt;
}
//線を引く確定探索2 数値の3の8近傍に数値の3がある場合
static void ArrowKakuteiTansaku2(int pX, int pY, int[,] pArrowArr)
{
int wkInd1 = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd1 == -1) return; //数値マスでない場合
int wkNumVal = NumInfoArr[wkInd1].NumVal;
if (wkNumVal != 3) return; //数値の3でない場合
NumInfoDef[] NumInfo3Only = Array.FindAll(NumInfoArr, A => A.NumVal == 3);
//Order By X座標、Y座標で、後続の座標のみをチェックする
//3の右に3があるケース
bool HasMigi = Array.Exists(NumInfo3Only, A => A.Point.X == pX + 1 && A.Point.Y == pY);
//3の左下に3があるケース
bool HasHidariShita = Array.Exists(NumInfo3Only, A => A.Point.X == pX - 1 && A.Point.Y == pY + 1);
//3の下に3があるケース
bool HasShita = Array.Exists(NumInfo3Only, A => A.Point.X == pX && A.Point.Y == pY + 1);
//3の右下に3があるケース
bool HasMigiShita = Array.Exists(NumInfo3Only, A => A.Point.X == pX + 1 && A.Point.Y == pY + 1);
if (HasMigi) { //3の右に3があるケース
TargetPointBitOn(pX, pY, Bit1 | Bit3, pArrowArr);
TargetPointBitOn(pX + 1, pY, Bit1 | Bit3, pArrowArr);
}
if (HasHidariShita) { //3の左下に3があるケース
TargetPointBitOn(pX, pY, Bit0 | Bit1, pArrowArr);
TargetPointBitOn(pX - 1, pY + 1, Bit2 | Bit3, pArrowArr);
}
if (HasShita) { //3の下に3があるケース
TargetPointBitOn(pX, pY, Bit0 | Bit2, pArrowArr);
TargetPointBitOn(pX, pY + 1, Bit0 | Bit2, pArrowArr);
}
if (HasMigiShita) { //3の右下に3があるケース
TargetPointBitOn(pX, pY, Bit0 | Bit3, pArrowArr);
TargetPointBitOn(pX + 1, pY + 1, Bit1 | Bit2, pArrowArr);
}
}
//線を引く確定探索3 線の方向が一意に決まる場合
static void ArrowKakuteiTansaku3(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
//両端で、バツ印が2つと、未確定マスが1マスなら、線で確定する
//指定座標の上の線から、つなげる場合
if (IsBitTrue(pArrowArr[pX, pY] & Bit0)) {
//左端からつなげる場合
CheckUniqLine(RinsetuCase01_Bit_UeLine_Hidari(pX, pY), pArrowArr, pBatuArr);
//右端からつなげる場合
CheckUniqLine(RinsetuCase02_Bit_UeLine_Migi(pX, pY), pArrowArr, pBatuArr);
}
//指定座標の右の線から、つなげる場合
if (IsBitTrue(pArrowArr[pX, pY] & Bit1)) {
//上端からつなげる場合
CheckUniqLine(RinsetuCase03_Bit_MigiLine_Ue(pX, pY), pArrowArr, pBatuArr);
//下端からつなげる場合
CheckUniqLine(RinsetuCase04_Bit_MigiLine_Shita(pX, pY), pArrowArr, pBatuArr);
}
//指定座標の下の線から、つなげる場合
if (IsBitTrue(pArrowArr[pX, pY] & Bit2)) {
//左端からつなげる場合
CheckUniqLine(RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY), pArrowArr, pBatuArr);
//右端からつなげる場合
CheckUniqLine(RinsetuCase06_Bit_ShitaLine_Migi(pX, pY), pArrowArr, pBatuArr);
}
//指定座標の左の線から、つなげる場合
if (IsBitTrue(pArrowArr[pX, pY] & Bit3)) {
//上端からつなげる場合
CheckUniqLine(RinsetuCase07_Bit_HidariLine_Ue(pX, pY), pArrowArr, pBatuArr);
//下端からつなげる場合
CheckUniqLine(RinsetuCase08_Bit_HidariLine_Shita(pX, pY), pArrowArr, pBatuArr);
}
}
//隣接した線の情報を持つ構造体
struct RinsetuBitDef
{
internal int X;
internal int Y;
internal int Bit;
}
//指定座標の上の線の左端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase01_Bit_UeLine_Hidari(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY - 1, Bit = Bit3 });
WillRetunList.Add(new RinsetuBitDef() { X = pX - 1, Y = pY, Bit = Bit0 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit3 });
return WillRetunList;
}
//指定座標の上の線の右端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase02_Bit_UeLine_Migi(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY - 1, Bit = Bit1 });
WillRetunList.Add(new RinsetuBitDef() { X = pX + 1, Y = pY, Bit = Bit0 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit1 });
return WillRetunList;
}
//指定座標の右の線の上端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase03_Bit_MigiLine_Ue(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY - 1, Bit = Bit1 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit0 });
WillRetunList.Add(new RinsetuBitDef() { X = pX + 1, Y = pY, Bit = Bit0 });
return WillRetunList;
}
//指定座標の右の線の下端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase04_Bit_MigiLine_Shita(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY + 1, Bit = Bit1 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit2 });
WillRetunList.Add(new RinsetuBitDef() { X = pX + 1, Y = pY, Bit = Bit2 });
return WillRetunList;
}
//指定座標の下の線の左端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase05_Bit_ShitaLine_Hidari(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit3 });
WillRetunList.Add(new RinsetuBitDef() { X = pX - 1, Y = pY, Bit = Bit2 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY + 1, Bit = Bit3 });
return WillRetunList;
}
//指定座標の下の線の右端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase06_Bit_ShitaLine_Migi(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit1 });
WillRetunList.Add(new RinsetuBitDef() { X = pX + 1, Y = pY, Bit = Bit2 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY + 1, Bit = Bit1 });
return WillRetunList;
}
//指定座標の左の線の上端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase07_Bit_HidariLine_Ue(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX - 1, Y = pY, Bit = Bit0 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY - 1, Bit = Bit3 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit0 });
return WillRetunList;
}
//指定座標の左の線の下端に、隣接した線の情報をListで返す
static List<RinsetuBitDef> RinsetuCase08_Bit_HidariLine_Shita(int pX, int pY)
{
var WillRetunList = new List<RinsetuBitDef>();
WillRetunList.Add(new RinsetuBitDef() { X = pX - 1, Y = pY, Bit = Bit2 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY, Bit = Bit2 });
WillRetunList.Add(new RinsetuBitDef() { X = pX, Y = pY + 1, Bit = Bit3 });
return WillRetunList;
}
//線の方向が一意に決まるなら、線を引く
static void CheckUniqLine(List<RinsetuBitDef> pRinsetuBitList, int[,] pArrowArr, int[,] pBatuArr)
{
//バツ印のビットかを判定
Predicate<RinsetuBitDef> IsBatuPointPred = pRinsetuBit =>
{
PointDef wkPoint = new PointDef() { X = pRinsetuBit.X, Y = pRinsetuBit.Y };
return IsBatuPoint(wkPoint, pRinsetuBit.Bit, pBatuArr);
};
//バツ印で線が引けない要素をRemoveした結果が、未確定の線だけなら、線で確定
pRinsetuBitList.RemoveAll(A => IsBatuPointPred(A));
if (pRinsetuBitList.Count != 1) return;
int RestPointX = pRinsetuBitList[0].X;
int RestPointY = pRinsetuBitList[0].Y;
int RestOneBit = pRinsetuBitList[0].Bit;
PointDef RestOnePoint = new PointDef() { X = RestPointX, Y = RestPointY };
if (IsMikakuteiPoint(RestOnePoint, RestOneBit, pArrowArr, pBatuArr)) {
TargetPointBitOn(RestPointX, RestPointY, RestOneBit, pArrowArr);
}
}
//指定座標の指定ビットが未確定かを返す
//指定座標が範囲外ならFalseを返す
static bool IsMikakuteiPoint(PointDef pPoint, int pBit, int[,] pArrowArr, int[,] pBatuArr)
{
int CurrX = pPoint.X, CurrY = pPoint.Y;
if (CurrX < 0 || UB_X < CurrX) return false;
if (CurrY < 0 || UB_Y < CurrY) return false;
if (IsBitTrue(pArrowArr[CurrX, CurrY] & pBit)) return false;
if (IsBitTrue(pBatuArr[CurrX, CurrY] & pBit)) return false;
return true;
}
//指定座標の指定ビットが線かを判定する
//指定座標が範囲外なら線でないとみなす
static bool IsArrowPoint(PointDef pPoint, int pBit, int[,] pArrowArr)
{
int CurrX = pPoint.X, CurrY = pPoint.Y;
if (CurrX < 0 || UB_X < CurrX) return false;
if (CurrY < 0 || UB_Y < CurrY) return false;
return IsBitTrue(pArrowArr[CurrX, CurrY] & pBit);
}
//指定座標の指定ビットがバツ印かを判定する
//指定座標が範囲外ならバツ印とみなす
static bool IsBatuPoint(PointDef pPoint, int pBit, int[,] pBatuArr)
{
int CurrX = pPoint.X, CurrY = pPoint.Y;
if (CurrX < 0 || UB_X < CurrX) return true;
if (CurrY < 0 || UB_Y < CurrY) return true;
return IsBitTrue(pBatuArr[CurrX, CurrY] & pBit);
}
//線を引く確定探索4 数値の3の頂点に線が引かれている場合
static void ArrowKakuteiTansaku4(int pX, int pY, int[,] pArrowArr)
{
int wkInd = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd == -1) return; //数値マスでない場合
int CurrNumVal = NumInfoArr[wkInd].NumVal;
//3でない場合
if (CurrNumVal != 3) return;
List<RinsetuBitDef> wkRinsetuBitList = null;
Action<int, int> KakuteiAct = (pKakuteiBit1, pKakuteiBit2) =>
{
//自分の座標はRemove
wkRinsetuBitList.RemoveAll(A => A.X == pX && A.Y == pY);
//頂点に線が引かれているかをチェック
if (wkRinsetuBitList.Exists(A => IsArrowPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pArrowArr)) == false)
return;
//線の確定処理
TargetPointBitOn(pX, pY, pKakuteiBit1, pArrowArr);
TargetPointBitOn(pX, pY, pKakuteiBit2, pArrowArr);
};
//指定座標の上の線の、左端をチェック
wkRinsetuBitList = RinsetuCase01_Bit_UeLine_Hidari(pX, pY);
KakuteiAct(Bit1, Bit2);
//指定座標の上の線の、右端をチェック
wkRinsetuBitList = RinsetuCase02_Bit_UeLine_Migi(pX, pY);
KakuteiAct(Bit2, Bit3);
//指定座標の下の線の、左端をチェック
wkRinsetuBitList = RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY);
KakuteiAct(Bit0, Bit1);
//指定座標の下の線の、右端をチェック
wkRinsetuBitList = RinsetuCase06_Bit_ShitaLine_Migi(pX, pY);
KakuteiAct(Bit0, Bit3);
}
//バツ印を付ける確定探索1 既に数値の線を引いている場合
static void BatuKakuteiTansaku1(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
int wkInd = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd == -1) return; //数値マスでない場合
int wkNumVal = NumInfoArr[wkInd].NumVal;
//線の数が不足している場合
int ArrowCnt = DeriveTargetPointBitCnt(pX, pY, pArrowArr);
if (ArrowCnt < wkNumVal) return;
//バツが必要数に到達している場合
int BatuCnt = DeriveTargetPointBitCnt(pX, pY, pBatuArr);
if (BatuCnt == 4 - wkNumVal) return;
//線でない箇所は、全てバツで確定
if (IsBitFalse(pArrowArr[pX, pY] & Bit0)) TargetPointBitOn(pX, pY, Bit0, pBatuArr);
if (IsBitFalse(pArrowArr[pX, pY] & Bit1)) TargetPointBitOn(pX, pY, Bit1, pBatuArr);
if (IsBitFalse(pArrowArr[pX, pY] & Bit2)) TargetPointBitOn(pX, pY, Bit2, pBatuArr);
if (IsBitFalse(pArrowArr[pX, pY] & Bit3)) TargetPointBitOn(pX, pY, Bit3, pBatuArr);
}
//バツ印を付ける確定探索2 線を3本引いている場合
static void BatuKakuteiTansaku2(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
int ArrowCnt = DeriveTargetPointBitCnt(pX, pY, pArrowArr);
if (ArrowCnt < 3) return;
//線でない箇所がバツで確定
if (IsBitFalse(pArrowArr[pX, pY] & Bit0)) TargetPointBitOn(pX, pY, Bit0, pBatuArr);
if (IsBitFalse(pArrowArr[pX, pY] & Bit1)) TargetPointBitOn(pX, pY, Bit1, pBatuArr);
if (IsBitFalse(pArrowArr[pX, pY] & Bit2)) TargetPointBitOn(pX, pY, Bit2, pBatuArr);
if (IsBitFalse(pArrowArr[pX, pY] & Bit3)) TargetPointBitOn(pX, pY, Bit3, pBatuArr);
}
//バツ印を付ける確定探索3 枝分かれが発生するので引けない場合
static void BatuKakuteiTansaku3(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
if (pX + 1 <= UB_X) { //右にマスが存在する場合
//ケース1
//11
if (IsBitTrue(pArrowArr[pX, pY] & Bit0)
&& IsBitTrue(pArrowArr[pX + 1, pY] & Bit0)) {
TargetPointBitOn(pX, pY, Bit1, pBatuArr);
TargetPointBitOn(pX, pY - 1, Bit1, pBatuArr);
}
//ケース2
//44
if (IsBitTrue(pArrowArr[pX, pY] & Bit2)
&& IsBitTrue(pArrowArr[pX + 1, pY] & Bit2)) {
TargetPointBitOn(pX, pY, Bit1, pBatuArr);
TargetPointBitOn(pX, pY + 1, Bit1, pBatuArr);
}
}
if (pY + 1 <= UB_Y) { //下にマスが存在する場合
//ケース3
//2
//2
if (IsBitTrue(pArrowArr[pX, pY] & Bit1)
&& IsBitTrue(pArrowArr[pX, pY + 1] & Bit1)) {
TargetPointBitOn(pX, pY, Bit2, pBatuArr);
TargetPointBitOn(pX + 1, pY, Bit2, pBatuArr);
}
//ケース4
//8
//8
if (IsBitTrue(pArrowArr[pX, pY] & Bit3)
&& IsBitTrue(pArrowArr[pX, pY + 1] & Bit3)) {
TargetPointBitOn(pX, pY, Bit2, pBatuArr);
TargetPointBitOn(pX - 1, pY, Bit2, pBatuArr);
}
}
//ケース5
//3
if (IsBitTrue(pArrowArr[pX, pY] & Bit0)
&& IsBitTrue(pArrowArr[pX, pY] & Bit1)) {
TargetPointBitOn(pX, pY - 1, Bit1, pBatuArr);
TargetPointBitOn(pX + 1, pY, Bit0, pBatuArr);
}
//ケース6
//6
if (IsBitTrue(pArrowArr[pX, pY] & Bit1)
&& IsBitTrue(pArrowArr[pX, pY] & Bit2)) {
TargetPointBitOn(pX + 1, pY, Bit2, pBatuArr);
TargetPointBitOn(pX, pY + 1, Bit1, pBatuArr);
}
//ケース6
//C
if (IsBitTrue(pArrowArr[pX, pY] & Bit2)
&& IsBitTrue(pArrowArr[pX, pY] & Bit3)) {
TargetPointBitOn(pX - 1, pY, Bit2, pBatuArr);
TargetPointBitOn(pX, pY + 1, Bit3, pBatuArr);
}
//ケース7
//9
if (IsBitTrue(pArrowArr[pX, pY] & Bit3)
&& IsBitTrue(pArrowArr[pX, pY] & Bit0)) {
TargetPointBitOn(pX, pY - 1, Bit3, pBatuArr);
TargetPointBitOn(pX - 1, pY, Bit0, pBatuArr);
}
}
//バツ印を付ける確定探索4 線を引くと、行き先が全てバツ印になる場合
static void BatuKakuteiTansaku4(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
//両端で、少なくとも片方の方向が全てバツ印なら、線は引けないのでバツ印で確定する
List<RinsetuBitDef> wkRinsetuBitList;
//指定座標の上が未確定の場合
if (IsBitFalse(pArrowArr[pX, pY] & Bit0) && IsBitFalse(pBatuArr[pX, pY] & Bit0)) {
//両端のうちの、左方向をチェック
wkRinsetuBitList = RinsetuCase01_Bit_UeLine_Hidari(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit0, pBatuArr);
//両端のうちの、右方向をチェック
wkRinsetuBitList = RinsetuCase02_Bit_UeLine_Migi(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit0, pBatuArr);
}
//指定座標の右が未確定の場合
if (IsBitFalse(pArrowArr[pX, pY] & Bit1) && IsBitFalse(pBatuArr[pX, pY] & Bit1)) {
//両端のうちの、上方向をチェック
wkRinsetuBitList = RinsetuCase03_Bit_MigiLine_Ue(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit1, pBatuArr);
//両端のうちの、下方向をチェック
wkRinsetuBitList = RinsetuCase04_Bit_MigiLine_Shita(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit1, pBatuArr);
}
//指定座標の下が未確定の場合
if (IsBitFalse(pArrowArr[pX, pY] & Bit2) && IsBitFalse(pBatuArr[pX, pY] & Bit2)) {
//両端のうちの、左方向をチェック
wkRinsetuBitList = RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit2, pBatuArr);
//両端のうちの、右方向をチェック
wkRinsetuBitList = RinsetuCase06_Bit_ShitaLine_Migi(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit2, pBatuArr);
}
//指定座標の左が未確定の場合
if (IsBitFalse(pArrowArr[pX, pY] & Bit3) && IsBitFalse(pBatuArr[pX, pY] & Bit3)) {
//両端のうちの、上方向をチェック
wkRinsetuBitList = RinsetuCase07_Bit_HidariLine_Ue(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit3, pBatuArr);
//両端のうちの、下方向をチェック
wkRinsetuBitList = RinsetuCase08_Bit_HidariLine_Shita(pX, pY);
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)))
TargetPointBitOn(pX, pY, Bit3, pBatuArr);
}
}
//バツ印を付ける確定探索5 線を引くと、孤立した島ができる場合
static void BatuKakuteiTansaku5(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
//ビットが3つ確定しているかを判定
int wkCnt1 = DeriveTargetPointBitCnt(pX, pY, pArrowArr);
int wkCnt2 = DeriveTargetPointBitCnt(pX, pY, pBatuArr);
if (wkCnt1 + wkCnt2 != 3) return;
Action<int> wkAct = (pBit) =>
{
if (IsMikakuteiPoint(new PointDef() { X = pX, Y = pY }, pBit, pArrowArr, pBatuArr)) {
int[,] wkArrowArr = (int[,])pArrowArr.Clone();
TargetPointBitOn(pX, pY, pBit, wkArrowArr);
int[,] wkBatuArr = (int[,])pBatuArr.Clone();
ExecKakuteiTansaku(wkArrowArr, wkBatuArr, false);
if (IsValid(wkArrowArr)) return;
TargetPointBitOn(pX, pY, pBit, pBatuArr);
}
};
wkAct(Bit0); wkAct(Bit1); wkAct(Bit2); wkAct(Bit3);
}
//バツ印を付ける確定探索6 数値の1の頂点に線とバツが隣接している場合
static void BatuKakuteiTansaku6(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
int wkInd = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd == -1) return; //数値マスでない場合
int CurrNumVal = NumInfoArr[wkInd].NumVal;
//1でない場合
if (CurrNumVal != 1) return;
List<RinsetuBitDef> wkRinsetuBitList = null;
Action<int, int> KakuteiAct = (pKakuteiBit1, pKakuteiBit2) =>
{
//自分の座標はRemove
wkRinsetuBitList.RemoveAll(A => A.X == pX && A.Y == pY);
//頂点に線とバツが隣接しているかをチェック
if (wkRinsetuBitList.Exists(A => IsArrowPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pArrowArr)) == false) return;
if (wkRinsetuBitList.Exists(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)) == false) return;
//バツの確定処理
TargetPointBitOn(pX, pY, pKakuteiBit1, pBatuArr);
TargetPointBitOn(pX, pY, pKakuteiBit2, pBatuArr);
};
//指定座標の上の線の、左端をチェック
wkRinsetuBitList = RinsetuCase01_Bit_UeLine_Hidari(pX, pY);
KakuteiAct(Bit1, Bit2);
//指定座標の上の線の、右端をチェック
wkRinsetuBitList = RinsetuCase02_Bit_UeLine_Migi(pX, pY);
KakuteiAct(Bit2, Bit3);
//指定座標の下の線の、左端をチェック
wkRinsetuBitList = RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY);
KakuteiAct(Bit0, Bit1);
//指定座標の下の線の、右端をチェック
wkRinsetuBitList = RinsetuCase06_Bit_ShitaLine_Migi(pX, pY);
KakuteiAct(Bit0, Bit3);
}
//数値確定探索1 数値が1か3で頂点にバツ印2つが隣接している場合
static void SuutiKakuteiTansaku1(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
int wkInd = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd == -1) return; //数値マスでない場合
int CurrNumVal = NumInfoArr[wkInd].NumVal;
//1でも3でもない場合
if (CurrNumVal != 1 && CurrNumVal != 3) return;
List<RinsetuBitDef> wkRinsetuBitList = null;
Action<int, int> KakuteiAct = (pKakuteiBit1, pKakuteiBit2) =>
{
//自分の座標はRemove
wkRinsetuBitList.RemoveAll(A => A.X == pX && A.Y == pY);
//バツ印2つが隣接しているかをチェック
if (wkRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)) == false)
return;
//数値が1の場合は、バツが確定する
if (CurrNumVal == 1) {
TargetPointBitOn(pX, pY, pKakuteiBit1, pBatuArr);
TargetPointBitOn(pX, pY, pKakuteiBit2, pBatuArr);
}
//数値が3の場合は、線が確定する
if (CurrNumVal == 3) {
TargetPointBitOn(pX, pY, pKakuteiBit1, pArrowArr);
TargetPointBitOn(pX, pY, pKakuteiBit2, pArrowArr);
}
};
//指定座標の上の線の、左端をチェック
wkRinsetuBitList = RinsetuCase01_Bit_UeLine_Hidari(pX, pY);
KakuteiAct(Bit0, Bit3);
//指定座標の上の線の、右端をチェック
wkRinsetuBitList = RinsetuCase02_Bit_UeLine_Migi(pX, pY);
KakuteiAct(Bit0, Bit1);
//指定座標の下の線の、左端をチェック
wkRinsetuBitList = RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY);
KakuteiAct(Bit2, Bit3);
//指定座標の下の線の、右端をチェック
wkRinsetuBitList = RinsetuCase06_Bit_ShitaLine_Migi(pX, pY);
KakuteiAct(Bit1, Bit2);
}
//数値確定探索2 数値が2で頂点にバツ印2つが隣接している場合
static void SuutiKakuteiTansaku2(int pX, int pY, int[,] pArrowArr, int[,] pBatuArr)
{
int wkInd = Array.FindIndex(NumInfoArr, A => A.Point.X == pX && A.Point.Y == pY);
if (wkInd == -1) return; //数値マスでない場合
int CurrNumVal = NumInfoArr[wkInd].NumVal;
//2でない場合
if (CurrNumVal != 2) return;
//確定ビット数が1でない場合
int wkCnt1 = DeriveTargetPointBitCnt(pX, pY, pArrowArr);
int wkCnt2 = DeriveTargetPointBitCnt(pX, pY, pBatuArr);
if (wkCnt1 + wkCnt2 != 1) return;
Action<int, int, int[,], List<RinsetuBitDef>> KakuteiAct
= (pCheckBit, pCascadeBit, pTargetArr, pRinsetuBitList) =>
{
if (IsBitFalse(pTargetArr[pX, pY] & pCheckBit)) return;
//自分の座標はRemove
pRinsetuBitList.RemoveAll(A => A.X == pX && A.Y == pY);
//バツ印2つが隣接しているかをチェック
if (pRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr)) == false)
return;
TargetPointBitOn(pX, pY, pCascadeBit, pTargetArr);
};
KakuteiAct(Bit0, Bit1, pArrowArr, RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY));
KakuteiAct(Bit0, Bit1, pBatuArr, RinsetuCase05_Bit_ShitaLine_Hidari(pX, pY));
KakuteiAct(Bit0, Bit3, pArrowArr, RinsetuCase06_Bit_ShitaLine_Migi(pX, pY));
KakuteiAct(Bit0, Bit3, pBatuArr, RinsetuCase06_Bit_ShitaLine_Migi(pX, pY));
KakuteiAct(Bit1, Bit2, pArrowArr, RinsetuCase07_Bit_HidariLine_Ue(pX, pY));
KakuteiAct(Bit1, Bit2, pBatuArr, RinsetuCase07_Bit_HidariLine_Ue(pX, pY));
KakuteiAct(Bit1, Bit0, pArrowArr, RinsetuCase08_Bit_HidariLine_Shita(pX, pY));
KakuteiAct(Bit1, Bit0, pBatuArr, RinsetuCase08_Bit_HidariLine_Shita(pX, pY));
KakuteiAct(Bit2, Bit1, pArrowArr, RinsetuCase01_Bit_UeLine_Hidari(pX, pY));
KakuteiAct(Bit2, Bit1, pBatuArr, RinsetuCase01_Bit_UeLine_Hidari(pX, pY));
KakuteiAct(Bit2, Bit3, pArrowArr, RinsetuCase02_Bit_UeLine_Migi(pX, pY));
KakuteiAct(Bit2, Bit3, pBatuArr, RinsetuCase02_Bit_UeLine_Migi(pX, pY));
KakuteiAct(Bit3, Bit2, pArrowArr, RinsetuCase03_Bit_MigiLine_Ue(pX, pY));
KakuteiAct(Bit3, Bit2, pBatuArr, RinsetuCase03_Bit_MigiLine_Ue(pX, pY));
KakuteiAct(Bit3, Bit0, pArrowArr, RinsetuCase04_Bit_MigiLine_Shita(pX, pY));
KakuteiAct(Bit3, Bit0, pBatuArr, RinsetuCase04_Bit_MigiLine_Shita(pX, pY));
}
//4方向のビット値を出力
static void PrintBitValueArr(int[,] pTargetArr)
{
//数値を全角16進数に変換
Func<int, char> IntToWideHexFunc = (pInt) =>
{
if (0 <= pInt && pInt <= 9)
return (char)('0' + pInt);
return (char)('A' + pInt - 10);
};
var sb = new System.Text.StringBuilder();
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
sb.Append(IntToWideHexFunc(pTargetArr[LoopX, LoopY]));
}
sb.AppendLine();
}
Console.WriteLine(sb.ToString());
}
struct JyoutaiDef
{
internal int[,] ArrowArr;
internal int[,] BatuArr;
}
//第4フェーズ 深さ優先探索
static void ExecDFS(int[,] pArrowArr, int[,] pBatuArr)
{
var stk = new Stack<JyoutaiDef>();
JyoutaiDef WillPush;
WillPush.ArrowArr = pArrowArr;
WillPush.BatuArr = pBatuArr;
stk.Push(WillPush);
if (IsClear(pArrowArr, pBatuArr)) {
Console.WriteLine("確定探索だけで解を発見");
PrintAnswer();
return;
}
Console.WriteLine("確定探索だけで解を発見できませんでした。");
Console.WriteLine("深さ優先を使います。");
Console.WriteLine();
while (stk.Count > 0) {
JyoutaiDef Popped = stk.Pop();
if (IsClear(Popped.ArrowArr, Popped.BatuArr)) {
Console.WriteLine("解を発見。経過時間={0}", sw.Elapsed);
PrintAnswer();
return;
}
Func<int, int, int, bool> IsPushTarget = (pX, pY, pBit) =>
{
if (IsBitFalse(Popped.ArrowArr[pX, pY] & pBit)
&& IsBitFalse(Popped.BatuArr[pX, pY] & pBit)) return true;
return false;
};
//未確定ビットに線もしくはバツ印を設定
int TargetX = -1, TargetY = -1, TargetBit = -1;
bool WillBreak = false;
//数値マスを優先して決定する
foreach (NumInfoDef EachNumInfo in NumInfoArr) {
if (IsPushTarget(EachNumInfo.Point.X, EachNumInfo.Point.Y, Bit0)) {
TargetX = EachNumInfo.Point.X; TargetY = EachNumInfo.Point.Y; TargetBit = Bit0;
WillBreak = true; break;
}
if (IsPushTarget(EachNumInfo.Point.X, EachNumInfo.Point.Y, Bit1)) {
TargetX = EachNumInfo.Point.X; TargetY = EachNumInfo.Point.Y; TargetBit = Bit1;
WillBreak = true; break;
}
if (IsPushTarget(EachNumInfo.Point.X, EachNumInfo.Point.Y, Bit2)) {
TargetX = EachNumInfo.Point.X; TargetY = EachNumInfo.Point.Y; TargetBit = Bit2;
WillBreak = true; break;
}
if (IsPushTarget(EachNumInfo.Point.X, EachNumInfo.Point.Y, Bit3)) {
TargetX = EachNumInfo.Point.X; TargetY = EachNumInfo.Point.Y; TargetBit = Bit3;
WillBreak = true; break;
}
if (WillBreak) break;
}
//数値マス以外のマス
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
if (WillBreak) break;
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
if (Array.Exists(NumInfoArr, A => A.Point.X == LoopX && A.Point.Y == LoopY))
continue;
if (IsPushTarget(LoopX, LoopY, Bit0)) {
TargetX = LoopX; TargetY = LoopY; TargetBit = Bit0;
WillBreak = true; break;
}
if (IsPushTarget(LoopX, LoopY, Bit1)) {
TargetX = LoopX; TargetY = LoopY; TargetBit = Bit1;
WillBreak = true; break;
}
if (IsPushTarget(LoopX, LoopY, Bit2)) {
TargetX = LoopX; TargetY = LoopY; TargetBit = Bit2;
WillBreak = true; break;
}
if (IsPushTarget(LoopX, LoopY, Bit3)) {
TargetX = LoopX; TargetY = LoopY; TargetBit = Bit3;
WillBreak = true; break;
}
}
}
//Push処理
if (TargetX != -1) {
WillPush.ArrowArr = (int[,])Popped.ArrowArr.Clone();
WillPush.BatuArr = (int[,])Popped.BatuArr.Clone();
TargetPointBitOn(TargetX, TargetY, TargetBit, WillPush.ArrowArr);
//Push前に確定探索
ExecKakuteiTansaku(WillPush.ArrowArr, WillPush.BatuArr, true);
if (IsValid(WillPush.ArrowArr)) stk.Push(WillPush);
WillPush.ArrowArr = (int[,])Popped.ArrowArr.Clone();
WillPush.BatuArr = (int[,])Popped.BatuArr.Clone();
TargetPointBitOn(TargetX, TargetY, TargetBit, WillPush.BatuArr);
//Push前に確定探索
ExecKakuteiTansaku(WillPush.ArrowArr, WillPush.BatuArr, true);
if (IsValid(WillPush.ArrowArr)) stk.Push(WillPush);
}
}
}
//盤面が有効な状態かを判定
static bool IsValid(int[,] pArrowArr)
{
//内側の島の座標のList
var UtigawaShimaList = new List<PointDef>();
//外側の島の座標のList
var SotogawaShimaList = new List<PointDef>();
//外周の時計回りで、UnionFindの開始座標とする
bool PushedUtigawa = false;
for (int LoopX = 0; LoopX <= UB_X - 1; LoopX++) {
if (IsBitTrue(pArrowArr[LoopX, 0] & Bit0)) { //内側の島かを判定
if (PushedUtigawa) { //内側の島をPush済の場合
if (UtigawaShimaList.Exists( //別の内側の島から到達不能ならNG
A => A.X == LoopX && A.Y == 0) == false) {
return false;
}
continue;
}
PushedUtigawa = true;
ExecUnionFind(LoopX, 0, pArrowArr, UtigawaShimaList);
}
else ExecUnionFind(LoopX, 0, pArrowArr, SotogawaShimaList);
}
for (int LoopY = 0; LoopY <= UB_Y - 1; LoopY++) {
if (IsBitTrue(pArrowArr[UB_X, LoopY] & Bit1)) { //内側の島かを判定
if (PushedUtigawa) { //内側の島をPush済の場合
if (UtigawaShimaList.Exists( //別の内側の島から到達不能ならNG
A => A.X == UB_X && A.Y == LoopY) == false) {
return false;
}
continue;
}
PushedUtigawa = true;
ExecUnionFind(UB_X, LoopY, pArrowArr, UtigawaShimaList);
}
else ExecUnionFind(UB_X, LoopY, pArrowArr, SotogawaShimaList);
}
for (int LoopX = UB_X; 1 <= LoopX; LoopX--) {
if (IsBitTrue(pArrowArr[LoopX, UB_Y] & Bit2)) { //内側の島かを判定
if (PushedUtigawa) { //内側の島をPush済の場合
if (UtigawaShimaList.Exists( //別の内側の島から到達不能ならNG
A => A.X == LoopX && A.Y == UB_Y) == false) {
return false;
}
continue;
}
PushedUtigawa = true;
ExecUnionFind(LoopX, UB_Y, pArrowArr, UtigawaShimaList);
}
else ExecUnionFind(LoopX, UB_Y, pArrowArr, SotogawaShimaList);
}
for (int LoopY = UB_Y; 1 <= LoopY; LoopY--) {
if (IsBitTrue(pArrowArr[0, LoopY] & Bit3)) { //内側の島かを判定
if (PushedUtigawa) { //内側の島をPush済の場合
if (UtigawaShimaList.Exists( //別の内側の島から到達不能ならNG
A => A.X == 0 && A.Y == LoopY) == false) {
return false;
}
continue;
}
PushedUtigawa = true;
ExecUnionFind(0, LoopY, pArrowArr, UtigawaShimaList);
}
else ExecUnionFind(0, LoopY, pArrowArr, SotogawaShimaList);
}
//内側の島と外側の島の座標をDistinctする
var ConcatedList = UtigawaShimaList.Concat(SotogawaShimaList).ToList();
for (int I = ConcatedList.Count - 1; 0 <= I; I--) {
for (int J = 0; J <= I - 1; J++) {
if (ConcatedList[I].X == ConcatedList[J].X
&& ConcatedList[I].Y == ConcatedList[J].Y) {
ConcatedList.RemoveAt(I);
}
}
}
//島の数 = 盤面かを判定
return ConcatedList.Count == (UB_X + 1) * (UB_Y + 1);
}
//クリア判定
static bool IsClear(int[,] pArrowArr, int[,] pBatuArr)
{
if (ClearHantei1(pArrowArr, pBatuArr) == false) return false;
if (ClearHantei2(pArrowArr) == false) return false;
if (ClearHantei3(pArrowArr, pBatuArr) == false) return false;
if (ClearHantei4(pArrowArr) == false) return false;
return true;
}
//クリア条件1 未確定ビットがないこと
static bool ClearHantei1(int[,] pArrowArr, int[,] pBatuArr)
{
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
Predicate<int> wkPred = pBit =>
{
if (IsBitFalse(pBatuArr[LoopX, LoopY] & pBit)
&& IsBitFalse(pArrowArr[LoopX, LoopY] & pBit)) return false;
return true;
};
if (wkPred(Bit0) == false) return false;
if (wkPred(Bit1) == false) return false;
if (wkPred(Bit2) == false) return false;
if (wkPred(Bit3) == false) return false;
}
}
return true;
}
//クリア条件2 全ての数値マスが線の数を満たしていること
static bool ClearHantei2(int[,] pArrowArr)
{
foreach (NumInfoDef EachNumInfo in NumInfoArr) {
int X = EachNumInfo.Point.X;
int Y = EachNumInfo.Point.Y;
int ArrowCnt = DeriveTargetPointBitCnt(X, Y, pArrowArr);
if (ArrowCnt != EachNumInfo.NumVal) return false;
}
return true;
}
//クリア条件3 切れた線がないこと
static bool ClearHantei3(int[,] pArrowArr, int[,] pBatuArr)
{
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
//線が切れてるかを判定
Predicate<List<RinsetuBitDef>> wkPred = pRinsetuBitList =>
pRinsetuBitList.TrueForAll(A => IsBatuPoint(new PointDef() { X = A.X, Y = A.Y },
A.Bit, pBatuArr));
//上の線が、切れてる場合
if (IsBitTrue(pArrowArr[LoopX, LoopY] & Bit0)) {
if (wkPred(RinsetuCase01_Bit_UeLine_Hidari(LoopX, LoopY))) return false;
if (wkPred(RinsetuCase02_Bit_UeLine_Migi(LoopX, LoopY))) return false;
}
//右の線が、切れてる場合
if (IsBitTrue(pArrowArr[LoopX, LoopY] & Bit1)) {
if (wkPred(RinsetuCase03_Bit_MigiLine_Ue(LoopX, LoopY))) return false;
if (wkPred(RinsetuCase04_Bit_MigiLine_Shita(LoopX, LoopY))) return false;
}
//下の線が、切れてる場合
if (IsBitTrue(pArrowArr[LoopX, LoopY] & Bit2)) {
if (wkPred(RinsetuCase05_Bit_ShitaLine_Hidari(LoopX, LoopY))) return false;
if (wkPred(RinsetuCase06_Bit_ShitaLine_Migi(LoopX, LoopY))) return false;
}
//左の線が、切れてる場合
if (IsBitTrue(pArrowArr[LoopX, LoopY] & Bit3)) {
if (wkPred(RinsetuCase07_Bit_HidariLine_Ue(LoopX, LoopY))) return false;
if (wkPred(RinsetuCase08_Bit_HidariLine_Shita(LoopX, LoopY))) return false;
}
}
}
return true;
}
//クリア条件4 内側になる島が1つしかないこと
static bool ClearHantei4(int[,] pArrowArr)
{
//内側の島の座標のList
AnswerUtigawaShimaList.Clear();
//外側の島の座標のList
var SotogawaShimaList = new List<PointDef>();
//外周の時計回りで、内側か外側かを判定しつつ、UnionFindの開始座標とする
bool PushedUtigawa = false;
for (int LoopX = 0; LoopX <= UB_X - 1; LoopX++) {
if (IsBitTrue(pArrowArr[LoopX, 0] & Bit0)) { //内側の島かを判定
if (PushedUtigawa) continue;
ExecUnionFind(LoopX, 0, pArrowArr, AnswerUtigawaShimaList);
PushedUtigawa = true;
}
else ExecUnionFind(LoopX, 0, pArrowArr, SotogawaShimaList);
}
for (int LoopY = 0; LoopY <= UB_Y - 1; LoopY++) {
if (IsBitTrue(pArrowArr[UB_X, LoopY] & Bit1)) { //内側の島かを判定
if (PushedUtigawa) continue;
ExecUnionFind(UB_X, LoopY, pArrowArr, AnswerUtigawaShimaList);
PushedUtigawa = true;
}
else ExecUnionFind(UB_X, LoopY, pArrowArr, SotogawaShimaList);
}
for (int LoopX = UB_X; 1 <= LoopX; LoopX--) {
if (IsBitTrue(pArrowArr[LoopX, UB_Y] & Bit2)) { //内側の島かを判定
if (PushedUtigawa) continue;
ExecUnionFind(LoopX, UB_Y, pArrowArr, AnswerUtigawaShimaList);
PushedUtigawa = true;
}
else ExecUnionFind(LoopX, UB_Y, pArrowArr, SotogawaShimaList);
}
for (int LoopY = UB_Y; 1 <= LoopY; LoopY--) {
if (IsBitTrue(pArrowArr[0, LoopY] & Bit3)) { //内側の島かを判定
if (PushedUtigawa) continue;
ExecUnionFind(0, LoopY, pArrowArr, AnswerUtigawaShimaList);
PushedUtigawa = true;
}
else ExecUnionFind(0, LoopY, pArrowArr, SotogawaShimaList);
}
//内側の島の数 + 外側の島の数 = 盤面
int ShimaCnt = AnswerUtigawaShimaList.Count + SotogawaShimaList.Count;
return ShimaCnt == (UB_X + 1) * (UB_Y + 1);
}
//島の座標のListを引数として、UnionFindで、線を越えずに移動可能な座標をAddする
static void ExecUnionFind(int pBaseX, int pBaseY, int[,] pArrowArr, List<PointDef> pShimaList)
{
var stk = new Stack<PointDef>();
PointDef WillPush;
Action<int, int> PushSyori = (pNewX, pNewY) =>
{
//訪問済なら再訪できない
if (pShimaList.Exists(A => A.X == pNewX && A.Y == pNewY))
return;
pShimaList.Add(new PointDef() { X = pNewX, Y = pNewY });
WillPush.X = pNewX;
WillPush.Y = pNewY;
stk.Push(WillPush);
};
PushSyori(pBaseX, pBaseY);
while (stk.Count > 0) {
PointDef Popped = stk.Pop();
//左に移動
if (Popped.X > 0 && IsBitFalse(pArrowArr[Popped.X, Popped.Y] & Bit3))
PushSyori(Popped.X - 1, Popped.Y);
//右に移動
if (Popped.X < UB_X && IsBitFalse(pArrowArr[Popped.X, Popped.Y] & Bit1))
PushSyori(Popped.X + 1, Popped.Y);
//上に移動
if (Popped.Y > 0 && IsBitFalse(pArrowArr[Popped.X, Popped.Y] & Bit0))
PushSyori(Popped.X, Popped.Y - 1);
//下に移動
if (Popped.Y < UB_Y && IsBitFalse(pArrowArr[Popped.X, Popped.Y] & Bit2))
PushSyori(Popped.X, Popped.Y + 1);
}
}
//解を出力
static void PrintAnswer()
{
//半角数字を全角数字に変換
Func<int, char> SingleToWideFunc = (pStr) => (char)('0' + pStr);
var sb = new System.Text.StringBuilder();
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
//内側の島の場合
int wkInd1 = AnswerUtigawaShimaList.FindIndex(A => A.X == LoopX && A.Y == LoopY);
if (wkInd1 >= 0) {
sb.Append('■');
continue;
}
//数値マスの場合
int wkInd2 = Array.FindIndex(NumInfoArr, A => A.Point.X == LoopX && A.Point.Y == LoopY);
if (wkInd2 >= 0) {
sb.Append(SingleToWideFunc(NumInfoArr[wkInd2].NumVal));
continue;
}
sb.Append('□');
}
sb.AppendLine();
}
Console.WriteLine(sb.ToString());
}
}
実行結果
確定探索だけで解を発見
□0□1■■■■
■3■□■3■2
■■■■■□□0
□3■■■■■□
■■■3■■■■
■■□□■3■■
■□1■■□3■
□0□□■□■■
追加問題02 ニコリビヨリのサンプル問題
char[,] Q02Arr = {{' ',' ',' ','3','3','2'},
{'3','0',' ',' ',' ','3'},
{' ',' ','2',' ',' ','2'},
{'0',' ',' ','2',' ',' '},
{'1',' ',' ',' ','3','0'},
{'0','2','2',' ',' ',' '}};
追加問題03 ニコリビヨリの問題
char[,] Q03Arr = {{' ','0',' ',' ','3','1',' ',' ',' ',' '},
{' ','3',' ',' ',' ','3',' ','2','3','3'},
{' ','2','2','3',' ',' ',' ','3',' ',' '},
{' ',' ',' ',' ','1',' ',' ','2',' ',' '},
{'0','1',' ',' ','2','3','1',' ',' ','3'},
{'2',' ',' ','3','1','3',' ',' ','0','3'},
{' ',' ','0',' ',' ','2',' ',' ',' ',' '},
{' ',' ','2',' ',' ',' ','2','0','1',' '},
{'1','3','0',' ','1',' ',' ',' ','3',' '},
{' ',' ',' ',' ','0','1',' ',' ','0',' '}};
追加問題04 ペンシルパズル入門の例題
char[,] Q04Arr = {{' ','3',' ',' ','3'},
{'2','0',' ','3','2'},
{' ',' ','1',' ',' '},
{'2','3',' ','3','2'},
{'0',' ',' ','3',' '}};
追加問題05 ペンシルパズル入門の1問目
char[,] Q05Arr = {{'2',' ',' ','2','0'},
{' ','3','2',' ',' '},
{'2',' ',' ',' ','2'},
{' ',' ','0','3',' '},
{'2','3',' ',' ','2'}};
追加問題06 ペンシルパズル入門の2問目
char[,] Q06Arr = {{' ','2','0','2',' '},
{'1',' ',' ',' ','2'},
{'1',' ','1',' ','3'},
{'3',' ',' ',' ','3'},
{' ','0','2','3',' '}};
追加問題07 ペンシルパズル入門の3問目
char[,] Q07Arr = {{'3','3',' ',' ','0'},
{' ',' ','1',' ','2'},
{' ','2',' ','1',' '},
{'2',' ','3',' ',' '},
{'3',' ',' ','3','1'}};
追加問題08 ペンシルパズル入門の4問目
char[,] Q08Arr = {{' ','3','1',' ','2','3',' '},
{'0',' ',' ','3',' ',' ','1'},
{'2',' ','2',' ','3',' ','3'},
{' ','1','3','0','2','1',' '},
{'3',' ','3',' ','2',' ','1'},
{'3',' ',' ','2',' ',' ','0'},
{' ','2','2',' ','1','3',' '}};
追加問題09 ペンシルパズル入門の5問目
char[,] Q09Arr = {{'3',' ','1','2','3',' ','3'},
{' ','2',' ',' ',' ','2',' '},
{'2',' ',' ','0',' ',' ','1'},
{'0',' ','3',' ','3',' ','3'},
{'2',' ',' ','1',' ',' ','1'},
{' ','2',' ',' ',' ','3',' '},
{'1',' ','1','3','2',' ','0'}};
追加問題10 ペンシルパズル入門の6問目
char[,] Q10Arr = {{' ','2',' ',' ','2','3',' '},
{'3',' ','3',' ','0',' ','1'},
{'1',' ','3',' ',' ',' ',' '},
{' ','0',' ','1',' ','1',' '},
{' ',' ',' ',' ','2',' ','0'},
{'3',' ','2',' ','3',' ','2'},
{' ','3','3',' ',' ','1',' '}};
追加問題11 ペンシルパズル入門の7問目
char[,] Q11Arr = {{' ','1','3',' ','0','2',' '},
{'0',' ',' ','3',' ',' ','2'},
{'2',' ',' ','3',' ',' ','3'},
{' ','1','0',' ','2','2',' '},
{'3',' ',' ','3',' ',' ','2'},
{'1',' ',' ','2',' ',' ','1'},
{' ','0','2',' ','2','1',' '}};
追加問題12 ペンシルパズル入門の8問目
char[,] Q12Arr = {{'1','2',' ',' ','0',' ',' ','3',' ','3'},
{' ',' ','3',' ',' ','1','3',' ',' ','1'},
{'2',' ',' ','3',' ',' ',' ',' ','0',' '},
{' ','3',' ',' ','0','3',' ','3',' ',' '},
{' ','0',' ','3',' ',' ','2',' ',' ','3'},
{'3',' ',' ','3',' ',' ','3',' ','1',' '},
{' ',' ','2',' ','2','2',' ',' ','3',' '},
{' ','2',' ',' ',' ',' ','1',' ',' ','3'},
{'0',' ',' ','3','1',' ',' ','3',' ',' '},
{'1',' ','1',' ',' ','0',' ',' ','1','0'}};
追加問題13 パズルBox12の1問目
char[,] Q13Arr = {{'0','2',' ',' ',' ','3',' ',' ',' ',' '},
{' ',' ',' ','3',' ','2',' ','3','0','3'},
{'3',' ','3','0',' ','0',' ',' ',' ',' '},
{'3',' ',' ',' ',' ',' ','2',' ','0',' '},
{' ',' ','3',' ','1',' ',' ',' ','3',' '},
{' ','1',' ',' ',' ','3',' ','0',' ',' '},
{' ','3',' ','3',' ',' ',' ',' ',' ','3'},
{' ',' ',' ',' ','3',' ','2','3',' ','3'},
{'0','3','2',' ','3',' ','0',' ',' ',' '},
{' ',' ',' ',' ','1',' ',' ',' ','3','3'}};
追加問題14 パズルBox12の5問目
char[,] Q14Arr = {{' ','3','3',' ','3',' ',' ',' ','2',' '},
{'3',' ',' ',' ',' ',' ','1',' ',' ','2'},
{' ',' ','3','3',' ',' ',' ','3',' ','0'},
{' ','3',' ',' ',' ','3',' ','2',' ',' '},
{' ',' ',' ','3',' ',' ',' ',' ',' ','3'},
{'0',' ',' ',' ',' ',' ','3',' ',' ',' '},
{' ',' ','3',' ','3',' ',' ',' ','0',' '},
{'2',' ','0',' ',' ',' ','3','3',' ',' '},
{'3',' ',' ','2',' ',' ',' ',' ',' ','3'},
{' ','0',' ',' ',' ','1',' ','3','3',' '}};
追加問題15 パズルBox10の07問目
char[,] Q01Arr =
{{' ','2','0',' ',' ',' ',' ',' ',' ',' ',' ','3','2','1',' ',' ',' ',' ',' ',' ',' ',' ','2','0',' '},
{'2',' ',' ','1',' ',' ','1','1',' ',' ','1',' ',' ',' ','3',' ',' ','3','0',' ',' ','2',' ',' ','3'},
{'3',' ',' ','1',' ','3',' ',' ','0',' ','1',' ',' ',' ','1',' ','0',' ',' ','2',' ','1',' ',' ','2'},
{' ','2','1',' ',' ','2',' ',' ','2',' ','3',' ',' ',' ','2',' ','2',' ',' ','1',' ',' ','3','1',' '},
{' ',' ',' ',' ',' ',' ','1','1',' ',' ',' ','2','3','0',' ',' ',' ','2','3',' ',' ',' ',' ',' ',' '},
{' ',' ','0','3','2',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','1','2','0',' ',' '},
{' ','3',' ',' ',' ','2',' ',' ','3',' ',' ','1',' ','2',' ',' ','0',' ',' ','0',' ',' ',' ','3',' '},
{' ','2',' ',' ',' ','1',' ',' ','2',' ',' ','1',' ','2',' ',' ','1',' ',' ','2',' ',' ',' ','2',' '},
{' ','1',' ',' ',' ','0',' ',' ','0',' ',' ','3',' ','1',' ',' ','0',' ',' ','0',' ',' ',' ','1',' '},
{' ',' ','1','1','2',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','3','3','1',' ',' '},
{' ',' ',' ',' ',' ',' ','1','0',' ',' ',' ','0','2','0',' ',' ',' ','3','3',' ',' ',' ',' ',' ',' '},
{' ','2','2',' ',' ','1',' ',' ','2',' ','2',' ',' ',' ','1',' ','2',' ',' ','0',' ',' ','3','3',' '},
{'2',' ',' ','2',' ','0',' ',' ','1',' ','3',' ',' ',' ','0',' ','1',' ',' ','3',' ','1',' ',' ','1'},
{'0',' ',' ','0',' ',' ','1','3',' ',' ','3',' ',' ',' ','1',' ',' ','1','1',' ',' ','2',' ',' ','3'},
{' ','3','1',' ',' ',' ',' ',' ',' ',' ',' ','2','1','2',' ',' ',' ',' ',' ',' ',' ',' ','2','1',' '}};
検証用のWindowsフォームアプリのソースと実行結果
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace SlitherLinkGUI
{
public partial class Form1 : Form
{
static List<int[]> HaitiArrowIntArrList;
static List<int[]> HaitiBatuIntArrList;
const int MasuHaba = 35;
//const int MasuHaba = 50;
static int UB_X;
static int UB_Y;
static char[,] Q01Arr =
{{' ','2','0',' ',' ',' ',' ',' ',' ',' ',' ','3','2','1',' ',' ',' ',' ',' ',' ',' ',' ','2','0',' '},
{'2',' ',' ','1',' ',' ','1','1',' ',' ','1',' ',' ',' ','3',' ',' ','3','0',' ',' ','2',' ',' ','3'},
{'3',' ',' ','1',' ','3',' ',' ','0',' ','1',' ',' ',' ','1',' ','0',' ',' ','2',' ','1',' ',' ','2'},
{' ','2','1',' ',' ','2',' ',' ','2',' ','3',' ',' ',' ','2',' ','2',' ',' ','1',' ',' ','3','1',' '},
{' ',' ',' ',' ',' ',' ','1','1',' ',' ',' ','2','3','0',' ',' ',' ','2','3',' ',' ',' ',' ',' ',' '},
{' ',' ','0','3','2',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','1','2','0',' ',' '},
{' ','3',' ',' ',' ','2',' ',' ','3',' ',' ','1',' ','2',' ',' ','0',' ',' ','0',' ',' ',' ','3',' '},
{' ','2',' ',' ',' ','1',' ',' ','2',' ',' ','1',' ','2',' ',' ','1',' ',' ','2',' ',' ',' ','2',' '},
{' ','1',' ',' ',' ','0',' ',' ','0',' ',' ','3',' ','1',' ',' ','0',' ',' ','0',' ',' ',' ','1',' '},
{' ',' ','1','1','2',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','3','3','1',' ',' '},
{' ',' ',' ',' ',' ',' ','1','0',' ',' ',' ','0','2','0',' ',' ',' ','3','3',' ',' ',' ',' ',' ',' '},
{' ','2','2',' ',' ','1',' ',' ','2',' ','2',' ',' ',' ','1',' ','2',' ',' ','0',' ',' ','3','3',' '},
{'2',' ',' ','2',' ','0',' ',' ','1',' ','3',' ',' ',' ','0',' ','1',' ',' ','3',' ','1',' ',' ','1'},
{'0',' ',' ','0',' ',' ','1','3',' ',' ','3',' ',' ',' ','1',' ',' ','1','1',' ',' ','2',' ',' ','3'},
{' ','3','1',' ',' ',' ',' ',' ',' ',' ',' ','2','1','2',' ',' ',' ',' ',' ',' ',' ',' ','2','1',' '}};
static char[,] QuestionArr = Q01Arr;
//X座標とY座標の入れ替え
static char[,] XYRev(char[,] pBaseArr)
{
int RevArrUB_X = pBaseArr.GetUpperBound(1);
int RevArrUB_Y = pBaseArr.GetUpperBound(0);
char[,] WillReturnArr = new char[RevArrUB_X + 1, RevArrUB_Y + 1];
for (int X = 0; X <= RevArrUB_X; X++) {
for (int Y = 0; Y <= RevArrUB_Y; Y++) {
WillReturnArr[X, Y] = pBaseArr[Y, X];
}
}
return WillReturnArr;
}
public Form1()
{
QuestionArr = XYRev(QuestionArr);
UB_X = QuestionArr.GetUpperBound(0);
UB_Y = QuestionArr.GetUpperBound(1);
var wkArrowHaitiList = new List<string>();
wkArrowHaitiList.Add("BC00000000000000069153C04");
wkArrowHaitiList.Add("C3828000000000002D02BC7AB");
wkArrowHaitiList.Add("7A8280040040000003800156A");
wkArrowHaitiList.Add("96840003C2906800000002D12");
wkArrowHaitiList.Add("A92D0000102AD0000000283C2");
wkArrowHaitiList.Add("AC07A8000000380000002C07A");
wkArrowHaitiList.Add("87A900000000000402C047AD2");
wkArrowHaitiList.Add("A928000000040007807AD503A");
wkArrowHaitiList.Add("A82AC0000029000102D05382A");
wkArrowHaitiList.Add("A8283C000002E800043ABE82A");
wkArrowHaitiList.Add("AC2C47802A8050006BEAC142A");
wkArrowHaitiList.Add("C3C511046AC2B82C54507ABEA");
wkArrowHaitiList.Add("3C13C02D107AA801113E92852");
wkArrowHaitiList.Add("07803C4782D6AC44444546ABE");
wkArrowHaitiList.Add("2D44455544554555555555681");
var wkBatuHaitiList = new List<string>();
wkBatuHaitiList.Add("43FD000000000000796EAC3FB");
wkBatuHaitiList.Add("3C7D4006C040000052F943854");
wkBatuHaitiList.Add("857D1003FEB80002F8103EA95");
wkBatuHaitiList.Add("69790004390684001000052ED");
wkBatuHaitiList.Add("56D2C00380052F80000047C3D");
wkBatuHaitiList.Add("53F85000000381004006D3F85");
wkBatuHaitiList.Add("7856900000040002FC2FB852D");
wkBatuHaitiList.Add("56D50400406B84287F852AFC5");
wkBatuHaitiList.Add("57D52FC2F8104382FD2FAC7D5");
wkBatuHaitiList.Add("57D7C3FC140414007BC5417D5");
wkBatuHaitiList.Add("53D3B87FC56FAFC014153EBD5");
wkBatuHaitiList.Add("3C3AEEFB953D47D2ABAF85415");
wkBatuHaitiList.Add("C3EC3FD2EF8557FEEEC16D7AD");
wkBatuHaitiList.Add("F87FC3B87D2953BBBBBAB9541");
wkBatuHaitiList.Add("D2BBBAAABBAABAAAAAAAAA97E");
HaitiArrowIntArrList = DeriveConvertedIntArrList(wkArrowHaitiList);
HaitiBatuIntArrList = DeriveConvertedIntArrList(wkBatuHaitiList);
InitializeComponent();
}
//大文字の16進数の文字列をInt型の配列に変換
static List<int[]> DeriveConvertedIntArrList(List<string> wkStringList)
{
//全角16進数を数値に変換
Func<char, int> WideHexToIntFunc = (pChar) =>
{
if ('0' <= pChar && pChar <= '9')
return pChar - '0';
return pChar - 'A' + 10;
};
var WillReturnList = new List<int[]>();
foreach (string EachStr in wkStringList) {
int[] wkIntArr = new int[EachStr.Length];
for (int I = 0; I <= wkIntArr.GetUpperBound(0); I++) {
wkIntArr[I] = WideHexToIntFunc(EachStr[I]);
}
WillReturnList.Add(wkIntArr);
}
return WillReturnList;
}
private void Form1_Load(object sender, System.EventArgs e)
{
Bitmap Canvas = new Bitmap(PictureBox1.Width, PictureBox1.Height);
using (Graphics g = Graphics.FromImage(Canvas)) {
for (int X = 0; X <= UB_X; X++) {
for (int Y = 0; Y <= UB_Y; Y++) {
int BaseX = MasuHaba * X;
int BaseY = MasuHaba * Y;
g.DrawLine(Pens.Black, BaseX, BaseY,
BaseX + MasuHaba, BaseY);
g.DrawLine(Pens.Black, BaseX, BaseY,
BaseX, BaseY + MasuHaba);
g.DrawLine(Pens.Black, BaseX + MasuHaba, BaseY,
BaseX + MasuHaba, BaseY + MasuHaba);
g.DrawLine(Pens.Black, BaseX, BaseY + MasuHaba,
BaseX + MasuHaba, BaseY + MasuHaba);
}
}
//数値を描画
PaintNum(g);
//線を描画
PaintArror(g);
//バツを描画
PaintBatu(g);
}
PictureBox1.Image = Canvas;
}
//数値を描画
static void PaintNum(Graphics pGraphics)
{
//半角数字を全角数字に変換
Func<char, char> SingleToWideFunc = (pStr) => (char)('0' + pStr - '0');
for (int LoopX = 0; LoopX <= UB_X; LoopX++) {
for (int LoopY = 0; LoopY <= UB_Y; LoopY++) {
if (QuestionArr[LoopX, LoopY] == ' ')
continue;
int BaseX = MasuHaba * LoopX;
int BaseY = MasuHaba * LoopY;
Font fnt = new Font("MS ゴシック", 12);
Point wkPoint = new Point() { X = BaseX + MasuHaba / 3, Y = BaseY + MasuHaba / 3 };
char wkChar = SingleToWideFunc(QuestionArr[LoopX, LoopY]);
pGraphics.DrawString(wkChar.ToString(), fnt, Brushes.Black, wkPoint);
}
}
}
//線を描画
static void PaintArror(Graphics pGraphics)
{
for (int Y = 0; Y <= HaitiArrowIntArrList.Count - 1; Y++) {
int[] CurrIntArr = HaitiArrowIntArrList[Y];
for (int X = 0; X <= CurrIntArr.GetUpperBound(0); X++) {
int CurrInt = CurrIntArr[X];
int BaseX = MasuHaba * X;
int BaseY = MasuHaba * Y;
var wkPen = new Pen(Color.Blue, 5);
if (IsBitTrue(CurrInt & Bit0)) {
pGraphics.DrawLine(wkPen, BaseX, BaseY,
BaseX + MasuHaba, BaseY);
}
if (IsBitTrue(CurrInt & Bit1)) {
pGraphics.DrawLine(wkPen, BaseX + MasuHaba, BaseY,
BaseX + MasuHaba, BaseY + MasuHaba);
}
if (IsBitTrue(CurrInt & Bit2)) {
pGraphics.DrawLine(wkPen, BaseX, BaseY + MasuHaba,
BaseX + MasuHaba, BaseY + MasuHaba);
}
if (IsBitTrue(CurrInt & Bit3)) {
pGraphics.DrawLine(wkPen, BaseX, BaseY,
BaseX, BaseY + MasuHaba);
}
wkPen.Dispose();
}
}
}
//4方向のビット定義
const int Bit0 = 1; //上方向
const int Bit1 = 2; //右方向
const int Bit2 = 4; //下方向
const int Bit3 = 8; //左方向
//ビット演算の結果が0より大きいかを返す
static bool IsBitTrue(int pInt)
{
return pInt > 0;
}
//バツを描画
static void PaintBatu(Graphics pGraphics)
{
for (int Y = 0; Y <= HaitiBatuIntArrList.Count - 1; Y++) {
int[] CurrIntArr = HaitiBatuIntArrList[Y];
for (int X = 0; X <= CurrIntArr.GetUpperBound(0); X++) {
int CurrInt = CurrIntArr[X];
int BaseX = MasuHaba * X;
int BaseY = MasuHaba * Y;
var wkPen = new Pen(Color.Red, 5);
if (IsBitTrue(CurrInt & Bit0)) {
pGraphics.DrawLine(wkPen, BaseX + MasuHaba / 3, BaseY,
BaseX + MasuHaba / 3 * 2, BaseY);
}
if (IsBitTrue(CurrInt & Bit1)) {
pGraphics.DrawLine(wkPen, BaseX + MasuHaba, BaseY + MasuHaba / 3,
BaseX + MasuHaba, BaseY + MasuHaba / 3 * 2);
}
if (IsBitTrue(CurrInt & Bit2)) {
pGraphics.DrawLine(wkPen, BaseX + MasuHaba / 3, BaseY + MasuHaba,
BaseX + MasuHaba / 3 * 2, BaseY + MasuHaba);
}
if (IsBitTrue(CurrInt & Bit3)) {
pGraphics.DrawLine(wkPen, BaseX, BaseY + MasuHaba / 3,
BaseX, BaseY + MasuHaba / 3 * 2);
}
wkPen.Dispose();
}
}
}
}
}
実行結果
解説
16進数で、座標の周りの線もしくはバツ印の管理をしてます。