it fricken works!
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user