91 lines
2.3 KiB
C#
91 lines
2.3 KiB
C#
using System.Numerics;
|
|
|
|
namespace Shogi.Domain
|
|
{
|
|
/// <summary>
|
|
/// Facilitates Shogi board state transitions, cognisant of Shogi rules.
|
|
/// The board is always from Player1's perspective.
|
|
/// [0,0] is the lower-left position, [8,8] is the higher-right position
|
|
/// </summary>
|
|
public sealed class Shogi
|
|
{
|
|
private readonly ShogiBoardState board;
|
|
private readonly StandardRules rules;
|
|
public string Error { get; private set; }
|
|
|
|
public Shogi(ShogiBoardState board)
|
|
{
|
|
this.board = board;
|
|
rules = new StandardRules(this.board);
|
|
Error = string.Empty;
|
|
}
|
|
|
|
public Shogi(IList<Move> moves) : this()
|
|
{
|
|
for (var i = 0; i < moves.Count; i++)
|
|
{
|
|
if (!Move(moves[i]))
|
|
{
|
|
// Todo: Add some smarts to know why a move was invalid. In check? Piece not found? etc.
|
|
throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}. {Error}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Move(Move move)
|
|
{
|
|
var moveSuccess = TryMove(move);
|
|
|
|
if (!moveSuccess)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var otherPlayer = WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
|
|
if (EvaluateCheckAfterMove(move, otherPlayer))
|
|
{
|
|
InCheck = otherPlayer;
|
|
IsCheckmate = EvaluateCheckmate();
|
|
}
|
|
else
|
|
{
|
|
InCheck = null;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts a given move. Returns false if the move is illegal.
|
|
/// </summary>
|
|
private bool TryMove(Move move)
|
|
{
|
|
// Try making the move in a "throw away" board.
|
|
var simulator = new StandardRules(new ShogiBoardState(this.board));
|
|
|
|
var simulatedMoveResults = move.PieceFromHand.HasValue
|
|
? simulator.PlaceFromHand(move)
|
|
: simulator.PlaceFromBoard(move);
|
|
if (!simulatedMoveResults)
|
|
{
|
|
// Surface the error description.
|
|
Error = simulationBoard.Error;
|
|
return false;
|
|
}
|
|
// If already in check, assert the move that resulted in check no longer results in check.
|
|
if (InCheck == WhoseTurn)
|
|
{
|
|
if (simulationBoard.EvaluateCheckAfterMove(MoveHistory[^1], WhoseTurn))
|
|
{
|
|
// Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// The move is valid and legal; update board state.
|
|
if (move.PieceFromHand.HasValue) PlaceFromHand(move);
|
|
else PlaceFromBoard(move);
|
|
return true;
|
|
}
|
|
}
|
|
}
|