Compare commits

...

2 Commits

Author SHA1 Message Date
479014a03b Добавленно TUI 2026-03-05 23:00:24 +04:00
3d57b2a574 рефактор HanoyTowers, добавление решателя 2026-03-05 22:07:29 +04:00
4 changed files with 298 additions and 65 deletions

View File

@@ -1,82 +1,183 @@
namespace su.divan2000.PLandDS_hanoi
using System;
using su.divan2000.UtilsTUI;
namespace su.divan2000.PLandDS_HanoiTowers
{
class HanoiTowers
{
private int[] tower_A;
private int[] tower_B;
private int[] tower_C;
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;
tower_A = new int[size];
tower_B = new int[size];
tower_C = new int[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++)
{
tower_A[i] = size - i;
towers[0][i] = size - i;
}
}
public string toString()
public override string ToString()
{
int charsPerTower = size.ToString().Length;
string result = "";
for (int i = size - 1; i >= 0; i--)
{
result += tower_A[i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
result += tower_B[i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
result += tower_C[i].ToString().PadLeft(charsPerTower).Replace("0", "|") + "\n";
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(int from, int to)
public void move(TowerName from, TowerName to)
{
int[] sourceTower = getTower(from);
int[] targetTower = getTower(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 == -1)
{
throw new InvalidOperationException("No disk to move");
}
if (targetTopIndex != -1 && sourceTower[sourceTopIndex] > targetTower[targetTopIndex])
{
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;
}
private int[] getTower(int index)
public int getTopIndex(int[] tower)
{
switch (index)
{
case 0: return tower_A;
case 1: return tower_B;
case 2: return tower_C;
default: throw new ArgumentException("Invalid tower index");
}
}
private int getTopIndex(int[] tower)
{
for (int i = size - 1; i >= 0; i--)
for (int i = size-1; i >= 0; i--)
{
if (tower[i] != 0)
{
return i;
}
}
return -1; // Tower is full
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;
}
}
}

View File

@@ -1,36 +1,14 @@
using System;
namespace su.divan2000.PLandDS_hanoi
namespace su.divan2000.PLandDS_HanoiTowers
{
class Program
{
static void Main()
{
HanoiTowers towers = new HanoiTowers(4);
Console.WriteLine(towers.toString());
try
{
towers.move(0, 1);
Console.WriteLine(towers.toString());
towers.move(0, 2);
Console.WriteLine(towers.toString());
towers.move(1, 2);
Console.WriteLine(towers.toString());
towers.move(0, 1);
Console.WriteLine(towers.toString());
towers.move(2, 0);
Console.WriteLine(towers.toString());
towers.move(2, 1);
Console.WriteLine(towers.toString());
towers.move(0, 1);
}
catch (InvalidOperationException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
Console.WriteLine(towers.toString());
HanoiUtils.RunTUI();
Environment.Exit(0);
}
}
}

45
TUI/InputHelper.cs Normal file
View File

@@ -0,0 +1,45 @@
using System;
namespace su.divan2000.UtilsTUI
{
public static class InputHelper
{
public static double AskDouble(string prompt)
{
double val;
while (true)
{
Console.Write(prompt);
if (double.TryParse(Console.ReadLine(), out val))
return val;
Console.WriteLine("Некорректный ввод. Введите число в формате 1.0, 2.5 и т.д.");
}
}
public static int AskInt(string prompt)
{
int val;
while (true)
{
Console.Write(prompt);
if (int.TryParse(Console.ReadLine(), out val))
return val;
Console.WriteLine("Некорректный ввод. Введите целое число.");
}
}
public static bool AskYesNo(string prompt)
{
while (true)
{
Console.Write(prompt);
string? s = Console.ReadLine()?.Trim().ToLower();
if (s == "да") return true;
if (s == "нет") return false;
Console.WriteLine("Введите 'да' или 'нет'.");
}
}
}
}

109
TUI/Menu.cs Normal file
View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace su.divan2000.UtilsTUI
{
/// <summary>
/// TUI menu;
/// </summary>
internal class Menu
{
private List<Option> options;
private int selected;
private string title;
/// <summary>
/// Constructor of menu;
/// </summary>
/// <param name="title">Title of menu</param>
public Menu(string title)
{
this.title = title;
this.options = new List<Option> { };
}
/// <summary>
/// Add option to menu;
/// </summary>
/// <param name="name">Name of option</param>
/// <param name="action">Action, runs if option selected</param>
public void AddOption(string name, Action action)
{
this.options.Add(new Option(name, action));
}
/// <summary>
/// Set title of menu;
/// </summary>
/// <param name="title">New title of menu</param>
public void SetTitle(string title)
{
this.title = title;
}
/// <summary>
/// Run menu;
/// Console will be clean!;
/// </summary>
public void RunMenu()
{
selected = 0;
this.PrintMenu();
ConsoleKeyInfo keyinfo;
do
{
keyinfo = Console.ReadKey();
if (keyinfo.Key == ConsoleKey.DownArrow)
{
if (selected + 1 < options.Count)
{
selected++;
this.PrintMenu();
}
}
if (keyinfo.Key == ConsoleKey.UpArrow)
{
if (selected > 0)
{
selected--;
this.PrintMenu();
}
}
if (keyinfo.Key == ConsoleKey.Enter)
{
Console.Clear();
options[selected].Action.Invoke();
}
} while (keyinfo.Key != ConsoleKey.Enter);
}
private void PrintMenu()
{
Console.Clear();
Console.WriteLine(title);
int optionIndex = 0;
foreach (Option option in options)
{
string pointer = optionIndex == selected ? " ->" : " ";
Console.WriteLine($"{pointer}{option.Name}");
optionIndex++;
}
}
private struct Option
{
public string Name { get; }
public Action Action { get; }
public Option(string name, Action action)
{
Name = name;
Action = action;
}
}
}
}