2
\$\begingroup\$

The next post in my Checker series: The C# wrapper for my F# library to make it cleaner for these methods to be called from C# code.

GeneralTypes.cs

public enum Player
{
    White, Black
}

public enum PieceType
{
    Checker,
    King
}

public static class Extensions
{
    public static Player Convert(this Checkers.Types.Player value) =>
        Equals(value, Checkers.Types.Player.Black) ? Player.Black : Player.White;

    public static Checkers.Types.Player ConvertBack(this Player value) =>
        value == Player.Black ? Checkers.Types.Player.Black : Checkers.Types.Player.White;

    public static PieceType Convert(this Checkers.Types.PieceType value) =>
        Equals(value, Checkers.Types.PieceType.Checker) ? PieceType.Checker : PieceType.King;

    public static Checkers.Types.PieceType ConvertBack(this PieceType value) =>
        value == PieceType.Checker ? Checkers.Types.PieceType.Checker : Checkers.Types.PieceType.King;

    public static Piece Convert(this FSharpOption<Checkers.Piece.Piece> piece)
    {
        if (Equals(piece, Checkers.Piece.whiteChecker))
        {
            return Piece.WhiteChecker;
        }
        if (Equals(piece, Checkers.Piece.whiteKing))
        {
            return Piece.WhiteKing;
        }
        if (Equals(piece, Checkers.Piece.blackChecker))
        {
            return Piece.BlackChecker;
        }
        if (Equals(piece, Checkers.Piece.blackKing))
        {
            return Piece.BlackKing;
        }

        return null;
    }

    public static FSharpOption<Checkers.Piece.Piece> ConvertBack(this Piece piece)
    {
        if (Equals(piece, Piece.WhiteChecker))
        {
            return Checkers.Piece.whiteChecker;
        }
        if (Equals(piece, Piece.WhiteKing))
        {
            return Checkers.Piece.whiteKing;
        }
        if (Equals(piece, Piece.BlackChecker))
        {
            return Checkers.Piece.blackChecker;
        }
        if (Equals(piece, Piece.BlackKing))
        {
            return Checkers.Piece.blackKing;
        }

        return null;
    }
}

Coord.cs

public class Coord
{
    public int Row { get; }
    public int Column { get; }

    public Coord(int row, int column)
    {
        Row = row;
        Column = column;
    }

    public static Coord operator +(Coord coord1, Coord cooord2)
    {
        return new Coord(coord1.Row + cooord2.Row, coord1.Column + cooord2.Column);
    }

    public static implicit operator Coord(Checkers.Types.Coord coord)
    {
        return new Coord(coord.Row, coord.Column);
    }

    public static implicit operator Checkers.Types.Coord(Coord coord)
    {
        return new Checkers.Types.Coord(coord.Row, coord.Column);
    }

    public static implicit operator Coord(FSharpOption<Checkers.Types.Coord> coord)
    {
        return Equals(coord, FSharpOption<Checkers.Types.Coord>.None)
            ? null
            : new Coord(coord.Value.Row, coord.Value.Column);
    }

    public static implicit operator FSharpOption<Checkers.Types.Coord>(Coord coord)
    {
        return coord == null
            ? FSharpOption<Checkers.Types.Coord>.None
            : FSharpOption<Checkers.Types.Coord>.Some(new Checkers.Types.Coord(coord.Row, coord.Column));
    }
}

Piece.cs

public class Piece
{
    public Player Player { get; }
    public PieceType PieceType { get; }

    public Piece(Player player, PieceType pieceType)
    {
        Player = player;
        PieceType = pieceType;
    }

    public static Piece WhiteChecker =>
        new Piece(Player.White, PieceType.Checker);

    public static Piece WhiteKing =>
        new Piece(Player.White, PieceType.King);

    public static Piece BlackChecker =>
        new Piece(Player.Black, PieceType.Checker);

    public static Piece BlackKing =>
        new Piece(Player.Black, PieceType.King);

    public override bool Equals(object obj)
    {
        if (obj == null || typeof(Piece) != obj.GetType())
        {
            return false;
        }

        var value = (Piece)obj;
        return Player.Equals(value.Player) &&
               PieceType.Equals(value.PieceType);
    }

    public override int GetHashCode() => Player.GetHashCode() ^ PieceType.GetHashCode();
}

Board.cs

public class Board
{
    public List<List<Piece>> GameBoard { get; }

    public Board(IEnumerable<IEnumerable<FSharpOption<Checkers.Piece.Piece>>> board)
    {
        GameBoard = board.Select(row => row.Select(piece => piece.Convert()).ToList()).ToList();
    }

    public Board() : this(Checkers.Board.defaultBoard) { }

    public Piece this[Coord coord] => GameBoard[coord.Row][coord.Column];

    public static implicit operator Board(FSharpList<FSharpList<FSharpOption<Checkers.Piece.Piece>>> value)
    {
        return new Board(value);
    }

    public static implicit operator FSharpList<FSharpList<FSharpOption<Checkers.Piece.Piece>>>(Board value)
    {
        return Checkers.Board.listFromSeq(value.GameBoard.Select(row => row.Select(piece => piece.ConvertBack())));
    }
}

GameController.cs

public class GameController : INotifyPropertyChanged
{
    public GameController(Board board, Player currentPlayer, Coord currentCoord = null)
    {
        Board = board;
        CurrentPlayer = currentPlayer;
        CurrentCoord = currentCoord;
    }

    public Player CurrentPlayer { get; }
    public Coord CurrentCoord { get; }

    private Board _board;
    public Board Board
    {
        get { return _board; }
        set
        {
            Debug.Assert(value.GameBoard != null);
            _board = value;
            OnPropertyChanged();
        }
    }

    public GameController(Checkers.GameController.GameController gameController)
        :this(gameController.Board, gameController.CurrentPlayer.Convert(), gameController.CurrentCoord) { }

    public GameController()
        : this(new Board(), Player.Black) { }

    public GameController Move(Coord startCoord, Coord endCoord) =>
        Checkers.PublicAPI.movePiece(startCoord, endCoord, this);

    public GameController Move(IEnumerable<Coord> moves) =>
        Checkers.PublicAPI.move(moves.Select(item => (Checkers.Types.Coord)item), this);

    public bool IsValidMove(Coord startCoord, Coord endCoord) =>
        Checkers.PublicAPI.isValidMove(startCoord, endCoord, this);

    public IEnumerable<Coord> GetMove(int searchDepth) =>
        Checkers.PublicAPI.getMove(searchDepth, this).Select(coord => (Coord)coord);

    public Player? GetWinningPlayer()
    {
        var player = Checkers.PublicAPI.isWon(this);
        return Equals(player, FSharpOption<Checkers.Types.Player>.None) ? new Player?() : player.Value.Convert();
    }

    public static implicit operator GameController(Checkers.GameController.GameController controller)
    {
        return new GameController(controller);
    }

    public static implicit operator Checkers.GameController.GameController(GameController controller)
    {
        return new Checkers.GameController.GameController(controller.Board, controller.CurrentPlayer.ConvertBack(),
            controller.CurrentCoord);
    }

    public static implicit operator GameController(FSharpOption<Checkers.GameController.GameController> controller)
    {
        return Equals(controller, FSharpOption<Checkers.GameController.GameController>.None)
            ? null
            : new GameController(controller.Value);
    }

    public static implicit operator FSharpOption<Checkers.GameController.GameController>(GameController controller)
    {
        return controller == null
            ? FSharpOption<Checkers.GameController.GameController>.None
            : FSharpOption<Checkers.GameController.GameController>.Some(
                new Checkers.GameController.GameController(controller.Board, controller.CurrentPlayer.ConvertBack(),
                    controller.CurrentCoord));
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
\$\endgroup\$
2
\$\begingroup\$

Piece.cs

Why do you need to create a new instance of piece and return it each time when public static properties of Piece class are retrieved?
I'd prefer to declare them as static readonly fields or static get-only properties as follows:

public static Piece WhiteChecker { get; } =
    new Piece(Player.White, PieceType.Checker);

public static Piece WhiteKing { get; } =
    new Piece(Player.White, PieceType.King);

public static Piece BlackChecker { get; } =
    new Piece(Player.Black, PieceType.Checker);

public static Piece BlackKing { get; } =
    new Piece(Player.Black, PieceType.King);

Consider to make your classes sealed if they don't intended for inheritance.
In your current implementation it's (hypothetically) possible that the following condition returns true for Piece object:

if (obj == null || typeof(Piece) != obj.GetType())

Reason: for some object of type that derived from Piece, obj.GetType() returns a type not equal to the Piece.

Another way is to compare types taking into account inheritance:

if (!typeof(Piece).IsInstanceOfType(obj))

Why not implement the IEquatable<Piece> interface in the Piece class? It will eliminate unnecessary boxing when you call the Equals method to compare two Pieces:

public class Piece : IEquatable<Piece>
{
    ... 

    public override bool Equals(object obj)
    {
        return Equals(obj as Piece);
    }

    public bool Equals(Piece other)
    {
        return other != null
            && Player.Equals(other.Player)
            && PieceType.Equals(other.PieceType);
    }

    ...
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.