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 MoveN(HanoiTowers towers, int n, HanoiTowers.TowerName from, HanoiTowers.TowerName to, HanoiTowers.TowerName aux) { List moves = new List(); 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 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; } } }