Add game logic and fix resources

This commit is contained in:
Andrew Cooper
2021-11-27 22:53:17 +11:00
parent 26374f143c
commit 9d7a904e68
14 changed files with 144 additions and 24 deletions

View File

@@ -0,0 +1,78 @@
using System;
using Tower.Models;
using Tower.Resources;
using Tower.UI;
namespace Tower
{
internal class Game
{
private readonly Towers _towers;
private readonly TowerDisplay _display;
private readonly int _optimalMoveCount;
private int _moveCount;
public Game(int diskCount)
{
_towers = new Towers(diskCount);
_display = new TowerDisplay(_towers);
_optimalMoveCount = (1 << diskCount) - 1;
}
public bool Play()
{
Console.Write(Strings.Instructions);
Console.Write(_display);
while (true)
{
if (!Input.TryReadNumber(Prompt.Disk, out int disk)) { return false; }
if (!_towers.TryFindDisk(disk, out var from, out var message))
{
Console.WriteLine(message);
continue;
}
if (!Input.TryReadNumber(Prompt.Needle, out var to)) { return false; }
if (!_towers.TryMoveDisk(from, to))
{
Console.Write(Strings.IllegalMove);
continue;
}
Console.Write(_display);
var result = CheckProgress();
if (result.HasValue) { return result.Value; }
}
}
private bool? CheckProgress()
{
_moveCount++;
if (_moveCount == 128)
{
Console.Write(Strings.TooManyMoves);
return false;
}
if (_towers.Finished)
{
if (_moveCount == _optimalMoveCount)
{
Console.Write(Strings.Congratulations);
}
Console.WriteLine(Strings.TaskFinished, _moveCount);
return true;
}
return default;
}
}
}

View File

@@ -8,6 +8,8 @@ namespace Tower.Models
{
private readonly Stack<int> _disks = new Stack<int>();
public bool IsEmpty => _disks.Count == 0;
public int Top => _disks.TryPeek(out var disk) ? disk : default;
public bool TryPut(int disk)

View File

@@ -2,33 +2,60 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Tower.Resources;
namespace Tower.Models
{
internal class Towers : IEnumerable<(int, int, int)>
{
private readonly Needle[] _needles = new[] { new Needle(), new Needle(), new Needle() };
private static int[] _availableDisks = new[] { 15, 13, 11, 9, 7, 5, 3 };
public bool TryFindDisk(int disk, out int needle)
private readonly Needle[] _needles = new[] { new Needle(), new Needle(), new Needle() };
private readonly int _smallestDisk;
public Towers(int diskCount)
{
for (needle = 1; needle <= 3; needle++)
foreach (int disk in _availableDisks.Take(diskCount))
{
if (_needles[needle].Top == disk) { return true; }
this[1].TryPut(disk);
_smallestDisk = disk;
}
}
private Needle this[int i] => _needles[i-1];
public bool Finished => this[1].IsEmpty && this[2].IsEmpty;
public bool TryFindDisk(int disk, out int needle, out string message)
{
needle = default;
message = default;
if (disk < _smallestDisk)
{
message = Strings.DiskNotInPlay;
return false;
}
for (needle = 1; needle <= 3; needle++)
{
if (this[needle].Top == disk) { return true; }
}
message = Strings.DiskUnavailable;
return false;
}
public bool TryMoveDisk(int from, int to)
{
if (!_needles[from].TryGetTopDisk(out var disk))
if (!this[from].TryGetTopDisk(out var disk))
{
throw new InvalidOperationException($"Needle {from} is empty");
}
if (_needles[to].TryPut(disk)) { return true; }
if (this[to].TryPut(disk)) { return true; }
_needles[from].TryPut(disk);
this[from].TryPut(disk);
return false;
}

View File

@@ -1,4 +1,6 @@
using System;
using Tower.Resources;
using Tower.UI;
namespace Tower
{
@@ -6,7 +8,20 @@ namespace Tower
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.Write(Strings.Title);
do
{
Console.Write(Strings.Intro);
if (!Input.TryReadNumber(Prompt.DiskCount, out var diskCount)) { return; }
var game = new Game(diskCount);
if (!game.Play()) { return; }
} while (Input.ReadYesNo(Strings.PlayAgainPrompt, Strings.YesNoPrompt));
Console.Write(Strings.Thanks);
}
}
}

View File

@@ -1,2 +1,2 @@
Congratulations
Congratulations!!

View File

@@ -0,0 +1 @@
That disk is not in play. Make another choice.

View File

@@ -8,3 +8,4 @@ startup with the disks on needle 1, and attempt to move them
to needle 3.
Good luck!

View File

@@ -1,7 +1,3 @@
Towers
Creative Computing Morristown, New Jersey
Towers of Hanoi puzzle.

View File

@@ -10,6 +10,7 @@ namespace Tower.Resources
internal static string DiskCountPrompt => GetResource();
internal static string DiskCountQuit => GetResource();
internal static string DiskCountRetry => GetResource();
internal static string DiskNotInPlay => GetResource();
internal static string DiskPrompt => GetResource();
internal static string DiskQuit => GetResource();
internal static string DiskRetry => GetResource();
@@ -21,6 +22,7 @@ namespace Tower.Resources
internal static string NeedleQuit => GetResource();
internal static string NeedleRetry => GetResource();
internal static string PlayAgainPrompt => GetResource();
internal static string TaskFinished => GetResource();
internal static string Thanks => GetResource();
internal static string Title => GetResource();
internal static string TooManyMoves => GetResource();

View File

@@ -0,0 +1,2 @@
You have performed the task in {0} moves.

View File

@@ -3,9 +3,3 @@
Towers of Hanoi puzzle.
You must transfer the disks from the left to the right
tower, one at a time, never putting a larger dish on a
smaller disk.

View File

@@ -1,2 +1,2 @@
Sorry, but i have orders to stop is you make more than
Sorry, but I have orders to stop if you make more than
128 moves.

View File

@@ -61,6 +61,8 @@ namespace Tower.UI
private static string ReadString(string prompt)
{
Prompt(prompt);
var inputValues = ReadStrings();
if (inputValues.Length > 1)
{

View File

@@ -19,15 +19,15 @@ namespace Tower.UI
foreach (var row in _towers)
{
AddTower(row.Item1);
AddTower(row.Item2);
AddTower(row.Item3);
AppendTower(row.Item1);
AppendTower(row.Item2);
AppendTower(row.Item3);
builder.AppendLine();
}
return builder.ToString();
void AddTower(int size)
void AppendTower(int size)
{
var padding = 10 - size / 2;
builder.Append(' ', padding).Append('*', Math.Max(1, size)).Append(' ', padding);