|
|
|
|
@@ -1,7 +1,4 @@
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Shogi.Domain.Other;
|
|
|
|
|
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
|
|
|
|
|
|
|
|
|
|
using Shogi.Domain.YetToBeAssimilatedIntoDDD;
|
|
|
|
|
namespace Shogi.Domain.ValueObjects;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
@@ -12,6 +9,7 @@ namespace Shogi.Domain.ValueObjects;
|
|
|
|
|
public sealed class ShogiBoard
|
|
|
|
|
{
|
|
|
|
|
private readonly StandardRules rules;
|
|
|
|
|
private static readonly Vector2 BoardSize = new Vector2(9, 9);
|
|
|
|
|
|
|
|
|
|
public ShogiBoard(BoardState initialState)
|
|
|
|
|
{
|
|
|
|
|
@@ -29,239 +27,273 @@ public sealed class ShogiBoard
|
|
|
|
|
/// validate legal vs illegal moves without having to worry about reverting board state.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <exception cref="InvalidOperationException"></exception>
|
|
|
|
|
public void Move(string from, string to, bool isPromotion = false)
|
|
|
|
|
public MoveResult Move(string from, string to, bool isPromotion = false)
|
|
|
|
|
{
|
|
|
|
|
// Validate the move
|
|
|
|
|
var rulesState = new Piece?[9, 9];
|
|
|
|
|
for (int x = 0; x < 9; x++)
|
|
|
|
|
for (int y = 0; y < 9; y++)
|
|
|
|
|
{
|
|
|
|
|
rulesState[x, y] = this.BoardState[x, y];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var rules = new BoardRules<Piece>(rulesState);
|
|
|
|
|
var validationResult = rules.ValidateMove(Notation.FromBoardNotation(from), Notation.FromBoardNotation(to), isPromotion);
|
|
|
|
|
if (validationResult.IsError)
|
|
|
|
|
var moveResult = IsMoveValid(Notation.FromBoardNotation(from), Notation.FromBoardNotation(to));
|
|
|
|
|
if (!moveResult.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException(validationResult.ResultMessage);
|
|
|
|
|
return moveResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move is valid, but is it legal?
|
|
|
|
|
// Check for correct player's turn.
|
|
|
|
|
if (BoardState.WhoseTurn != BoardState[from]!.Owner)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Not allowed to move the opponent's pieces.");
|
|
|
|
|
return new MoveResult(false, "Not allowed to move the opponent's pieces.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Simulate the move on a throw -away state and look for "check" and "check-mate".
|
|
|
|
|
var simulationState = new BoardState(BoardState);
|
|
|
|
|
var moveResult = simulationState.Move(from, to, isPromotion);
|
|
|
|
|
if (!moveResult.Success)
|
|
|
|
|
// Simulate the move on a throw-away state and look for "check" and "check-mate".
|
|
|
|
|
var simState = new BoardState(BoardState);
|
|
|
|
|
moveResult = simState.Move(from, to, isPromotion);
|
|
|
|
|
if (!moveResult.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException(moveResult.Reason);
|
|
|
|
|
return moveResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var simulation = new StandardRules(simulationState);
|
|
|
|
|
// If already in check, assert the move that resulted in check no longer results in check.
|
|
|
|
|
if (BoardState.InCheck == BoardState.WhoseTurn
|
|
|
|
|
&& simulation.IsOpposingKingThreatenedByPosition(BoardState.PreviousMove.To))
|
|
|
|
|
var kings = simState.State
|
|
|
|
|
.Where(kvp => kvp.Value?.WhichPiece == WhichPiece.King)
|
|
|
|
|
.Cast<KeyValuePair<string, Piece>>()
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
if (kings.Length != 2) throw new InvalidOperationException("Unexpected scenario: board does not have two kings in play.");
|
|
|
|
|
|
|
|
|
|
// Look for threats against the kings.
|
|
|
|
|
var inCheckResult = simState.State
|
|
|
|
|
.Where(kvp => kvp.Value != null)
|
|
|
|
|
.Cast<KeyValuePair<string, Piece>>()
|
|
|
|
|
.Aggregate(InCheckResult.NobodyInCheck, (inCheckResult, kvp) =>
|
|
|
|
|
{
|
|
|
|
|
var newInCheckResult = inCheckResult;
|
|
|
|
|
var threatPiece = kvp.Value;
|
|
|
|
|
var opposingKingPosition = Notation.FromBoardNotation(kings.Single(king => king.Value.Owner != threatPiece.Owner).Key);
|
|
|
|
|
var candidatePositions = threatPiece.GetPathFromStartToEnd(Notation.FromBoardNotation(kvp.Key), opposingKingPosition);
|
|
|
|
|
|
|
|
|
|
foreach (var position in candidatePositions)
|
|
|
|
|
{
|
|
|
|
|
// No piece at this position, so pathing is unobstructed. Continue pathing.
|
|
|
|
|
if (simState[position] == null) continue;
|
|
|
|
|
|
|
|
|
|
var pieceAtPosition = simState[position]!;
|
|
|
|
|
if (pieceAtPosition.WhichPiece == WhichPiece.King && pieceAtPosition.Owner != threatPiece.Owner)
|
|
|
|
|
{
|
|
|
|
|
newInCheckResult &= pieceAtPosition.Owner == WhichPlayer.Player1 ? InCheckResult.Player2InCheck : InCheckResult.Player1InCheck;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newInCheckResult;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var playerPutThemselfInCheck = BoardState.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
? inCheckResult.HasFlag(InCheckResult.Player1InCheck)
|
|
|
|
|
: inCheckResult.HasFlag(InCheckResult.Player2InCheck);
|
|
|
|
|
|
|
|
|
|
if (playerPutThemselfInCheck)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Unable to move because you are still in check.");
|
|
|
|
|
return new MoveResult(false, "This move puts the moving player in check, which is illega.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (simulation.DidPlayerPutThemselfInCheck())
|
|
|
|
|
// Move is legal; mutate the real state.
|
|
|
|
|
BoardState.Move(from, to, isPromotion);
|
|
|
|
|
var playerPutOpponentInCheck = BoardState.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
? inCheckResult.HasFlag(InCheckResult.Player2InCheck)
|
|
|
|
|
: inCheckResult.HasFlag(InCheckResult.Player1InCheck);
|
|
|
|
|
if (playerPutOpponentInCheck)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Illegal move. This move places you in check.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var otherPlayer = BoardState.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
BoardState.InCheck = BoardState.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
? WhichPlayer.Player2
|
|
|
|
|
: WhichPlayer.Player1;
|
|
|
|
|
_ = BoardState.Move(from, to, isPromotion); // "Rules" should not be doing any data changes. "State" should do that.
|
|
|
|
|
if (rules.IsOpponentInCheckAfterMove())
|
|
|
|
|
{
|
|
|
|
|
BoardState.InCheck = otherPlayer;
|
|
|
|
|
if (rules.IsOpponentInCheckMate())
|
|
|
|
|
{
|
|
|
|
|
BoardState.IsCheckmate = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BoardState.InCheck = null;
|
|
|
|
|
}
|
|
|
|
|
BoardState.WhoseTurn = otherPlayer;
|
|
|
|
|
|
|
|
|
|
// TODO: Look for check-mate.
|
|
|
|
|
return new MoveResult(true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//var simulation = new StandardRules(simState);
|
|
|
|
|
//// If already in check, assert the move that resulted in check no longer results in check.
|
|
|
|
|
//if (BoardState.InCheck == BoardState.WhoseTurn
|
|
|
|
|
// && simulation.IsOpposingKingThreatenedByPosition(BoardState.PreviousMove.To))
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException("Unable to move because you are still in check.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//if (simulation.DidPlayerPutThemselfInCheck())
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException("Illegal move. This move places you in check.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//var otherPlayer = BoardState.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
// ? WhichPlayer.Player2
|
|
|
|
|
// : WhichPlayer.Player1;
|
|
|
|
|
//_ = BoardState.Move(from, to, isPromotion); // "Rules" should not be doing any data changes. "State" should do that.
|
|
|
|
|
//if (rules.IsOpponentInCheckAfterMove())
|
|
|
|
|
//{
|
|
|
|
|
// BoardState.InCheck = otherPlayer;
|
|
|
|
|
// if (rules.IsOpponentInCheckMate())
|
|
|
|
|
// {
|
|
|
|
|
// BoardState.IsCheckmate = true;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
//else
|
|
|
|
|
//{
|
|
|
|
|
// BoardState.InCheck = null;
|
|
|
|
|
//}
|
|
|
|
|
//BoardState.WhoseTurn = otherPlayer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Move(WhichPiece pieceInHand, string to)
|
|
|
|
|
{
|
|
|
|
|
var index = BoardState.ActivePlayerHand.FindIndex(p => p.WhichPiece == pieceInHand);
|
|
|
|
|
if (index == -1)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException($"{pieceInHand} does not exist in the hand.");
|
|
|
|
|
}
|
|
|
|
|
//var index = BoardState.ActivePlayerHand.FindIndex(p => p.WhichPiece == pieceInHand);
|
|
|
|
|
//if (index == -1)
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException($"{pieceInHand} does not exist in the hand.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
if (BoardState[to] != null)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Illegal placement of piece from the hand. Destination is not empty.");
|
|
|
|
|
}
|
|
|
|
|
//if (BoardState[to] != null)
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException("Illegal placement of piece from the hand. Destination is not empty.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
var toVector = Notation.FromBoardNotation(to);
|
|
|
|
|
switch (pieceInHand)
|
|
|
|
|
{
|
|
|
|
|
case WhichPiece.Knight:
|
|
|
|
|
{
|
|
|
|
|
// Knight cannot be placed onto the farthest two ranks from the hand.
|
|
|
|
|
if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y > 6
|
|
|
|
|
|| BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y < 2)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Illegal move. Knight has no valid moves after placement.");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case WhichPiece.Lance:
|
|
|
|
|
case WhichPiece.Pawn:
|
|
|
|
|
{
|
|
|
|
|
// Lance and Pawn cannot be placed onto the farthest rank from the hand.
|
|
|
|
|
if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y == 8
|
|
|
|
|
|| BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y == 0)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException($"Illegal move. {pieceInHand} has no valid moves after placement.");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//var toVector = Notation.FromBoardNotation(to);
|
|
|
|
|
//switch (pieceInHand)
|
|
|
|
|
//{
|
|
|
|
|
// case WhichPiece.Knight:
|
|
|
|
|
// {
|
|
|
|
|
// // Knight cannot be placed onto the farthest two ranks from the hand.
|
|
|
|
|
// if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y > 6
|
|
|
|
|
// || BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y < 2)
|
|
|
|
|
// {
|
|
|
|
|
// throw new InvalidOperationException("Illegal move. Knight has no valid moves after placement.");
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// case WhichPiece.Lance:
|
|
|
|
|
// case WhichPiece.Pawn:
|
|
|
|
|
// {
|
|
|
|
|
// // Lance and Pawn cannot be placed onto the farthest rank from the hand.
|
|
|
|
|
// if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y == 8
|
|
|
|
|
// || BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y == 0)
|
|
|
|
|
// {
|
|
|
|
|
// throw new InvalidOperationException($"Illegal move. {pieceInHand} has no valid moves after placement.");
|
|
|
|
|
// }
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
var tempBoard = new BoardState(BoardState);
|
|
|
|
|
var simulation = new StandardRules(tempBoard);
|
|
|
|
|
var moveResult = simulation.Move(pieceInHand, to);
|
|
|
|
|
if (!moveResult.Success)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException(moveResult.Reason);
|
|
|
|
|
}
|
|
|
|
|
//var tempBoard = new BoardState(BoardState);
|
|
|
|
|
//var simulation = new StandardRules(tempBoard);
|
|
|
|
|
//var moveResult = simulation.Move(pieceInHand, to);
|
|
|
|
|
//if (!moveResult.Success)
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException(moveResult.Reason);
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// If already in check, assert the move that resulted in check no longer results in check.
|
|
|
|
|
if (BoardState.InCheck == BoardState.WhoseTurn
|
|
|
|
|
&& simulation.IsOpposingKingThreatenedByPosition(BoardState.PreviousMove.To))
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Unable to drop piece becauase you are still in check.");
|
|
|
|
|
}
|
|
|
|
|
//// If already in check, assert the move that resulted in check no longer results in check.
|
|
|
|
|
//if (BoardState.InCheck == BoardState.WhoseTurn
|
|
|
|
|
// && simulation.IsOpposingKingThreatenedByPosition(BoardState.PreviousMove.To))
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException("Unable to drop piece becauase you are still in check.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
if (simulation.DidPlayerPutThemselfInCheck())
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Illegal move. This move places you in check.");
|
|
|
|
|
}
|
|
|
|
|
//if (simulation.DidPlayerPutThemselfInCheck())
|
|
|
|
|
//{
|
|
|
|
|
// throw new InvalidOperationException("Illegal move. This move places you in check.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// Update the non-simulation board.
|
|
|
|
|
var otherPlayer = tempBoard.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
? WhichPlayer.Player2
|
|
|
|
|
: WhichPlayer.Player1;
|
|
|
|
|
_ = rules.Move(pieceInHand, to);
|
|
|
|
|
if (rules.IsOpponentInCheckAfterMove())
|
|
|
|
|
{
|
|
|
|
|
BoardState.InCheck = otherPlayer;
|
|
|
|
|
// A pawn, placed from the hand, cannot be the cause of checkmate.
|
|
|
|
|
if (rules.IsOpponentInCheckMate() && pieceInHand != WhichPiece.Pawn)
|
|
|
|
|
{
|
|
|
|
|
BoardState.IsCheckmate = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//// Update the non-simulation board.
|
|
|
|
|
//var otherPlayer = tempBoard.WhoseTurn == WhichPlayer.Player1
|
|
|
|
|
// ? WhichPlayer.Player2
|
|
|
|
|
// : WhichPlayer.Player1;
|
|
|
|
|
//_ = rules.Move(pieceInHand, to);
|
|
|
|
|
//if (rules.IsOpponentInCheckAfterMove())
|
|
|
|
|
//{
|
|
|
|
|
// BoardState.InCheck = otherPlayer;
|
|
|
|
|
// // A pawn, placed from the hand, cannot be the cause of checkmate.
|
|
|
|
|
// if (rules.IsOpponentInCheckMate() && pieceInHand != WhichPiece.Pawn)
|
|
|
|
|
// {
|
|
|
|
|
// BoardState.IsCheckmate = true;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
var kingPosition = otherPlayer == WhichPlayer.Player1
|
|
|
|
|
? tempBoard.Player1KingPosition
|
|
|
|
|
: tempBoard.Player2KingPosition;
|
|
|
|
|
BoardState.WhoseTurn = otherPlayer;
|
|
|
|
|
//var kingPosition = otherPlayer == WhichPlayer.Player1
|
|
|
|
|
// ? tempBoard.Player1KingPosition
|
|
|
|
|
// : tempBoard.Player2KingPosition;
|
|
|
|
|
//BoardState.WhoseTurn = otherPlayer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Prints a ASCII representation of the board for debugging board state.
|
|
|
|
|
/// The purpose is to ensure a proposed board move is valid with regard to the moved piece's rules.
|
|
|
|
|
/// This event does not worry about check or check-mate, or if a move is legal according to all Shogi rules.
|
|
|
|
|
/// It asserts that a proposed move is possible and worthy of further validation (check, check-mate, etc).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public string ToStringStateAsAscii()
|
|
|
|
|
private MoveResult IsMoveValid(Vector2 from, Vector2 to)
|
|
|
|
|
{
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
|
builder.Append(" ");
|
|
|
|
|
builder.Append("Player 2");
|
|
|
|
|
builder.AppendLine();
|
|
|
|
|
for (var rank = 8; rank >= 0; rank--)
|
|
|
|
|
if (IsWithinBounds(from) && IsWithinBounds(to))
|
|
|
|
|
{
|
|
|
|
|
// Horizontal line
|
|
|
|
|
builder.Append(" - ");
|
|
|
|
|
for (var file = 0; file < 8; file++) builder.Append("- - ");
|
|
|
|
|
builder.Append("- -");
|
|
|
|
|
|
|
|
|
|
// Print Rank ruler.
|
|
|
|
|
builder.AppendLine();
|
|
|
|
|
builder.Append($"{rank + 1} ");
|
|
|
|
|
|
|
|
|
|
// Print pieces.
|
|
|
|
|
builder.Append(" |");
|
|
|
|
|
for (var x = 0; x < 9; x++)
|
|
|
|
|
if (BoardState[to]?.WhichPiece == WhichPiece.King)
|
|
|
|
|
{
|
|
|
|
|
var piece = BoardState[x, rank];
|
|
|
|
|
if (piece == null)
|
|
|
|
|
{
|
|
|
|
|
builder.Append(" ");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
builder.AppendFormat("{0}", ToAscii(piece));
|
|
|
|
|
}
|
|
|
|
|
builder.Append('|');
|
|
|
|
|
return new MoveResult(false, "Kings may not be captured.");
|
|
|
|
|
}
|
|
|
|
|
builder.AppendLine();
|
|
|
|
|
|
|
|
|
|
var piece = BoardState[from];
|
|
|
|
|
if (piece == null)
|
|
|
|
|
{
|
|
|
|
|
return new MoveResult(false, $"There is no piece at position {from}.");
|
|
|
|
|
}
|
|
|
|
|
var matchingPaths = piece.MoveSet.Where(p => p.NormalizedStep == Vector2.Normalize(to - from));
|
|
|
|
|
if (!matchingPaths.Any())
|
|
|
|
|
{
|
|
|
|
|
return new MoveResult(false, "Piece cannot move like that.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var multiStepPaths = matchingPaths.Where(path => path.Distance == YetToBeAssimilatedIntoDDD.Pathing.Distance.MultiStep).ToArray();
|
|
|
|
|
foreach (var path in multiStepPaths)
|
|
|
|
|
{
|
|
|
|
|
// Assert that no pieces exist along the from -> to path.
|
|
|
|
|
var isPathObstructed = GetPositionsAlongPath(from, to, path)
|
|
|
|
|
.Any(pos => BoardState[pos] != null);
|
|
|
|
|
if (isPathObstructed)
|
|
|
|
|
{
|
|
|
|
|
return new MoveResult(false, "Piece cannot move through other pieces.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pieceAtTo = BoardState[to];
|
|
|
|
|
if (pieceAtTo?.Owner == piece.Owner)
|
|
|
|
|
{
|
|
|
|
|
return new MoveResult(false, "Cannot capture your own pieces.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Horizontal line
|
|
|
|
|
builder.Append(" - ");
|
|
|
|
|
for (var x = 0; x < 8; x++) builder.Append("- - ");
|
|
|
|
|
builder.Append("- -");
|
|
|
|
|
builder.AppendLine();
|
|
|
|
|
builder.Append(" ");
|
|
|
|
|
builder.Append("Player 1");
|
|
|
|
|
|
|
|
|
|
builder.AppendLine();
|
|
|
|
|
builder.AppendLine();
|
|
|
|
|
// Print File ruler.
|
|
|
|
|
builder.Append(" ");
|
|
|
|
|
builder.Append(" A B C D E F G H I ");
|
|
|
|
|
|
|
|
|
|
return builder.ToString();
|
|
|
|
|
return new MoveResult(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
///
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="piece"></param>
|
|
|
|
|
/// <returns>
|
|
|
|
|
/// A string with three characters.
|
|
|
|
|
/// The first character indicates promotion status.
|
|
|
|
|
/// The second character indicates piece.
|
|
|
|
|
/// The third character indicates ownership.
|
|
|
|
|
/// </returns>
|
|
|
|
|
private static string ToAscii(Piece piece)
|
|
|
|
|
private static IEnumerable<Vector2> GetPositionsAlongPath(Vector2 from, Vector2 to, YetToBeAssimilatedIntoDDD.Pathing.Path path)
|
|
|
|
|
{
|
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
|
if (piece.IsPromoted) builder.Append('^');
|
|
|
|
|
else builder.Append(' ');
|
|
|
|
|
|
|
|
|
|
var name = piece.WhichPiece switch
|
|
|
|
|
var next = from;
|
|
|
|
|
while (next != to && next.X >= 0 && next.X < 9 && next.Y >= 0 && next.Y < 9)
|
|
|
|
|
{
|
|
|
|
|
WhichPiece.King => "K",
|
|
|
|
|
WhichPiece.GoldGeneral => "G",
|
|
|
|
|
WhichPiece.SilverGeneral => "S",
|
|
|
|
|
WhichPiece.Bishop => "B",
|
|
|
|
|
WhichPiece.Rook => "R",
|
|
|
|
|
WhichPiece.Knight => "k",
|
|
|
|
|
WhichPiece.Lance => "L",
|
|
|
|
|
WhichPiece.Pawn => "P",
|
|
|
|
|
_ => throw new ArgumentException($"Unknown value for {nameof(WhichPiece)}."),
|
|
|
|
|
};
|
|
|
|
|
builder.Append(name);
|
|
|
|
|
next += path.Step;
|
|
|
|
|
yield return next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (piece.Owner == WhichPlayer.Player2) builder.Append('.');
|
|
|
|
|
else builder.Append(' ');
|
|
|
|
|
|
|
|
|
|
return builder.ToString();
|
|
|
|
|
private static bool IsWithinBounds(Vector2 position)
|
|
|
|
|
{
|
|
|
|
|
var isPositive = position - position == Vector2.Zero;
|
|
|
|
|
return isPositive && position.X <= BoardSize.X && position.Y <= BoardSize.Y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|