From f6bde494744cbcff2bb46c8656ab3c2e9763dc2c Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Tue, 24 Jan 2023 07:40:15 +1100 Subject: [PATCH 1/6] Configure project --- 72_Queen/csharp/Queen.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/72_Queen/csharp/Queen.csproj b/72_Queen/csharp/Queen.csproj index d3fe4757..3870320c 100644 --- a/72_Queen/csharp/Queen.csproj +++ b/72_Queen/csharp/Queen.csproj @@ -6,4 +6,12 @@ enable enable + + + + + + + + From 083a11f42ffbe9ddb95a864115099cb2d8170269 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Tue, 24 Jan 2023 17:47:34 +1100 Subject: [PATCH 2/6] Add string resources --- 72_Queen/csharp/Program.cs | 3 ++ 72_Queen/csharp/Resources/AnyonePrompt.txt | 1 + 72_Queen/csharp/Resources/ComputerMove.txt | 1 + 72_Queen/csharp/Resources/Congratulations.txt | 7 +++ 72_Queen/csharp/Resources/Forfeit.txt | 2 + 72_Queen/csharp/Resources/IWin.txt | 4 ++ 72_Queen/csharp/Resources/IllegalMove.txt | 2 + 72_Queen/csharp/Resources/IllegalStart.txt | 3 ++ 72_Queen/csharp/Resources/Instructions.txt | 16 +++++++ .../csharp/Resources/InstructionsPrompt.txt | 1 + 72_Queen/csharp/Resources/MovePrompt.txt | 1 + 72_Queen/csharp/Resources/Resource.cs | 47 +++++++++++++++++++ 72_Queen/csharp/Resources/StartPrompt.txt | 1 + 72_Queen/csharp/Resources/Thanks.txt | 1 + 72_Queen/csharp/Resources/Title.txt | 5 ++ 72_Queen/csharp/Resources/YesyOrNo.txt | 1 + 16 files changed, 96 insertions(+) create mode 100644 72_Queen/csharp/Program.cs create mode 100644 72_Queen/csharp/Resources/AnyonePrompt.txt create mode 100644 72_Queen/csharp/Resources/ComputerMove.txt create mode 100644 72_Queen/csharp/Resources/Congratulations.txt create mode 100644 72_Queen/csharp/Resources/Forfeit.txt create mode 100644 72_Queen/csharp/Resources/IWin.txt create mode 100644 72_Queen/csharp/Resources/IllegalMove.txt create mode 100644 72_Queen/csharp/Resources/IllegalStart.txt create mode 100644 72_Queen/csharp/Resources/Instructions.txt create mode 100644 72_Queen/csharp/Resources/InstructionsPrompt.txt create mode 100644 72_Queen/csharp/Resources/MovePrompt.txt create mode 100644 72_Queen/csharp/Resources/Resource.cs create mode 100644 72_Queen/csharp/Resources/StartPrompt.txt create mode 100644 72_Queen/csharp/Resources/Thanks.txt create mode 100644 72_Queen/csharp/Resources/Title.txt create mode 100644 72_Queen/csharp/Resources/YesyOrNo.txt diff --git a/72_Queen/csharp/Program.cs b/72_Queen/csharp/Program.cs new file mode 100644 index 00000000..0733c377 --- /dev/null +++ b/72_Queen/csharp/Program.cs @@ -0,0 +1,3 @@ +using Games.Common.IO; + +var io = new ConsoleIO(); \ No newline at end of file diff --git a/72_Queen/csharp/Resources/AnyonePrompt.txt b/72_Queen/csharp/Resources/AnyonePrompt.txt new file mode 100644 index 00000000..a0289fd6 --- /dev/null +++ b/72_Queen/csharp/Resources/AnyonePrompt.txt @@ -0,0 +1 @@ +Anyone else care to try \ No newline at end of file diff --git a/72_Queen/csharp/Resources/ComputerMove.txt b/72_Queen/csharp/Resources/ComputerMove.txt new file mode 100644 index 00000000..f31b8e32 --- /dev/null +++ b/72_Queen/csharp/Resources/ComputerMove.txt @@ -0,0 +1 @@ +Computer moves to square {0} diff --git a/72_Queen/csharp/Resources/Congratulations.txt b/72_Queen/csharp/Resources/Congratulations.txt new file mode 100644 index 00000000..9f1db232 --- /dev/null +++ b/72_Queen/csharp/Resources/Congratulations.txt @@ -0,0 +1,7 @@ + +C O N G R A T U L A T I O N S . . . + +You have won--very well played. +It looks like I have met my match. +Thanks for playing--I can't win all the time. + diff --git a/72_Queen/csharp/Resources/Forfeit.txt b/72_Queen/csharp/Resources/Forfeit.txt new file mode 100644 index 00000000..120da75d --- /dev/null +++ b/72_Queen/csharp/Resources/Forfeit.txt @@ -0,0 +1,2 @@ +Looks like I have won by forfeit. + diff --git a/72_Queen/csharp/Resources/IWin.txt b/72_Queen/csharp/Resources/IWin.txt new file mode 100644 index 00000000..ced5d716 --- /dev/null +++ b/72_Queen/csharp/Resources/IWin.txt @@ -0,0 +1,4 @@ + +Nice try, but it looks like I have won. +Thanks for playing. + diff --git a/72_Queen/csharp/Resources/IllegalMove.txt b/72_Queen/csharp/Resources/IllegalMove.txt new file mode 100644 index 00000000..3d31638d --- /dev/null +++ b/72_Queen/csharp/Resources/IllegalMove.txt @@ -0,0 +1,2 @@ + +Y O U C H E A T . . . Try again diff --git a/72_Queen/csharp/Resources/IllegalStart.txt b/72_Queen/csharp/Resources/IllegalStart.txt new file mode 100644 index 00000000..25402bb6 --- /dev/null +++ b/72_Queen/csharp/Resources/IllegalStart.txt @@ -0,0 +1,3 @@ +Please read the instructions again. +You have begun illegally. + diff --git a/72_Queen/csharp/Resources/Instructions.txt b/72_Queen/csharp/Resources/Instructions.txt new file mode 100644 index 00000000..d440b335 --- /dev/null +++ b/72_Queen/csharp/Resources/Instructions.txt @@ -0,0 +1,16 @@ +We are going to play a game based on one of the chess +moves. Our queen will be able to move only to the left, +down, or diagonally down and to the left. + +The object of the game is to place the queen in the lower +left hand square by alternating moves between you and the +computer. The first one to place the queen there wins. + +You go first and place the queen in any one of the squares +on the top row or right hand column. +That will be your first move. +We alternate moves. +You may forfeit by typing '0' as your move. +Be sure to press the return key after each response. + + diff --git a/72_Queen/csharp/Resources/InstructionsPrompt.txt b/72_Queen/csharp/Resources/InstructionsPrompt.txt new file mode 100644 index 00000000..0d311b60 --- /dev/null +++ b/72_Queen/csharp/Resources/InstructionsPrompt.txt @@ -0,0 +1 @@ +Do you want instructions \ No newline at end of file diff --git a/72_Queen/csharp/Resources/MovePrompt.txt b/72_Queen/csharp/Resources/MovePrompt.txt new file mode 100644 index 00000000..8cb18999 --- /dev/null +++ b/72_Queen/csharp/Resources/MovePrompt.txt @@ -0,0 +1 @@ +What is your move \ No newline at end of file diff --git a/72_Queen/csharp/Resources/Resource.cs b/72_Queen/csharp/Resources/Resource.cs new file mode 100644 index 00000000..5290766e --- /dev/null +++ b/72_Queen/csharp/Resources/Resource.cs @@ -0,0 +1,47 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Queen.Resources; + +internal static class Resource +{ + internal static class Streams + { + public static Stream Title => GetStream(); + public static Stream Instructions => GetStream(); + public static Stream YesOrNo => GetStream(); + public static Stream IllegalStart => GetStream(); + public static Stream ComputerMove => GetStream(); + public static Stream IllegalMove => GetStream(); + public static Stream Forfeit => GetStream(); + public static Stream IWin => GetStream(); + public static Stream Congratulations => GetStream(); + public static Stream Thanks => GetStream(); + } + + internal static class Prompts + { + public static string Instructions => GetPrompt(); + public static string Start => GetPrompt(); + public static string Move => GetPrompt(); + public static string Anyone => GetPrompt(); + } + + internal static class Formats + { + public static string Balance => GetString(); + } + + private static string GetPrompt([CallerMemberName] string? name = null) => GetString($"{name}Prompt"); + + private static string GetString([CallerMemberName] string? name = null) + { + using var stream = GetStream(name); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private static Stream GetStream([CallerMemberName] string? name = null) => + Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt") + ?? throw new Exception($"Could not find embedded resource stream '{name}'."); +} \ No newline at end of file diff --git a/72_Queen/csharp/Resources/StartPrompt.txt b/72_Queen/csharp/Resources/StartPrompt.txt new file mode 100644 index 00000000..0b6f395b --- /dev/null +++ b/72_Queen/csharp/Resources/StartPrompt.txt @@ -0,0 +1 @@ +Where would you like to start \ No newline at end of file diff --git a/72_Queen/csharp/Resources/Thanks.txt b/72_Queen/csharp/Resources/Thanks.txt new file mode 100644 index 00000000..53980b09 --- /dev/null +++ b/72_Queen/csharp/Resources/Thanks.txt @@ -0,0 +1 @@ +Ok --- thanks again. \ No newline at end of file diff --git a/72_Queen/csharp/Resources/Title.txt b/72_Queen/csharp/Resources/Title.txt new file mode 100644 index 00000000..63549d09 --- /dev/null +++ b/72_Queen/csharp/Resources/Title.txt @@ -0,0 +1,5 @@ + Queen + Creative Computing Morristown, New Jersey + + + diff --git a/72_Queen/csharp/Resources/YesyOrNo.txt b/72_Queen/csharp/Resources/YesyOrNo.txt new file mode 100644 index 00000000..657bf8aa --- /dev/null +++ b/72_Queen/csharp/Resources/YesyOrNo.txt @@ -0,0 +1 @@ +Please answer 'Yes' or 'No'. From 29636d5696c8fb97464a6c4d0a5d3add7df1a709 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Wed, 25 Jan 2023 23:01:36 +1100 Subject: [PATCH 3/6] CSHARP-72 Add game intro --- 72_Queen/csharp/Games.cs | 34 +++++++++++++++++++ 72_Queen/csharp/Program.cs | 8 +++-- .../Resources/{YesyOrNo.txt => YesOrNo.txt} | 0 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 72_Queen/csharp/Games.cs rename 72_Queen/csharp/Resources/{YesyOrNo.txt => YesOrNo.txt} (100%) diff --git a/72_Queen/csharp/Games.cs b/72_Queen/csharp/Games.cs new file mode 100644 index 00000000..73a0b80b --- /dev/null +++ b/72_Queen/csharp/Games.cs @@ -0,0 +1,34 @@ +namespace Queen; + +internal class Game +{ + private readonly IReadWrite _io; + private readonly IRandom _random; + + public Game(IReadWrite io, IRandom random) + { + _io = io; + _random = random; + } + + internal void Play() + { + _io.Write(Streams.Title); + if (_io.ShouldDisplayInstructions()) { _io.Write(Streams.Instructions); } + } +} + +internal static class IOExtensions +{ + internal static bool ShouldDisplayInstructions(this IReadWrite io) + { + while (true) + { + var answer = io.ReadString(Prompts.Instructions).ToLower(); + if (answer == "yes") { return true; } + if (answer == "no") { return false; } + + io.Write(Streams.YesOrNo); + } + } +} diff --git a/72_Queen/csharp/Program.cs b/72_Queen/csharp/Program.cs index 0733c377..a32aded0 100644 --- a/72_Queen/csharp/Program.cs +++ b/72_Queen/csharp/Program.cs @@ -1,3 +1,7 @@ -using Games.Common.IO; +global using Games.Common.IO; +global using Games.Common.Randomness; +global using static Queen.Resources.Resource; -var io = new ConsoleIO(); \ No newline at end of file +using Queen; + +new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file diff --git a/72_Queen/csharp/Resources/YesyOrNo.txt b/72_Queen/csharp/Resources/YesOrNo.txt similarity index 100% rename from 72_Queen/csharp/Resources/YesyOrNo.txt rename to 72_Queen/csharp/Resources/YesOrNo.txt From 1c911e9b3055469c6ed1a1e7aad490fbb6a5fbf7 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Fri, 27 Jan 2023 08:32:16 +1100 Subject: [PATCH 4/6] Add replay loop --- 72_Queen/csharp/Games.cs | 72 +++++++++++++++++++++++++-- 72_Queen/csharp/Program.cs | 2 +- 72_Queen/csharp/Resources/Board.txt | 26 ++++++++++ 72_Queen/csharp/Resources/Resource.cs | 1 + 4 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 72_Queen/csharp/Resources/Board.txt diff --git a/72_Queen/csharp/Games.cs b/72_Queen/csharp/Games.cs index 73a0b80b..4504c771 100644 --- a/72_Queen/csharp/Games.cs +++ b/72_Queen/csharp/Games.cs @@ -1,11 +1,11 @@ namespace Queen; -internal class Game +internal class Games { private readonly IReadWrite _io; private readonly IRandom _random; - public Game(IReadWrite io, IRandom random) + public Games(IReadWrite io, IRandom random) { _io = io; _random = random; @@ -14,21 +14,83 @@ internal class Game internal void Play() { _io.Write(Streams.Title); - if (_io.ShouldDisplayInstructions()) { _io.Write(Streams.Instructions); } + if (_io.ReadYesNo(Prompts.Instructions)) { _io.Write(Streams.Instructions); } + + while (true) + { + PlayGame(); + + if (!_io.ReadYesNo(Prompts.Anyone)) + { + _io.Write(Streams.Thanks); + return; + } + } + } + + internal void PlayGame() + { + _io.Write(Streams.Board); + var humanPosition = _io.ReadPosition(Prompts.Start, p => p.IsStart, Streams.IllegalStart, repeatPrompt: true) + if (humanPosition.IsZero) + { + _io.Write(Streams.Forfeit); + return; + } + + } } internal static class IOExtensions { - internal static bool ShouldDisplayInstructions(this IReadWrite io) + internal static bool ReadYesNo(this IReadWrite io, string prompt) { while (true) { - var answer = io.ReadString(Prompts.Instructions).ToLower(); + var answer = io.ReadString(prompt).ToLower(); if (answer == "yes") { return true; } if (answer == "no") { return false; } io.Write(Streams.YesOrNo); } } + + internal static Position ReadPosition( + this IReadWrite io, + string prompt, + Predicate isValid, + Stream error, + bool repeatPrompt = false) + { + while (true) + { + var response = io.ReadNumber(prompt); + var number = (int)response; + var position = new Position(number); + if (number == response && (position.IsZero || isValid(position))) + { + return position; + } + + io.Write(error); + if (!repeatPrompt) { prompt = ""; } + } + } } + +internal record struct Position(int Diagonal, int Row) +{ + public static readonly Position Zero = new(0); + + public Position(int number) + : this(Diagonal: number / 10, Row: number % 10) + { + } + + public bool IsZero => Row == 0 && Diagonal == 0; + public bool IsStart => Row == 1 || Row == Diagonal; + public bool IsEnd => Row == 8 && Diagonal == 15; + + public override string ToString() => $"{Diagonal}{Row}"; +} \ No newline at end of file diff --git a/72_Queen/csharp/Program.cs b/72_Queen/csharp/Program.cs index a32aded0..a03fe364 100644 --- a/72_Queen/csharp/Program.cs +++ b/72_Queen/csharp/Program.cs @@ -4,4 +4,4 @@ global using static Queen.Resources.Resource; using Queen; -new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file +new Games(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file diff --git a/72_Queen/csharp/Resources/Board.txt b/72_Queen/csharp/Resources/Board.txt new file mode 100644 index 00000000..45a8ab0a --- /dev/null +++ b/72_Queen/csharp/Resources/Board.txt @@ -0,0 +1,26 @@ + + 81 71 61 51 41 31 21 11 + + + 92 82 72 62 52 42 32 22 + + + 103 93 83 73 63 53 43 33 + + + 114 104 94 84 74 64 54 44 + + + 125 115 105 95 85 75 65 55 + + + 136 126 116 106 96 86 76 66 + + + 147 137 127 117 107 97 87 77 + + + 158 148 138 128 118 108 98 88 + + + diff --git a/72_Queen/csharp/Resources/Resource.cs b/72_Queen/csharp/Resources/Resource.cs index 5290766e..9ff43842 100644 --- a/72_Queen/csharp/Resources/Resource.cs +++ b/72_Queen/csharp/Resources/Resource.cs @@ -10,6 +10,7 @@ internal static class Resource public static Stream Title => GetStream(); public static Stream Instructions => GetStream(); public static Stream YesOrNo => GetStream(); + public static Stream Board => GetStream(); public static Stream IllegalStart => GetStream(); public static Stream ComputerMove => GetStream(); public static Stream IllegalMove => GetStream(); From 3e88424a5216963e5d0c7c548aae21567d1a4ce0 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Sat, 28 Jan 2023 15:25:52 +1100 Subject: [PATCH 5/6] Add computer strategy --- 72_Queen/csharp/Computer.cs | 0 72_Queen/csharp/Game.cs | 164 +++++++++++++++++++++ 72_Queen/csharp/Games.cs | 96 ------------ 72_Queen/csharp/Program.cs | 2 +- 72_Queen/csharp/Resources/Board.txt | 1 + 72_Queen/csharp/Resources/Forfeit.txt | 3 +- 72_Queen/csharp/Resources/Instructions.txt | 1 - 72_Queen/csharp/Resources/Thanks.txt | 2 + 8 files changed, 170 insertions(+), 99 deletions(-) create mode 100644 72_Queen/csharp/Computer.cs create mode 100644 72_Queen/csharp/Game.cs delete mode 100644 72_Queen/csharp/Games.cs diff --git a/72_Queen/csharp/Computer.cs b/72_Queen/csharp/Computer.cs new file mode 100644 index 00000000..e69de29b diff --git a/72_Queen/csharp/Game.cs b/72_Queen/csharp/Game.cs new file mode 100644 index 00000000..220e4535 --- /dev/null +++ b/72_Queen/csharp/Game.cs @@ -0,0 +1,164 @@ +namespace Queen; + +internal class Game +{ + private readonly IReadWrite _io; + private readonly IRandom _random; + private readonly Computer _computer; + + public Game(IReadWrite io, IRandom random) + { + _io = io; + _random = random; + _computer = new Computer(random); + } + + internal void PlaySeries() + { + _io.Write(Streams.Title); + if (_io.ReadYesNo(Prompts.Instructions)) { _io.Write(Streams.Instructions); } + + while (true) + { + var result = PlayGame(); + _io.Write(result switch + { + Result.HumanForfeits => Streams.Forfeit, + Result.HumanWins => Streams.Congratulations, + Result.ComputerWins => Streams.IWin, + _ => throw new InvalidOperationException($"Unexpected result {result}") + }); + + if (!_io.ReadYesNo(Prompts.Anyone)) { break; } + } + + _io.Write(Streams.Thanks); + } + + private Result PlayGame() + { + _io.Write(Streams.Board); + var humanPosition = _io.ReadPosition(Prompts.Start, p => p.IsStart, Streams.IllegalStart, repeatPrompt: true); + if (humanPosition.IsZero) { return Result.HumanForfeits; } + + while (true) + { + var computerPosition = _computer.GetMove(humanPosition); + if (computerPosition.IsEnd) { return Result.ComputerWins; } + } + + } + + private enum Result { ComputerWins, HumanWins, HumanForfeits }; +} + +internal class Computer +{ + private static readonly HashSet _randomiseFrom = new() { 41, 44, 73, 75, 126, 127 }; + private static readonly HashSet _desirable = new() { 73, 75, 126, 127, 158 }; + private readonly IRandom _random; + + public Computer(IRandom random) + { + _random = random; + } + + public Position GetMove(Position from) + => from + (_randomiseFrom.Contains(from) ? _random.NextMove() : FindMove(from)); + + private Move FindMove(Position from) + { + for (int i = 7; i > 0; i--) + { + if (IsOptimal(Move.Left, out var move)) { return move; } + if (IsOptimal(Move.Down, out move)) { return move; } + if (IsOptimal(Move.DownLeft, out move)) { return move; } + + bool IsOptimal(Move direction, out Move move) + { + move = direction * i; + return _desirable.Contains(from + move); + } + } + + return _random.NextMove(); + } +} + +internal static class IOExtensions +{ + internal static bool ReadYesNo(this IReadWrite io, string prompt) + { + while (true) + { + var answer = io.ReadString(prompt).ToLower(); + if (answer == "yes") { return true; } + if (answer == "no") { return false; } + + io.Write(Streams.YesOrNo); + } + } + + internal static Position ReadPosition( + this IReadWrite io, + string prompt, + Predicate isValid, + Stream error, + bool repeatPrompt = false) + { + while (true) + { + var response = io.ReadNumber(prompt); + var number = (int)response; + var position = new Position(number); + if (number == response && (position.IsZero || isValid(position))) + { + return position; + } + + io.Write(error); + if (!repeatPrompt) { prompt = ""; } + } + } +} + +internal record struct Position(int Diagonal, int Row) +{ + public static readonly Position Zero = new(0); + + public Position(int number) + : this(Diagonal: number / 10, Row: number % 10) + { + } + + public bool IsZero => Row == 0 && Diagonal == 0; + public bool IsStart => Row == 1 || Row == Diagonal; + public bool IsEnd => Row == 8 && Diagonal == 15; + + public override string ToString() => $"{Diagonal}{Row}"; + + public static implicit operator Position(int value) => new(value); + + public static Position operator +(Position position, Move move) + => new(Diagonal: position.Diagonal + move.Diagonal, Row: position.Row + move.Row); +} + +internal static class RandomExtensions +{ + internal static Move NextMove(this IRandom random) + => random.NextFloat() switch + { + > 0.6F => Move.Down, + > 0.3F => Move.DownLeft, + _ => Move.Left + }; +} + +internal record struct Move(int Diagonal, int Row) +{ + public static readonly Move Left = new(1, 0); + public static readonly Move DownLeft = new(2, 1); + public static readonly Move Down = new(1, 1); + + public static Move operator *(Move move, int scale) => new(move.Diagonal * scale, move.Row * scale); +} \ No newline at end of file diff --git a/72_Queen/csharp/Games.cs b/72_Queen/csharp/Games.cs deleted file mode 100644 index 4504c771..00000000 --- a/72_Queen/csharp/Games.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace Queen; - -internal class Games -{ - private readonly IReadWrite _io; - private readonly IRandom _random; - - public Games(IReadWrite io, IRandom random) - { - _io = io; - _random = random; - } - - internal void Play() - { - _io.Write(Streams.Title); - if (_io.ReadYesNo(Prompts.Instructions)) { _io.Write(Streams.Instructions); } - - while (true) - { - PlayGame(); - - if (!_io.ReadYesNo(Prompts.Anyone)) - { - _io.Write(Streams.Thanks); - return; - } - } - } - - internal void PlayGame() - { - _io.Write(Streams.Board); - var humanPosition = _io.ReadPosition(Prompts.Start, p => p.IsStart, Streams.IllegalStart, repeatPrompt: true) - if (humanPosition.IsZero) - { - _io.Write(Streams.Forfeit); - return; - } - - - } -} - -internal static class IOExtensions -{ - internal static bool ReadYesNo(this IReadWrite io, string prompt) - { - while (true) - { - var answer = io.ReadString(prompt).ToLower(); - if (answer == "yes") { return true; } - if (answer == "no") { return false; } - - io.Write(Streams.YesOrNo); - } - } - - internal static Position ReadPosition( - this IReadWrite io, - string prompt, - Predicate isValid, - Stream error, - bool repeatPrompt = false) - { - while (true) - { - var response = io.ReadNumber(prompt); - var number = (int)response; - var position = new Position(number); - if (number == response && (position.IsZero || isValid(position))) - { - return position; - } - - io.Write(error); - if (!repeatPrompt) { prompt = ""; } - } - } -} - -internal record struct Position(int Diagonal, int Row) -{ - public static readonly Position Zero = new(0); - - public Position(int number) - : this(Diagonal: number / 10, Row: number % 10) - { - } - - public bool IsZero => Row == 0 && Diagonal == 0; - public bool IsStart => Row == 1 || Row == Diagonal; - public bool IsEnd => Row == 8 && Diagonal == 15; - - public override string ToString() => $"{Diagonal}{Row}"; -} \ No newline at end of file diff --git a/72_Queen/csharp/Program.cs b/72_Queen/csharp/Program.cs index a03fe364..5e017df6 100644 --- a/72_Queen/csharp/Program.cs +++ b/72_Queen/csharp/Program.cs @@ -4,4 +4,4 @@ global using static Queen.Resources.Resource; using Queen; -new Games(new ConsoleIO(), new RandomNumberGenerator()).Play(); \ No newline at end of file +new Game(new ConsoleIO(), new RandomNumberGenerator()).PlaySeries(); \ No newline at end of file diff --git a/72_Queen/csharp/Resources/Board.txt b/72_Queen/csharp/Resources/Board.txt index 45a8ab0a..854be1bb 100644 --- a/72_Queen/csharp/Resources/Board.txt +++ b/72_Queen/csharp/Resources/Board.txt @@ -1,4 +1,5 @@ + 81 71 61 51 41 31 21 11 diff --git a/72_Queen/csharp/Resources/Forfeit.txt b/72_Queen/csharp/Resources/Forfeit.txt index 120da75d..09858bc1 100644 --- a/72_Queen/csharp/Resources/Forfeit.txt +++ b/72_Queen/csharp/Resources/Forfeit.txt @@ -1,2 +1,3 @@ -Looks like I have won by forfeit. + +It looks like I have won by forfeit. diff --git a/72_Queen/csharp/Resources/Instructions.txt b/72_Queen/csharp/Resources/Instructions.txt index d440b335..fc2e85b0 100644 --- a/72_Queen/csharp/Resources/Instructions.txt +++ b/72_Queen/csharp/Resources/Instructions.txt @@ -13,4 +13,3 @@ We alternate moves. You may forfeit by typing '0' as your move. Be sure to press the return key after each response. - diff --git a/72_Queen/csharp/Resources/Thanks.txt b/72_Queen/csharp/Resources/Thanks.txt index 53980b09..2e2e7b63 100644 --- a/72_Queen/csharp/Resources/Thanks.txt +++ b/72_Queen/csharp/Resources/Thanks.txt @@ -1 +1,3 @@ + + Ok --- thanks again. \ No newline at end of file From 3f2dd9f0a90c86307cf2da0b9038d7021d653ae7 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sat, 28 Jan 2023 16:30:49 +1100 Subject: [PATCH 6/6] Complete game --- 72_Queen/csharp/Computer.cs | 34 +++++++ 72_Queen/csharp/Game.cs | 117 +--------------------- 72_Queen/csharp/IOExtensions.cs | 38 +++++++ 72_Queen/csharp/Move.cs | 15 +++ 72_Queen/csharp/Position.cs | 24 +++++ 72_Queen/csharp/RandomExtensions.cs | 12 +++ 72_Queen/csharp/Resources/IllegalMove.txt | 2 +- 72_Queen/csharp/Resources/Resource.cs | 5 +- 8 files changed, 131 insertions(+), 116 deletions(-) create mode 100644 72_Queen/csharp/IOExtensions.cs create mode 100644 72_Queen/csharp/Move.cs create mode 100644 72_Queen/csharp/Position.cs create mode 100644 72_Queen/csharp/RandomExtensions.cs diff --git a/72_Queen/csharp/Computer.cs b/72_Queen/csharp/Computer.cs index e69de29b..459c7337 100644 --- a/72_Queen/csharp/Computer.cs +++ b/72_Queen/csharp/Computer.cs @@ -0,0 +1,34 @@ +namespace Queen; + +internal class Computer +{ + private static readonly HashSet _randomiseFrom = new() { 41, 44, 73, 75, 126, 127 }; + private static readonly HashSet _desirable = new() { 73, 75, 126, 127, 158 }; + private readonly IRandom _random; + + public Computer(IRandom random) + { + _random = random; + } + + public Position GetMove(Position from) + => from + (_randomiseFrom.Contains(from) ? _random.NextMove() : FindMove(from)); + + private Move FindMove(Position from) + { + for (int i = 7; i > 0; i--) + { + if (IsOptimal(Move.Left, out var move)) { return move; } + if (IsOptimal(Move.Down, out move)) { return move; } + if (IsOptimal(Move.DownLeft, out move)) { return move; } + + bool IsOptimal(Move direction, out Move move) + { + move = direction * i; + return _desirable.Contains(from + move); + } + } + + return _random.NextMove(); + } +} diff --git a/72_Queen/csharp/Game.cs b/72_Queen/csharp/Game.cs index 220e4535..35b1dc9a 100644 --- a/72_Queen/csharp/Game.cs +++ b/72_Queen/csharp/Game.cs @@ -44,121 +44,14 @@ internal class Game while (true) { var computerPosition = _computer.GetMove(humanPosition); + _io.Write(Strings.ComputerMove(computerPosition)); if (computerPosition.IsEnd) { return Result.ComputerWins; } - } + humanPosition = _io.ReadPosition(Prompts.Move, p => (p - computerPosition).IsValid, Streams.IllegalMove); + if (humanPosition.IsZero) { return Result.HumanForfeits; } + if (humanPosition.IsEnd) { return Result.HumanWins; } + } } private enum Result { ComputerWins, HumanWins, HumanForfeits }; } - -internal class Computer -{ - private static readonly HashSet _randomiseFrom = new() { 41, 44, 73, 75, 126, 127 }; - private static readonly HashSet _desirable = new() { 73, 75, 126, 127, 158 }; - private readonly IRandom _random; - - public Computer(IRandom random) - { - _random = random; - } - - public Position GetMove(Position from) - => from + (_randomiseFrom.Contains(from) ? _random.NextMove() : FindMove(from)); - - private Move FindMove(Position from) - { - for (int i = 7; i > 0; i--) - { - if (IsOptimal(Move.Left, out var move)) { return move; } - if (IsOptimal(Move.Down, out move)) { return move; } - if (IsOptimal(Move.DownLeft, out move)) { return move; } - - bool IsOptimal(Move direction, out Move move) - { - move = direction * i; - return _desirable.Contains(from + move); - } - } - - return _random.NextMove(); - } -} - -internal static class IOExtensions -{ - internal static bool ReadYesNo(this IReadWrite io, string prompt) - { - while (true) - { - var answer = io.ReadString(prompt).ToLower(); - if (answer == "yes") { return true; } - if (answer == "no") { return false; } - - io.Write(Streams.YesOrNo); - } - } - - internal static Position ReadPosition( - this IReadWrite io, - string prompt, - Predicate isValid, - Stream error, - bool repeatPrompt = false) - { - while (true) - { - var response = io.ReadNumber(prompt); - var number = (int)response; - var position = new Position(number); - if (number == response && (position.IsZero || isValid(position))) - { - return position; - } - - io.Write(error); - if (!repeatPrompt) { prompt = ""; } - } - } -} - -internal record struct Position(int Diagonal, int Row) -{ - public static readonly Position Zero = new(0); - - public Position(int number) - : this(Diagonal: number / 10, Row: number % 10) - { - } - - public bool IsZero => Row == 0 && Diagonal == 0; - public bool IsStart => Row == 1 || Row == Diagonal; - public bool IsEnd => Row == 8 && Diagonal == 15; - - public override string ToString() => $"{Diagonal}{Row}"; - - public static implicit operator Position(int value) => new(value); - - public static Position operator +(Position position, Move move) - => new(Diagonal: position.Diagonal + move.Diagonal, Row: position.Row + move.Row); -} - -internal static class RandomExtensions -{ - internal static Move NextMove(this IRandom random) - => random.NextFloat() switch - { - > 0.6F => Move.Down, - > 0.3F => Move.DownLeft, - _ => Move.Left - }; -} - -internal record struct Move(int Diagonal, int Row) -{ - public static readonly Move Left = new(1, 0); - public static readonly Move DownLeft = new(2, 1); - public static readonly Move Down = new(1, 1); - - public static Move operator *(Move move, int scale) => new(move.Diagonal * scale, move.Row * scale); -} \ No newline at end of file diff --git a/72_Queen/csharp/IOExtensions.cs b/72_Queen/csharp/IOExtensions.cs new file mode 100644 index 00000000..51959967 --- /dev/null +++ b/72_Queen/csharp/IOExtensions.cs @@ -0,0 +1,38 @@ +namespace Queen; + +internal static class IOExtensions +{ + internal static bool ReadYesNo(this IReadWrite io, string prompt) + { + while (true) + { + var answer = io.ReadString(prompt).ToLower(); + if (answer == "yes") { return true; } + if (answer == "no") { return false; } + + io.Write(Streams.YesOrNo); + } + } + + internal static Position ReadPosition( + this IReadWrite io, + string prompt, + Predicate isValid, + Stream error, + bool repeatPrompt = false) + { + while (true) + { + var response = io.ReadNumber(prompt); + var number = (int)response; + var position = new Position(number); + if (number == response && (position.IsZero || isValid(position))) + { + return position; + } + + io.Write(error); + if (!repeatPrompt) { prompt = ""; } + } + } +} diff --git a/72_Queen/csharp/Move.cs b/72_Queen/csharp/Move.cs new file mode 100644 index 00000000..4e18647b --- /dev/null +++ b/72_Queen/csharp/Move.cs @@ -0,0 +1,15 @@ +namespace Queen; + +internal record struct Move(int Diagonal, int Row) +{ + public static readonly Move Left = new(1, 0); + public static readonly Move DownLeft = new(2, 1); + public static readonly Move Down = new(1, 1); + + public bool IsValid => Diagonal > 0 && (IsLeft || IsDown || IsDownLeft); + private bool IsLeft => Row == 0; + private bool IsDown => Row == Diagonal; + private bool IsDownLeft => Row * 2 == Diagonal; + + public static Move operator *(Move move, int scale) => new(move.Diagonal * scale, move.Row * scale); +} \ No newline at end of file diff --git a/72_Queen/csharp/Position.cs b/72_Queen/csharp/Position.cs new file mode 100644 index 00000000..69971163 --- /dev/null +++ b/72_Queen/csharp/Position.cs @@ -0,0 +1,24 @@ +namespace Queen; + +internal record struct Position(int Diagonal, int Row) +{ + public static readonly Position Zero = new(0); + + public Position(int number) + : this(Diagonal: number / 10, Row: number % 10) + { + } + + public bool IsZero => Row == 0 && Diagonal == 0; + public bool IsStart => Row == 1 || Row == Diagonal; + public bool IsEnd => Row == 8 && Diagonal == 15; + + public override string ToString() => $"{Diagonal}{Row}"; + + public static implicit operator Position(int value) => new(value); + + public static Position operator +(Position position, Move move) + => new(Diagonal: position.Diagonal + move.Diagonal, Row: position.Row + move.Row); + public static Move operator -(Position to, Position from) + => new(Diagonal: to.Diagonal - from.Diagonal, Row: to.Row - from.Row); +} diff --git a/72_Queen/csharp/RandomExtensions.cs b/72_Queen/csharp/RandomExtensions.cs new file mode 100644 index 00000000..b4e375fd --- /dev/null +++ b/72_Queen/csharp/RandomExtensions.cs @@ -0,0 +1,12 @@ +namespace Queen; + +internal static class RandomExtensions +{ + internal static Move NextMove(this IRandom random) + => random.NextFloat() switch + { + > 0.6F => Move.Down, + > 0.3F => Move.DownLeft, + _ => Move.Left + }; +} diff --git a/72_Queen/csharp/Resources/IllegalMove.txt b/72_Queen/csharp/Resources/IllegalMove.txt index 3d31638d..ae4a3bbc 100644 --- a/72_Queen/csharp/Resources/IllegalMove.txt +++ b/72_Queen/csharp/Resources/IllegalMove.txt @@ -1,2 +1,2 @@ -Y O U C H E A T . . . Try again +Y O U C H E A T . . . Try again \ No newline at end of file diff --git a/72_Queen/csharp/Resources/Resource.cs b/72_Queen/csharp/Resources/Resource.cs index 9ff43842..e8297eca 100644 --- a/72_Queen/csharp/Resources/Resource.cs +++ b/72_Queen/csharp/Resources/Resource.cs @@ -12,7 +12,6 @@ internal static class Resource public static Stream YesOrNo => GetStream(); public static Stream Board => GetStream(); public static Stream IllegalStart => GetStream(); - public static Stream ComputerMove => GetStream(); public static Stream IllegalMove => GetStream(); public static Stream Forfeit => GetStream(); public static Stream IWin => GetStream(); @@ -28,9 +27,9 @@ internal static class Resource public static string Anyone => GetPrompt(); } - internal static class Formats + internal static class Strings { - public static string Balance => GetString(); + public static string ComputerMove(Position position) => string.Format(GetString(), position); } private static string GetPrompt([CallerMemberName] string? name = null) => GetString($"{name}Prompt");