Complete game

This commit is contained in:
drewjcooper
2023-01-28 16:30:49 +11:00
parent 3e88424a52
commit 3f2dd9f0a9
8 changed files with 131 additions and 116 deletions

View File

@@ -0,0 +1,34 @@
namespace Queen;
internal class Computer
{
private static readonly HashSet<Position> _randomiseFrom = new() { 41, 44, 73, 75, 126, 127 };
private static readonly HashSet<Position> _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();
}
}

View File

@@ -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<Position> _randomiseFrom = new() { 41, 44, 73, 75, 126, 127 };
private static readonly HashSet<Position> _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<Position> 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);
}

View File

@@ -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<Position> 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 = ""; }
}
}
}

15
72_Queen/csharp/Move.cs Normal file
View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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
};
}

View File

@@ -1,2 +1,2 @@
Y O U C H E A T . . . Try again
Y O U C H E A T . . . Try again

View File

@@ -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");