From db186bb86eb33f2ca9d6528c0da7c28f1337b8b9 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Tue, 13 Sep 2022 07:21:53 +1000 Subject: [PATCH] Add Generation encapsulation --- 56_Life_for_Two/csharp/Board.cs | 77 ++++++++------------------- 56_Life_for_Two/csharp/Game.cs | 32 ++++-------- 56_Life_for_Two/csharp/Generation.cs | 78 ++++++++++++++++++++++++++++ 56_Life_for_Two/csharp/Piece.cs | 8 ++- 4 files changed, 116 insertions(+), 79 deletions(-) create mode 100644 56_Life_for_Two/csharp/Generation.cs diff --git a/56_Life_for_Two/csharp/Board.cs b/56_Life_for_Two/csharp/Board.cs index a2c8e397..7886d4c2 100644 --- a/56_Life_for_Two/csharp/Board.cs +++ b/56_Life_for_Two/csharp/Board.cs @@ -1,21 +1,28 @@ +using System.Text; + namespace LifeforTwo; internal class Board { - private readonly Piece[,] _cells = new Piece[7,7]; - - private readonly Dictionary _cellCounts = new(); + private readonly Piece[,] _cells = new Piece[7, 7]; + private readonly Dictionary _cellCounts = + new() { [Piece.None] = 0, [Piece.Player1] = 0, [Piece.Player2] = 0 }; public Piece this[Coordinates coordinates] { - get => _cells[coordinates.X, coordinates.Y]; - set => _cells[coordinates.X, coordinates.Y] = value; + get => this[coordinates.X, coordinates.Y]; + set => this[coordinates.X, coordinates.Y] = value; } public Piece this[int x, int y] { get => _cells[x, y]; - set => _cells[x, y] = value; + set + { + if (!_cells[x, y].IsEmpty) { _cellCounts[_cells[x, y]] -= 1; } + _cells[x, y] = value; + _cellCounts[value] += 1; + } } public int Player1Count => _cellCounts[Piece.Player1]; @@ -23,69 +30,27 @@ internal class Board internal bool IsEmptyAt(Coordinates coordinates) => this[coordinates].IsEmpty; - public string? Result => - (Player1Count, Player2Count) switch - { - (0, 0) => Strings.Draw, - (_, 0) => string.Format(Formats.Winner, 1), - (0, _) => string.Format(Formats.Winner, 2), - _ => null - }; - - internal void ClearCell(Coordinates coordinates) => this[coordinates] = Piece.NewEmpty(); - + internal void ClearCell(Coordinates coordinates) => this[coordinates] = Piece.NewNone(); internal void AddPlayer1Piece(Coordinates coordinates) => this[coordinates] = Piece.NewPlayer1(); - internal void AddPlayer2Piece(Coordinates coordinates) => this[coordinates] = Piece.NewPlayer2(); - public void CalculateNextGeneration() + public override string ToString() { - _cellCounts[Piece.None] = _cellCounts[Piece.Player1] = _cellCounts[Piece.Player2] = 0; + var builder = new StringBuilder(); - for (var x = 1; x <= 5; x++) - { - for (var y = 1; y <= 5; y++) - { - this[x, y] = this[x, y].GetNext(); - _cellCounts[this[x, y].Value]++; - } - } - - CountNeighbours(); - } - - public void CountNeighbours() - { - for (var x = 1; x <= 5; x++) - { - for (var y = 1; y <= 5; y++) - { - var coordinates = new Coordinates(x, y); - var piece = this[coordinates]; - if (!piece.IsEmpty) - { - foreach (var neighbour in coordinates.GetNeighbors()) - { - this[neighbour] = this[neighbour].AddNeighbour(piece); - } - } - } - } - } - - public void Display(IReadWrite io) - { for (var y = 0; y <= 6; y++) { - io.WriteLine(); + builder.AppendLine(); for (var x = 0; x <= 6; x++) { - io.Write(GetDisplay(x, y)); + builder.Append(GetCellDisplay(x, y)); } } + + return builder.ToString(); } - private string GetDisplay(int x, int y) => + private string GetCellDisplay(int x, int y) => (x, y) switch { (0 or 6, _) => $" {y % 6} ", diff --git a/56_Life_for_Two/csharp/Game.cs b/56_Life_for_Two/csharp/Game.cs index c4656d43..caa613f3 100644 --- a/56_Life_for_Two/csharp/Game.cs +++ b/56_Life_for_Two/csharp/Game.cs @@ -1,54 +1,44 @@ internal class Game { private readonly IReadWrite _io; - private readonly Board _board; public Game(IReadWrite io) { _io = io; - _board = new Board(); } public void Play() { _io.Write(Streams.Title); - for (var player = 1; player <= 2; player++) - { - _io.WriteLine(Formats.InitialPieces, player); - for (var i = 1; i <= 3; i++) - { - _board[_io.ReadCoordinates(_board)] = player == 1 ? Piece.NewPlayer1() : Piece.NewPlayer2(); - } - } + var generation = Generation.Create(_io); - _board.CountNeighbours(); - _board.Display(_io); + _io.Write(generation); while(true) { - _board.CalculateNextGeneration(); + generation = generation.CalculateNextGeneration(); _io.WriteLine(); - _board.Display(_io); + _io.Write(generation); - if (_board.Result is not null) { break; } + if (generation.Result is not null) { break; } - var player1Coordinate = _io.ReadCoordinates(1, _board); - var player2Coordinate = _io.ReadCoordinates(2, _board); + var player1Coordinate = _io.ReadCoordinates(1, generation.Board); + var player2Coordinate = _io.ReadCoordinates(2, generation.Board); if (player1Coordinate == player2Coordinate) { _io.Write(Streams.SameCoords); // This is a bug existing in the original code. The line should be _board[_coordinates[_player]] = 0; - _board.ClearCell(player1Coordinate + 1); + generation.Board.ClearCell(player1Coordinate + 1); } else { - _board.AddPlayer1Piece(player1Coordinate); - _board.AddPlayer2Piece(player2Coordinate); + generation.Board.AddPlayer1Piece(player1Coordinate); + generation.Board.AddPlayer2Piece(player2Coordinate); } } - _io.WriteLine(_board.Result); + _io.WriteLine(generation.Result); } } diff --git a/56_Life_for_Two/csharp/Generation.cs b/56_Life_for_Two/csharp/Generation.cs new file mode 100644 index 00000000..1335df34 --- /dev/null +++ b/56_Life_for_Two/csharp/Generation.cs @@ -0,0 +1,78 @@ +internal class Generation +{ + private readonly Board _board; + + public Generation(Board board) + { + _board = board; + CountNeighbours(); + } + + public Board Board => _board; + + public int Player1Count => _board.Player1Count; + public int Player2Count => _board.Player2Count; + + public string? Result => + (Player1Count, Player2Count) switch + { + (0, 0) => Strings.Draw, + (_, 0) => string.Format(Formats.Winner, 1), + (0, _) => string.Format(Formats.Winner, 2), + _ => null + }; + + public static Generation Create(IReadWrite io) + { + var board = new Board(); + + SetInitialPieces(1, coord => board.AddPlayer1Piece(coord)); + SetInitialPieces(2, coord => board.AddPlayer2Piece(coord)); + + return new Generation(board); + + void SetInitialPieces(int player, Action setPiece) + { + io.WriteLine(Formats.InitialPieces, player); + for (var i = 1; i <= 3; i++) + { + setPiece(io.ReadCoordinates(board)); + } + } + } + + public Generation CalculateNextGeneration() + { + var board = new Board(); + + for (var x = 1; x <= 5; x++) + { + for (var y = 1; y <= 5; y++) + { + board[x, y] = _board[x, y].GetNext(); + } + } + + return new(board); + } + + private void CountNeighbours() + { + for (var x = 1; x <= 5; x++) + { + for (var y = 1; y <= 5; y++) + { + var coordinates = new Coordinates(x, y); + var piece = _board[coordinates]; + if (piece.IsEmpty) { continue; } + + foreach (var neighbour in coordinates.GetNeighbors()) + { + _board[neighbour] = _board[neighbour].AddNeighbour(piece); + } + } + } + } + + public override string ToString() => _board.ToString(); +} \ No newline at end of file diff --git a/56_Life_for_Two/csharp/Piece.cs b/56_Life_for_Two/csharp/Piece.cs index 545de669..20e5adba 100644 --- a/56_Life_for_Two/csharp/Piece.cs +++ b/56_Life_for_Two/csharp/Piece.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; namespace LifeforTwo; @@ -19,10 +20,10 @@ public struct Piece private Piece(int value) => _value = value; - public int Value => _value; + public int Value => _value & PieceMask; public bool IsEmpty => (_value & PieceMask) == None; - public static Piece NewEmpty() => new(None); + public static Piece NewNone() => new(None); public static Piece NewPlayer1() => new(Player1); public static Piece NewPlayer2() => new(Player2); @@ -47,4 +48,7 @@ public struct Piece Player2 => "#", _ => " " }; + + public static implicit operator Piece(int value) => new(value); + public static implicit operator int(Piece piece) => piece.Value; } \ No newline at end of file