mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-31 15:07:42 -08:00
Add game logic and fix resources
This commit is contained in:
78
90 Tower/csharp/Tower/Game.cs
Normal file
78
90 Tower/csharp/Tower/Game.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
|
||||
Congratulations
|
||||
Congratulations!!
|
||||
|
||||
1
90 Tower/csharp/Tower/Resources/DiskNotInPlay.txt
Normal file
1
90 Tower/csharp/Tower/Resources/DiskNotInPlay.txt
Normal file
@@ -0,0 +1 @@
|
||||
That disk is not in play. Make another choice.
|
||||
@@ -8,3 +8,4 @@ startup with the disks on needle 1, and attempt to move them
|
||||
to needle 3.
|
||||
|
||||
Good luck!
|
||||
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
Towers
|
||||
Creative Computing Morristown, New Jersey
|
||||
|
||||
|
||||
|
||||
Towers of Hanoi puzzle.
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
2
90 Tower/csharp/Tower/Resources/TaskFinished.txt
Normal file
2
90 Tower/csharp/Tower/Resources/TaskFinished.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
You have performed the task in {0} moves.
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -61,6 +61,8 @@ namespace Tower.UI
|
||||
|
||||
private static string ReadString(string prompt)
|
||||
{
|
||||
Prompt(prompt);
|
||||
|
||||
var inputValues = ReadStrings();
|
||||
if (inputValues.Length > 1)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user