checkmate complete

This commit is contained in:
2022-06-10 21:53:05 -05:00
parent dabdb6c6b0
commit 2dcc6ca417
7 changed files with 53 additions and 66 deletions

View File

@@ -8,10 +8,10 @@ namespace Shogi.Domain.UnitTests
{ {
public class ShogiShould public class ShogiShould
{ {
private readonly ITestOutputHelper logger; private readonly ITestOutputHelper console;
public ShogiShould(ITestOutputHelper logger) public ShogiShould(ITestOutputHelper console)
{ {
this.logger = logger; this.console = console;
} }
[Fact] [Fact]
@@ -455,6 +455,7 @@ namespace Shogi.Domain.UnitTests
shogi.Move("E7", "E8", false); shogi.Move("E7", "E8", false);
// Assert - checkmate // Assert - checkmate
console.WriteLine(shogi.ToStringStateAsAscii());
boardState.IsCheckmate.Should().BeTrue(); boardState.IsCheckmate.Should().BeTrue();
boardState.InCheck.Should().Be(WhichPlayer.Player2); boardState.InCheck.Should().Be(WhichPlayer.Player2);
} }

View File

@@ -6,5 +6,14 @@ namespace Shogi.Domain
{ {
public static bool IsKing(this Piece self) => self.WhichPiece == WhichPiece.King; public static bool IsKing(this Piece self) => self.WhichPiece == WhichPiece.King;
public static bool IsBetween(this float self, float min, float max)
{
return self >= min && self <= max;
}
public static bool IsInsideBoardBoundary(this Vector2 self)
{
return self.X.IsBetween(0, 8) && self.Y.IsBetween(0, 8);
}
} }
} }

View File

@@ -29,6 +29,7 @@ namespace Shogi.Domain.Pathing
{ {
throw new ArgumentException("No paths to get nearest path from."); throw new ArgumentException("No paths to get nearest path from.");
} }
var shortestPath = paths.First(); var shortestPath = paths.First();
foreach (var path in paths.Skip(1)) foreach (var path in paths.Skip(1))
{ {

View File

@@ -26,6 +26,6 @@ namespace Shogi.Domain.Pieces
{ {
} }
public override IEnumerable<Path> MoveSet => Player1Paths; public override IEnumerable<Path> MoveSet => Owner == WhichPlayer.Player1 ? Player1Paths : Player2Paths;
} }
} }

View File

@@ -72,6 +72,8 @@ namespace Shogi.Domain.Pieces
{ {
position += path.Direction; position += path.Direction;
steps.Add(position); steps.Add(position);
if (path.Distance == Distance.OneStep) break;
} }
if (position == end) if (position == end)

View File

@@ -58,11 +58,10 @@ namespace Shogi.Domain
if (rules.IsOpponentInCheckAfterMove()) if (rules.IsOpponentInCheckAfterMove())
{ {
boardState.InCheck = otherPlayer; boardState.InCheck = otherPlayer;
// TODO: evaluate checkmate. if (rules.IsOpponentInCheckMate())
//if (rules.IsOpponentInCheckMate()) {
//{ boardState.IsCheckmate = true;
// boardState.IsCheckmate = true; }
//}
} }
else else
{ {

View File

@@ -1,4 +1,5 @@
using Shogi.Domain.Pathing; using Shogi.Domain.Pathing;
using Shogi.Domain.Pieces;
using BoardTile = System.Collections.Generic.KeyValuePair<System.Numerics.Vector2, Shogi.Domain.Pieces.Piece>; using BoardTile = System.Collections.Generic.KeyValuePair<System.Numerics.Vector2, Shogi.Domain.Pieces.Piece>;
namespace Shogi.Domain namespace Shogi.Domain
@@ -9,7 +10,7 @@ namespace Shogi.Domain
internal StandardRules(ShogiBoardState board) internal StandardRules(ShogiBoardState board)
{ {
this.boardState = board; boardState = board;
} }
/// <summary> /// <summary>
@@ -179,19 +180,17 @@ namespace Shogi.Domain
return threatenedPiece.WhichPiece == WhichPiece.King; return threatenedPiece.WhichPiece == WhichPiece.King;
} }
internal bool IsPlayerInCheckMate(WhichPlayer whichPlayer) internal bool IsOpponentInCheckMate()
{ {
// Assume checkmate, then try to disprove.
if (!boardState.InCheck.HasValue) return false; if (!boardState.InCheck.HasValue) return false;
// Get all pieces from opponent who threaten the king in question.
// Get all pieces from "other player" who threaten the king in question. var opponent = boardState.WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
var otherPlayer = whichPlayer == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1; var tilesOccupiedByOpponent = boardState.GetTilesOccupiedBy(opponent);
var tilesOccupiedByOtherPlayer = boardState.GetTilesOccupiedBy(otherPlayer); var kingPosition = boardState.WhoseTurn == WhichPlayer.Player1
var kingPosition = whichPlayer == WhichPlayer.Player1
? boardState.Player1KingPosition ? boardState.Player1KingPosition
: boardState.Player2KingPosition; : boardState.Player2KingPosition;
var threats = tilesOccupiedByOtherPlayer.Where(tile => PieceHasLineOfSight(tile, kingPosition)).ToList(); var threats = tilesOccupiedByOpponent.Where(tile => PieceHasLineOfSight(tile, kingPosition)).ToList();
if (threats.Count == 1) if (threats.Count == 1)
{ {
/* If there is exactly one threat it is possible to block the check. /* If there is exactly one threat it is possible to block the check.
@@ -200,7 +199,7 @@ namespace Shogi.Domain
*/ */
var threat = threats.Single(); var threat = threats.Single();
var pathFromThreatToKing = threat.Value.GetPathFromStartToEnd(threat.Key, kingPosition); var pathFromThreatToKing = threat.Value.GetPathFromStartToEnd(threat.Key, kingPosition);
var tilesThatCouldBlockTheThreat = boardState.GetTilesOccupiedBy(whichPlayer); var tilesThatCouldBlockTheThreat = boardState.GetTilesOccupiedBy(boardState.WhoseTurn);
foreach (var threatBlockingPosition in pathFromThreatToKing) foreach (var threatBlockingPosition in pathFromThreatToKing)
{ {
var tilesThatDoBlockThreat = tilesThatCouldBlockTheThreat var tilesThatDoBlockThreat = tilesThatCouldBlockTheThreat
@@ -218,25 +217,35 @@ namespace Shogi.Domain
/* /*
* If no ability to block the check, maybe the king can evade check by moving. * If no ability to block the check, maybe the king can evade check by moving.
*/ */
// TODO: Implement this in the Piece class instead. foreach (var maybeSafePosition in GetPossiblePositionsForKing(this.boardState.WhoseTurn))
var possibleSafePositions = new[]
{ {
Direction.Up, Direction.Down, Direction.Left, Direction.Right, Direction.UpLeft, Direction.UpRight, Direction.DownLeft, Direction.DownRight threats = tilesOccupiedByOpponent
} .Where(tile => PieceHasLineOfSight(tile, maybeSafePosition))
.Select(direction => kingPosition + direction); .ToList();
if (!threats.Any())
foreach (var maybeSafePosition in possibleSafePositions) {
{ return false;
}
} }
} }
return true;
}
private IList<Vector2> GetPossiblePositionsForKing(WhichPlayer whichPlayer)
{
var kingPosition = whichPlayer == WhichPlayer.Player1
? boardState.Player1KingPosition
: boardState.Player2KingPosition;
return false; return King.KingPaths
.Select(path => path.Direction + kingPosition)
.Where(newPosition => newPosition.IsInsideBoardBoundary())
// Where tile at position is empty, meaning the king could move there.
.Where(newPosition => boardState[newPosition] == null)
.ToList();
} }
private bool PieceHasLineOfSight(BoardTile tile, Vector2 lineOfSightTarget) private bool PieceHasLineOfSight(BoardTile tile, Vector2 lineOfSightTarget)
@@ -244,41 +253,7 @@ namespace Shogi.Domain
var path = tile.Value.GetPathFromStartToEnd(tile.Key, lineOfSightTarget); var path = tile.Value.GetPathFromStartToEnd(tile.Key, lineOfSightTarget);
return path return path
.SkipLast(1) .SkipLast(1)
.All(position => this.boardState[ShogiBoardState.ToBoardNotation(position)] == null); .All(position => boardState[ShogiBoardState.ToBoardNotation(position)] == null);
}
public bool EvaluateCheckmate_Old()
{
if (!boardState.InCheck.HasValue) return false;
// Assume true and try to disprove.
var isCheckmate = true;
//boardState.ForEachNotNull((piece, from) => // For each piece...
//{
// Short circuit
//if (!isCheckmate) return;
//if (piece.Owner == boardState.InCheck) // ...owned by the player in check...
//{
// ...evaluate if any move gets the player out of check.
//PathEvery(from, (other, position) =>
//{
// var simulationBoard = new StandardRules(new ShogiBoardState(board));
// var fromNotation = ShogiBoardState.ToBoardNotation(from);
// var toNotation = ShogiBoardState.ToBoardNotation(position);
// var simulationResult = simulationBoard.Move(fromNotation, toNotation, false);
// if (simulationResult.Success)
// {
// //if (!IsPlayerInCheckAfterMove(from, position, board.InCheck.Value))
// //{
// // isCheckmate = false;
// //}
// }
//});
//}
// TODO: Assert that a player could not place a piece from their hand to avoid check.
//});
return isCheckmate;
} }
} }
} }