checkmate complete
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ namespace Shogi.Domain.Pieces
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<Path> MoveSet => Player1Paths;
|
public override IEnumerable<Path> MoveSet => Owner == WhichPlayer.Player1 ? Player1Paths : Player2Paths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user