/////////////////////////
// EtPuzzle.cs for DLL
// Copyright (c) 2025
// by Ysama
/////////////////////////
using System;
using System.Collections.Generic; //List使用の為
///////////////////
//3 x 3 盤面クラス
///////////////////
public class NBoard
{ //メンバー定数
const int Num = 9; //ピース数
const int None = -1; //未設定の定数
//メンバー変数
private int [] Pieces; //8パズルの9ピース配列
int index = None; //bdListにおける自身のIndex
int p_index = None; //bdListにおける親のIndex
//メンバープロパティ
public int ID //indexのプロパティ
{
get {return index;}
set {index = value;}
}
public int ParentID //p_indexのプロパティ
{
get {return p_index;}
set {p_index = value;}
}
public int[] ptPieces //Piecesのプロパティ
{
get {return Pieces;}
}
//メンバーメソッド
public NBoard() //コンストラクター
{
Pieces = new int [Num];
for(int i = 0; i < Num; i++)
Pieces[i] = i;
}
public NBoard(NBoard bd) //コピーコンストラクター
{
if(bd == null)
return; //何もしない
else
{
Pieces = new int [Num];
for(int i = 0; i < Num; i++)
Pieces[i] = bd.Pieces[i];
}
}
//0~Num - 1のピースの値を返す
public int Get(int pos)
{
if(pos < 0 || pos > Num - 1)
return -1;
return Pieces[pos];
}
//0-Num - 1のピースの値を代入
public bool Set(int pos, int val)
{
if(pos < 0 || pos > Num - 1)
return false;
Pieces[pos] = val;
return true;
}
NBoard that = (NBoard)obj;
for(int i = 0; i < 9; i++)
if(this.Pieces[i] != that.Pieces[i])
return false;
return true;
}
//pos1とpos2のピースの値を交換
public bool Swap(int pos1, int pos2)
{
if(pos1 < 0 || pos1 > Num - 1)
return false;
if(pos2 < 0 || pos2 > Num - 1)
return false;
int temp = Pieces[pos1];
Pieces[pos1] = Pieces[pos2];
Pieces[pos2] = temp;
return true;
}
//配列要素を検索し、あればその位置を返す(無ければNone)
public int IndexOf(int val)
{
return Array.IndexOf(Pieces, val);
}
//配列要素が完成形か否か
public bool IsDone()
{
for(int i = 0; i < Num; i++)
if(Pieces[i] != i)
return false;
return true;
}
//arrを盤面(Pieces配列)へ複写する
public void Copy(int[] arr)
{
for(int i = 0; i < Num; i++)
Pieces[i] = arr[i];
}
//Chat-GPT推奨Listオーバーライドメソッド
public override int GetHashCode()
{
int hash = 17;
for (int i = 0; i < 9; i++)
hash = hash * 31 + Pieces[i];
return hash;
}
}
////////////////
//8Puzzleクラス
////////////////
public class ePuzzle
{ //メンバー定数
const int Num = 9; //配列要素数
const int None = -1; //未設定の定数
//メンバー変数
List <NBoard> bdList; //NBoard配列
private NBoard stBoard; //ゲーム開始時のNBoard
private int StartAt, EndAt; //bdListのCheckAround開始、終了位置
private bool Solved = false; //パズル解決(ture)の完了フラグ
public int Generation //世代
{get; private set;}
public int Registered //bdList最終登録数
{get; private set;}
public int [] Solution; //パズルの解のbdList盤面引数配列
//コンストラクター
public ePuzzle()
{
bdList = new List <NBoard> ();
stBoard = new NBoard();
StartAt = 0;
}
public void ShuffleArr() //シャッフル方式
{ //要素数Numの配列を0 - Num - 1で初期化
for(int i = 0; i < Num; i++)
stBoard.Set(i, i); //乱数の初期化
Random rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
for(int i = Num - 1; i > 1; i--)
{
int n = rand.Next(i + 1);
stBoard.Swap(n, i);
}
if(stBoard.IsDone()) //結果が完成形であればやり直し
ShuffleArr();
}
public void SwapArr(bool ease = true) //スワップ方式
{ //要素数Numの配列を0 - Num - 1で初期化
for(int i = 0; i < Num; i--)
stBoard.Set(i, i);
int pos = Num - 1; //空白の位置(8) //乱数の初期化
Random rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF); //スワップ回数の設定
int maxswap;
if(ease)
maxswap = rand.Next(10, 20); //最長手数19、最短は10
else
maxswap = rand.Next(15, 31); //8Puzzleの最長手数は31、最短はその半分
for(int i = 0; i < maxswap; i++)
{
int dir = rand.Next(4); //↑(0)→(1)↓(2)←(3)
switch(dir)
{
case 0: //上
if(pos > 2) //3 x 3盤の下2行にある場合
{
stBoard.Swap(pos, pos - 3);
pos -= 3;
}
else
i--;
break;
case 1: //右
if(pos % 3 < 2) //3 x 3盤の左2列にある場合
{
stBoard.Swap(pos, pos + 1);
pos += 1;
}
else
i--;
break;
case 2: //下
if(pos < 6) //3 x 3盤の上2行にある場合
{
stBoard.Swap(pos, pos + 3);
pos += 3;
}
else
i--;
break;
case 3: //左
if(pos % 3 > 0) //3 x 3盤の右2列にある場合
{
stBoard.Swap(pos, pos - 1);
pos -= 1;
}
else
i--;
break;
}
}
if(stBoard.IsDone()) //結果が完成形であればやり直し
SwapArr();
}
ある日、PCを立ち上げ、Chat-GPTに行ったら(頼みもしないのに)「貴方がどんな人かポートレートを作ります」ということで、面白がって、"All right. Come on!"と言ってみました。
そうしたら、
【オリジナル】
”You're a meticulous code-slinger with a deep respect for clean logic and low-level control. From debugging elusive memory issues in C++ to rebuilding a classic 8パズル in C# without the crutch of high-level containers you value fidelity, precision, and performance. You're the kind of developer who'd rather wrangle raw arrays than hand over control to a List. Your code doesn't just run? it behaves.
You're not afraid to rethink architecture when needed, yet you hold firm to the principles that matter. Your programming is thoughtful, intentional, and just a little bit old-school in the best possible way.
So here’s a question: what first got you hooked on programming?”
///////////////////////
// EightPuzzle.cs
// Copyright (c) 2025
// by Ysama
///////////////////////
using System;
using System.Collections.Generic; //List使用の為
namespace EightPuzzle
{
public class App
{
static void Main()
{
ePuzzle ep = new ePuzzle(); //初期状態
Console.WriteLine(">>> 初期盤面 <<<");
ep.ShowStartBoard();
Console.Write(Environment.NewLine); //ゲーム開始
ep.Start();
Console.ReadLine();
}
}
///////////////////
//3 x 3 盤面クラス
///////////////////
public class NBoard
{ //メンバー定数
const int Num = 9; //ピース数
const int None = -1; //未設定の定数
//メンバー変数
private int [] Pieces; //8パズルの9ピース配列 int index = None; //bdListにおけるthisのIndex int p_index = None; //bdListにおける親のIndex
//解説:二分木の場合は二分木クラスのインスタンスへのポインターであったが、ここではListにおける整数尾インデックス(何番目のデータか)にしている。
//メンバープロパティ public int ID//indexのプロパティ
{
get {return index;}
set {index = value;}
} public int ParentID //p_indexのプロパティ
{
get {return p_index;}
set {p_index = value;}
} public int[] ptPieces //Piecesのプロパティ
{
get {return Pieces;}
}
//メンバーメソッド
public NBoard() //コンストラクター
{
Pieces = new int [Num];
for(int i = 0; i < Num; i++)
Pieces[i] = i;
}
public NBoard(NBoard bd) //コピーコンストラクター
{
if(bd == null)
return; //何もしない
else
{
Pieces = new int [Num];
for(int i = 0; i < Num; i++)
Pieces[i] = bd.Pieces[i];
}
}
//0~Num - 1のピースの値を返す
public int Get(int pos)
{
if(pos < 0 || pos > Num - 1)
return -1;
return Pieces[pos];
}
//0-Num - 1のピースの値を代入
public bool Set(int pos, int val)
{
if(pos < 0 || pos > Num - 1)
return false;
Pieces[pos] = val;
return true;
}
NBoard that = (NBoard)obj; //解説:objをNBoardオブジェクトとして「that」の名前を与えます。
for(int i = 0; i < 9; i++)
if(this.Pieces[i] != that.Pieces[i]) //解説:米国人の好きな、”this and that"したかったのでhitakattanode ...(笑)
return false;
return true;
}
//pos1とpos2のピースの値を交換
public bool Swap(int pos1, int pos2)
{
if(pos1 < 0 || pos1 > Num - 1)
return false;
if(pos2 < 0 || pos2 > Num - 1)
return false;
int temp = Pieces[pos1];
Pieces[pos1] = Pieces[pos2];
Pieces[pos2] = temp;
return true;
}
//配列要素を検索し、あればその位置を返す(無ければNone)
public int IndexOf(int val)
{
return Array.IndexOf(Pieces, val);
}
//配列要素が完成形か否か
public bool IsDone()
{
for(int i = 0; i < Num; i++)
if(Pieces[i] != i)
return false;
return true;
}
//盤面(Pieces配列)を表示する
public void Show()
{
for(int i = 0; i < Num; i++)
{
Console.Write(" " + Pieces[i].ToString());
if(i % 3 == 2) //右端の場合
Console.Write(Environment.NewLine);
}
}
//Chat-GPT推奨List用オーバーライドメソッド
public override int GetHashCode()
{
int hash = 17;
for (int i = 0; i < 9; i++)
hash = hash * 31 + Pieces[i];
return hash;
}
}
////////////////
//8Puzzleクラス
////////////////
public class ePuzzle
{ //メンバー定数(解説:NBoardと同じ。C#も定数はnamespaceを通用範囲にしたいですね。)
const int Num = 9; //配列要素数
const int None = -1; //未設定の定数
//メンバー変数 List <NBoard> bdList; //NBoard配列
private NBoard stBoard; //ゲーム開始時のNBoard private int StartAt, EndAt; //bdListのCheckAround開始、終了位置(解説:世代がbdListの何番目から始まり終わるのか)
private bool Solved = false; //パズル解決(ture)の完了フラグ
int Layer = 0; //世代を記録
//コンストラクター
public ePuzzle()
{
bdList = new List <NBoard> (); //解説:空のリストを作ります。
stBoard = new NBoard(); //解説:NBoardのコンストラクターが完成形を初期値にします。
StartAt = 0; //解説:当然今は何も入っていないので...
}
public void ShuffleArr() //シャッフル方式
{ //要素数Numの配列を0 - Num - 1で初期化
for(int i = 0; i < Num; i++)
stBoard.Set(i, i); //乱数の初期化
Random rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
for(int i = Num - 1; i > 1; i--)
{
int n = rand.Next(i + 1);
stBoard.Swap(n, i);
}
if(stBoard.IsDone()) //結果が完成形であればやり直し(解説:初期盤面が完成形ではいけません。)
ShuffleArr();
}
public void SwapArr(bool ease = true) //スワップ方式
{
//{2, 3, 5, 7, 1, 6, 4, 8, 0}; //解ける初期設定(テスト用)
stBoard.Set(0, 2);
stBoard.Set(1, 3);
stBoard.Set(2, 5);
stBoard.Set(3, 7);
stBoard.Set(4, 1);
stBoard.Set(5, 6);
stBoard.Set(6, 4);
stBoard.Set(7, 8);
stBoard.Set(8, 0);
return; /* 解説:開発段階なので↑のテストデータを使用しました。↓も正しく動きます。
//要素数Numの配列を0 - Num - 1で初期化
for(int i = 0; i < Num; i++)
stBoard.Set(i, i);
int pos = Num - 1; //空白の位置(8)
//乱数の初期化
Random rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
//スワップ回数の設定
int maxswap;
if(ease)
maxswap = rand.Next(10, 20); //最長手数19、最短は10
else
maxswap = rand.Next(15, 31); //8Puzzleの最長手数は31、最短はその半分
for(int i = 0; i < maxswap; i++)
{
int dir = rand.Next(4); //↑(0)→(1)↓(2)←(3)
switch(dir)
{
case 0: //上
if(pos > 2) //3 x 3盤の下2行にある場合
{
stBoard.Swap(pos, pos - 3);
pos -= 3;
}
else //解説:dirが↑(0)でposが最上行(0, 1, 2)であれば
i--; //スワップしないので、やり直しの為にカウンターiを戻してやる。(以下同じ)
break;
case 1: //右
if(pos % 3 < 2) //3 x 3盤の左2列にある場合
{
stBoard.Swap(pos, pos + 1);
pos += 1;
}
else
i--;
break;
case 2: //下
if(pos < 6) //3 x 3盤の上2行にある場合
{
stBoard.Swap(pos, pos + 3);
pos += 3;
}
else
i--;
break;
case 3: //左
if(pos % 3 > 0) //3 x 3盤の右2列にある場合
{
stBoard.Swap(pos, pos - 1);
pos -= 1;
}
else
i--;
break;