Files
HanoyTowers/HanoiTowers.cs
2026-03-05 23:00:24 +04:00

183 lines
6.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using su.divan2000.UtilsTUI;
namespace su.divan2000.PLandDS_HanoiTowers
{
class HanoiTowers
{
private int[][] towers;
private int size;
public enum TowerName { A, B, C }
public int Size { get { return size; } }
public HanoiTowers(int size)
{
this.size = size;
towers = new int[3][];
towers[0] = new int[size];
towers[1] = new int[size];
towers[2] = new int[size];
for (int i = 0; i < size; i++)
{
towers[0][i] = size - i;
}
}
public override string ToString()
{
int charsPerTower = size.ToString().Length;
string result = "";
for (int i = size - 1; i >= 0; i--)
{
result += towers[0][i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
result += towers[1][i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
result += towers[2][i].ToString().PadLeft(charsPerTower).Replace("0", "|") + "\n";
}
return result;
}
public void move(TowerName from, TowerName to)
{
if (from == to) return;
int[] sourceTower = towers[(int)from];
int[] targetTower = towers[(int)to];
int sourceTopIndex = getTopIndex(sourceTower);
int targetTopIndex = getTopIndex(targetTower);
if (sourceTopIndex < 0)
throw new InvalidOperationException("Source tower is empty");
if (targetTopIndex >= size)
throw new InvalidOperationException("Target tower is full");
if (targetTopIndex >= 0 && sourceTower[sourceTopIndex] > targetTower[targetTopIndex])
throw new InvalidOperationException("Cannot place larger disk on smaller one");
targetTower[targetTopIndex + 1] = sourceTower[sourceTopIndex];
sourceTower[sourceTopIndex] = 0;
}
public int getTopIndex(int[] tower)
{
for (int i = size-1; i >= 0; i--)
{
if (tower[i] != 0)
{
return i;
}
}
return -1; // Tower is empty
}
public bool isSolved()
{
return towers[0].All(disk => disk == 0) && towers[1].All(disk => disk == 0) && towers[2].All(disk => disk != 0);
}
public void reset()
{
for (int i = 0; i < size; i++)
{
towers[0][i] = size - i;
towers[1][i] = 0;
towers[2][i] = 0;
}
}
}
static class HanoiUtils
{
public struct Move
{
public HanoiTowers.TowerName From;
public HanoiTowers.TowerName To;
public Move(HanoiTowers.TowerName from, HanoiTowers.TowerName to)
{
From = from;
To = to;
}
}
public static List<Move> MoveN(HanoiTowers towers, int n, HanoiTowers.TowerName from, HanoiTowers.TowerName to, HanoiTowers.TowerName aux)
{
List<Move> moves = new List<Move>();
if (n == 1)
{
moves.Add(new Move(from, to));
}
else
{
moves.AddRange(MoveN(towers, n - 1, from, aux, to));
moves.Add(new Move(from, to));
moves.AddRange(MoveN(towers, n - 1, aux, to, from));
}
return moves;
}
public static void ExecuteMove(HanoiTowers towers, Move move)
{
towers.move(move.From, move.To);
}
public static int RunTUI()
{
bool running;
int n = InputHelper.AskInt("Введите количество дисков в ханойской башне: ");
HanoiTowers towers = new HanoiTowers(n);
HanoiTowers.TowerName selectedTower = HanoiTowers.TowerName.A;
Menu manualInput = new Menu("Выберите башню:\n"+towers);
manualInput.AddOption("Башня A", () => selectedTower = HanoiTowers.TowerName.A);
manualInput.AddOption("Башня B", () => selectedTower = HanoiTowers.TowerName.B);
manualInput.AddOption("Башня C", () => selectedTower = HanoiTowers.TowerName.C);
Menu menu = new Menu("Выберите действие:\n"+towers);
menu.AddOption("Сделать ход вручную", () =>
{
manualInput.SetTitle("Выберите башню, с которой хотите переместить диск:\n" + towers);
manualInput.RunMenu();
HanoiTowers.TowerName from = selectedTower;
manualInput.SetTitle("Перемещение с башни " + from + "\nВыберите башню, на которую хотите переместить диск:\n" + towers);
manualInput.RunMenu();
HanoiTowers.TowerName to = selectedTower;
try
{
towers.move(from, to);
} catch (InvalidOperationException ex)
{
Console.WriteLine("Невозможно переместить диск: " + ex.Message);
}
});
menu.AddOption("Сбросить башню", () =>
{
towers.reset();
});
menu.AddOption("Решить башню автоматически", () =>
{
List<Move> moves = MoveN(towers, towers.Size, HanoiTowers.TowerName.A, HanoiTowers.TowerName.C, HanoiTowers.TowerName.B);
towers.reset();
foreach (Move move in moves) {
ExecuteMove(towers, move);
Console.Clear();
Console.WriteLine("Решение башни:\n" + towers);
System.Threading.Thread.Sleep(500); // Пауза для визуализации
}
});
menu.AddOption("Выход", () =>
{
running = false;
});
for (running = true; running;)
{
menu.SetTitle("Выберите действие:\n" + towers);
menu.RunMenu();
running = !towers.isSolved();
}
return 0;
}
}
}