//////////////////////////////////////////////////////////////////
// TowerOfHanoi.cs
// Tower of Hanoi, a mathimatic game
// https://www.programmingalgorithms.com/algorithm/tower-of-hanoi/
// 使用上の注意：最大の盤数はPoleクラスで10に設定されています。
//				これを変更する場合には、ShowHanoi()メソッドの60を
//				最大盤数 x 6に、MakeDisk()メソッドの10を最大盤数
//				に変更してください。
//				また、ShowHanoi()メソッドで 0.1秒のウェィトを置い
//				ています（Thread.Sleep(100)）が変更可能です。
//////////////////////////////////////////////////////////////////

using System;
using System.Threading;

class TowerOfHanoi
{
	//「TowerOfHanoi」クラス共通変数の宣言
	public static readonly string[] disk = {"|", "□", "□□", "□□□", "□□□□", "□□□□□", "□□□□□□", "□□□□□□□", "□□□□□□□□", "□□□□□□□□□", "□□□□□□□□□□"};
	public static Pole[] pole = new Pole[3];
	public static int tire = 0;

	//Main関数
	public static void Main()
	{
		//段数のユーザー設定
		Console.Write("円盤柱の段数（整数）を入力してください：");
		while(!int.TryParse(Console.ReadLine(), out tire) || tire <= 0)
		{
			Console.Write("\n円盤柱の段数（整数）を入力してください：");
		}
		//3本の柱のインスタンス作成
		for(int i = 0; i < 3; i++)
			pole[i] = new Pole();
		//最初の柱に円盤を設置する
		for(int i = 0; i < tire; i++)
			pole[0].push(tire - i);
		//「ハノイの塔」の初期状態を表示
		ShowHanoi();
		Console.WriteLine("開始します。何かキーを入押してください：");
		Console.ReadKey();
		//「ハノイの塔」の開始
		TowerofHanoi(tire, 0, 1, 2);
		//コンソールを閉じさせない為
		Console.WriteLine("終了しました。何かキーを入押してください：");
		Console.ReadKey();
	}

	public static void TowerofHanoi(int diskCount, int fromPole, int toPole, int viaPole)
	{
		if(diskCount > 0)
		{
			TowerofHanoi(diskCount - 1, fromPole, viaPole, toPole);
			pole[toPole].push(pole[fromPole].pop());
			ShowHanoi();
			Thread.Sleep(100);
			//Console.ReadKey();	//毎回の移動を確認する場合これを使ってください。
			TowerofHanoi(diskCount - 1, viaPole, toPole, fromPole);
		}
	}

	public static void ShowHanoi()
	{
		Console.Clear();
		Console.WriteLine(">>>>> Tower of Hanoi <<<<<\n");
		for(int i = tire - 1; i >= 0; i--)
		{
			Console.WriteLine(MakeDisk(pole[0].getDisk(i)) + MakeDisk(pole[1].getDisk(i)) + MakeDisk(pole[2].getDisk(i)));
		}
		string Horizen = new String('―', 60);
		Console.WriteLine(Horizen);
	}

	public static string MakeDisk(int num)
	{
		return String.Format("{0, 10}", disk[num]) + String.Format("{0, -10}", disk[num]);
	}
}

class Pole
{
	const int MaxTire = 10;
	int[] Disks;
	int Tire = 0;

	public Pole()
	{
		Disks = new int [MaxTire];
	}

	public Pole(int num)
	{
		if(num > MaxTire)
		{
			Disks = new int [MaxTire];
		}
		else
		{
			Disks = new int [num];
		}
	}

	public void push(int num)	//柱に盤を嵌める
	{
		Disks[Tire] = num;
		Tire++;
	}

	public int pop()			//柱から盤を抜き、盤数を返す（なければ0）
	{
		int val = 0;
		if(Tire > 0)
		{
			val = Disks[Tire - 1];
			Disks[Tire - 1] = 0;
			--Tire;
		}
		return val;
	}

	public int getDisk(int num)	//柱のnum番目の盤数を返す（なければ0）
	{
		return Disks[num];
	}
}
