it fricken works!

This commit is contained in:
2021-03-02 19:08:10 -06:00
parent 3da187be17
commit 2e976c01e9
6 changed files with 41 additions and 32 deletions

View File

@@ -8,8 +8,8 @@ namespace Gameboard.ShogiUI.BoardState
public static readonly Vector2 Down = new Vector2(0, -1); public static readonly Vector2 Down = new Vector2(0, -1);
public static readonly Vector2 Left = new Vector2(-1, 0); public static readonly Vector2 Left = new Vector2(-1, 0);
public static readonly Vector2 Right = new Vector2(1, 0); public static readonly Vector2 Right = new Vector2(1, 0);
public static readonly Vector2 UpLeft = new Vector2(1, 1); public static readonly Vector2 UpLeft = new Vector2(-1, 1);
public static readonly Vector2 UpRight = new Vector2(-1, 1); public static readonly Vector2 UpRight = new Vector2(1, 1);
public static readonly Vector2 DownLeft = new Vector2(-1, -1); public static readonly Vector2 DownLeft = new Vector2(-1, -1);
public static readonly Vector2 DownRight = new Vector2(1, -1); public static readonly Vector2 DownRight = new Vector2(1, -1);
public static readonly Vector2 KnightLeft = new Vector2(-1, 2); public static readonly Vector2 KnightLeft = new Vector2(-1, 2);

View File

@@ -28,8 +28,7 @@ namespace Gameboard.ShogiUI.BoardState.Pieces
if (IsPromoted) clone.Promote(); if (IsPromoted) clone.Promote();
return clone; return clone;
} }
public override ICollection<Path> GetPaths() => Owner == WhichPlayer.Player1 // The move set for a King is the same regardless of orientation.
? MoveSet public override ICollection<Path> GetPaths() => MoveSet;
: MoveSet.Select(_ => new Path(Vector2.Negate(_.Direction), _.Distance)).ToList();
} }
} }

View File

@@ -15,10 +15,10 @@ namespace Gameboard.ShogiUI.BoardState
public class ShogiBoard public class ShogiBoard
{ {
private delegate void MoveSetCallback(Piece piece, Vector2 position); private delegate void MoveSetCallback(Piece piece, Vector2 position);
private ShogiBoard validationBoard; private readonly PathFinder2D<Piece> pathFinder;
public ShogiBoard validationBoard;
private Vector2 player1King; private Vector2 player1King;
private Vector2 player2King; private Vector2 player2King;
private PathFinder2D<Piece> pathFinder;
public IReadOnlyDictionary<WhichPlayer, List<Piece>> Hands { get; } public IReadOnlyDictionary<WhichPlayer, List<Piece>> Hands { get; }
public Array2D<Piece> Board { get; } public Array2D<Piece> Board { get; }
public List<Move> MoveHistory { get; } public List<Move> MoveHistory { get; }
@@ -44,15 +44,11 @@ namespace Gameboard.ShogiUI.BoardState
{ {
for (var i = 0; i < moves.Count; i++) for (var i = 0; i < moves.Count; i++)
{ {
if (!TryMove(moves[i])) if (!Move(moves[i]))
{ {
throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}."); throw new InvalidOperationException($"Unable to construct ShogiBoard with the given move at index {i}.");
} }
} }
if (EvaluateCheckAfterMove(WhoseTurn))
{
InCheck = WhoseTurn;
}
} }
private ShogiBoard(ShogiBoard toCopy) private ShogiBoard(ShogiBoard toCopy)
@@ -75,7 +71,7 @@ namespace Gameboard.ShogiUI.BoardState
public bool Move(Move move) public bool Move(Move move)
{ {
var otherPlayer = WhoseTurn == WhichPlayer.Player1 ? WhichPlayer.Player2 : WhichPlayer.Player1;
var moveSuccess = TryMove(move); var moveSuccess = TryMove(move);
if (!moveSuccess) if (!moveSuccess)
@@ -84,13 +80,10 @@ namespace Gameboard.ShogiUI.BoardState
} }
// Evaluate check // Evaluate check
if (EvaluateCheckAfterMove(WhoseTurn)) if (EvaluateCheckAfterMove(move, otherPlayer))
{ {
InCheck = WhoseTurn; InCheck = otherPlayer;
if (InCheck.HasValue) IsCheckmate = EvaluateCheckmate();
{
//IsCheckmate = EvaluateCheckmate();
}
} }
return true; return true;
} }
@@ -114,12 +107,15 @@ namespace Gameboard.ShogiUI.BoardState
validationBoard = null; validationBoard = null;
return false; return false;
} }
// Assert that this move does not put the moving player in check. // If already in check, assert the move that resulted in check no longer results in check.
if (validationBoard.EvaluateCheckAfterMove(WhoseTurn)) if (InCheck == WhoseTurn)
{
if (validationBoard.EvaluateCheckAfterMove(MoveHistory[^1], WhoseTurn))
{ {
// Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn; // Sneakily using this.WhoseTurn instead of validationBoard.WhoseTurn;
return false; return false;
} }
}
// The move is valid and legal; update board state. // The move is valid and legal; update board state.
if (move.PieceFromCaptured.HasValue) PlaceFromHand(move); if (move.PieceFromCaptured.HasValue) PlaceFromHand(move);
@@ -251,13 +247,11 @@ namespace Gameboard.ShogiUI.BoardState
Console.WriteLine(builder.ToString()); Console.WriteLine(builder.ToString());
} }
#region Rules Validation #region Rules Validation
private bool EvaluateCheckAfterMove(WhichPlayer whichPlayer) private bool EvaluateCheckAfterMove(Move move, WhichPlayer whichPlayer)
{ {
var isCheck = false; var isCheck = false;
var kingPosition = whichPlayer == WhichPlayer.Player1 ? player1King : player2King; var kingPosition = whichPlayer == WhichPlayer.Player1 ? player1King : player2King;
// Get last move.
var move = MoveHistory[^1];
// Check if the move put the king in check. // Check if the move put the king in check.
if (pathFinder.PathTo(move.To, kingPosition)) return true; if (pathFinder.PathTo(move.To, kingPosition)) return true;
@@ -327,11 +321,16 @@ namespace Gameboard.ShogiUI.BoardState
pathFinder.PathEvery(from, (other, position) => pathFinder.PathEvery(from, (other, position) =>
{ {
if (validationBoard == null) validationBoard = new ShogiBoard(this); if (validationBoard == null) validationBoard = new ShogiBoard(this);
var moveSuccess = validationBoard.TryMove(new Move { From = from, To = position }); var moveToTry = new Move { From = from, To = position };
var moveSuccess = validationBoard.TryMove(moveToTry);
if (moveSuccess) if (moveSuccess)
{
validationBoard = null;
if (!EvaluateCheckAfterMove(moveToTry, InCheck.Value))
{ {
isCheckmate = false; isCheckmate = false;
} }
}
}); });
} }
}); });

View File

@@ -1,6 +1,7 @@
using FluentAssertions; using FluentAssertions;
using Gameboard.ShogiUI.BoardState; using Gameboard.ShogiUI.BoardState;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
@@ -304,15 +305,22 @@ namespace Gameboard.ShogiUI.UnitTests.BoardState
new Move { From = new Vector2(4, 4), To = new Vector2(4, 5) }, new Move { From = new Vector2(4, 4), To = new Vector2(4, 5) },
// P2 King // P2 King
new Move { From = new Vector2(4, 8), To = new Vector2(4, 7) }, new Move { From = new Vector2(4, 8), To = new Vector2(4, 7) },
// P1 Pawn promotes // P1 Pawn promotes, threatens P2 King
new Move { From = new Vector2(4, 5), To = new Vector2(4, 6), IsPromotion = true }, new Move { From = new Vector2(4, 5), To = new Vector2(4, 6), IsPromotion = true },
// P2 King retreat // P2 King retreat
new Move { From = new Vector2(4, 7), To = new Vector2(4, 8) }, new Move { From = new Vector2(4, 7), To = new Vector2(4, 8) },
}; };
var shogi = new ShogiBoard(moves); var shogi = new ShogiBoard(moves);
Console.WriteLine("Prereq");
shogi.PrintStateAsAscii();
// Act - P1 Pawn wins by checkmate. // Act - P1 Pawn wins by checkmate.
var moveSuccess = shogi.Move(new Move { From = new Vector2(4, 6), To = new Vector2(4, 7) }); var moveSuccess = shogi.Move(new Move { From = new Vector2(4, 6), To = new Vector2(4, 7) });
Console.WriteLine("Checkmate");
shogi.PrintStateAsAscii();
shogi.Move(new Move { From = new Vector2(4, 8), To = new Vector2(4, 7) });
shogi.PrintStateAsAscii();
// Assert // Assert
moveSuccess.Should().BeTrue(); moveSuccess.Should().BeTrue();

View File

@@ -1,7 +1,9 @@
using System.Numerics; using System.Diagnostics;
using System.Numerics;
namespace PathFinding namespace PathFinding
{ {
[DebuggerDisplay("{Direction} - {Distance}")]
public class Path public class Path
{ {
public Vector2 Direction { get; } public Vector2 Direction { get; }

View File

@@ -72,19 +72,20 @@ namespace PathFinding
foreach (var path in element.GetPaths()) foreach (var path in element.GetPaths())
{ {
var shouldPath = true; var shouldPath = true;
var next = Vector2.Add(from, path.Direction); var next = Vector2.Add(from, path.Direction); ;
while (shouldPath && next.X < width && next.Y < height && next.X >= 0 && next.Y >= 0) while (shouldPath && next.X < width && next.Y < height && next.X >= 0 && next.Y >= 0)
{ {
var collider = collection[(int)next.X, (int)next.Y]; var collider = collection[(int)next.X, (int)next.Y];
if (collider != null) if (collider != null)
{ {
callback(collider, next); callback(collider, next);
shouldPath = false;
} }
next = Vector2.Add(from, path.Direction);
if (path.Distance == Distance.OneStep) if (path.Distance == Distance.OneStep)
{ {
shouldPath = false; shouldPath = false;
} }
next = Vector2.Add(next, path.Direction);
} }
} }
} }