Compare commits
2 Commits
dbc3b3359e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 479014a03b | |||
| 3d57b2a574 |
177
HanoiTowers.cs
177
HanoiTowers.cs
@@ -1,82 +1,183 @@
|
|||||||
namespace su.divan2000.PLandDS_hanoi
|
using System;
|
||||||
|
using su.divan2000.UtilsTUI;
|
||||||
|
|
||||||
|
namespace su.divan2000.PLandDS_HanoiTowers
|
||||||
{
|
{
|
||||||
class HanoiTowers
|
class HanoiTowers
|
||||||
{
|
{
|
||||||
private int[] tower_A;
|
private int[][] towers;
|
||||||
private int[] tower_B;
|
|
||||||
private int[] tower_C;
|
|
||||||
|
|
||||||
private int size;
|
private int size;
|
||||||
|
|
||||||
|
public enum TowerName { A, B, C }
|
||||||
|
public int Size { get { return size; } }
|
||||||
|
|
||||||
public HanoiTowers(int size)
|
public HanoiTowers(int size)
|
||||||
{
|
{
|
||||||
this.size = size;
|
this.size = size;
|
||||||
tower_A = new int[size];
|
towers = new int[3][];
|
||||||
tower_B = new int[size];
|
towers[0] = new int[size];
|
||||||
tower_C = new int[size];
|
towers[1] = new int[size];
|
||||||
|
towers[2] = new int[size];
|
||||||
|
|
||||||
for (int i = 0; i < size; i++)
|
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;
|
int charsPerTower = size.ToString().Length;
|
||||||
string result = "";
|
string result = "";
|
||||||
for (int i = size - 1; i >= 0; i--)
|
for (int i = size - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
result += tower_A[i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
|
result += towers[0][i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
|
||||||
result += tower_B[i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
|
result += towers[1][i].ToString().PadLeft(charsPerTower).Replace("0", "|") + " ";
|
||||||
result += tower_C[i].ToString().PadLeft(charsPerTower).Replace("0", "|") + "\n";
|
result += towers[2][i].ToString().PadLeft(charsPerTower).Replace("0", "|") + "\n";
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(int from, int to)
|
public void move(TowerName from, TowerName to)
|
||||||
{
|
{
|
||||||
int[] sourceTower = getTower(from);
|
if (from == to) return;
|
||||||
int[] targetTower = getTower(to);
|
|
||||||
|
|
||||||
|
int[] sourceTower = towers[(int)from];
|
||||||
|
int[] targetTower = towers[(int)to];
|
||||||
int sourceTopIndex = getTopIndex(sourceTower);
|
int sourceTopIndex = getTopIndex(sourceTower);
|
||||||
int targetTopIndex = getTopIndex(targetTower);
|
int targetTopIndex = getTopIndex(targetTower);
|
||||||
|
|
||||||
if (sourceTopIndex == -1)
|
if (sourceTopIndex < 0)
|
||||||
{
|
throw new InvalidOperationException("Source tower is empty");
|
||||||
throw new InvalidOperationException("No disk to move");
|
if (targetTopIndex >= size)
|
||||||
}
|
throw new InvalidOperationException("Target tower is full");
|
||||||
|
if (targetTopIndex >= 0 && sourceTower[sourceTopIndex] > targetTower[targetTopIndex])
|
||||||
if (targetTopIndex != -1 && sourceTower[sourceTopIndex] > targetTower[targetTopIndex])
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Cannot place larger disk on smaller one");
|
throw new InvalidOperationException("Cannot place larger disk on smaller one");
|
||||||
}
|
|
||||||
|
|
||||||
targetTower[targetTopIndex + 1] = sourceTower[sourceTopIndex];
|
targetTower[targetTopIndex + 1] = sourceTower[sourceTopIndex];
|
||||||
sourceTower[sourceTopIndex] = 0;
|
sourceTower[sourceTopIndex] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] getTower(int index)
|
public int getTopIndex(int[] tower)
|
||||||
{
|
{
|
||||||
switch (index)
|
for (int i = size-1; i >= 0; i--)
|
||||||
{
|
|
||||||
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--)
|
|
||||||
{
|
{
|
||||||
if (tower[i] != 0)
|
if (tower[i] != 0)
|
||||||
{
|
{
|
||||||
return i;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
28
Program.cs
28
Program.cs
@@ -1,36 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace su.divan2000.PLandDS_hanoi
|
namespace su.divan2000.PLandDS_HanoiTowers
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static void Main()
|
static void Main()
|
||||||
{
|
{
|
||||||
HanoiTowers towers = new HanoiTowers(4);
|
HanoiUtils.RunTUI();
|
||||||
Console.WriteLine(towers.toString());
|
|
||||||
|
|
||||||
try
|
Environment.Exit(0);
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
45
TUI/InputHelper.cs
Normal file
45
TUI/InputHelper.cs
Normal 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
109
TUI/Menu.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user