Fix Hand css.

Implement placing from hand.
This commit is contained in:
2024-10-25 23:46:14 -05:00
parent 7d47fafea0
commit 550942281b
4 changed files with 105 additions and 119 deletions

View File

@@ -13,7 +13,6 @@ public class EmailSender : IEmailSender
private readonly HttpClient client; private readonly HttpClient client;
private string apiKey; private string apiKey;
public EmailSender(HttpClient client, IOptionsMonitor<ApiKeys> apiKeys) public EmailSender(HttpClient client, IOptionsMonitor<ApiKeys> apiKeys)
{ {
this.apiKey = apiKeys.CurrentValue.BrevoEmailService; this.apiKey = apiKeys.CurrentValue.BrevoEmailService;

View File

@@ -8,7 +8,7 @@ internal enum InCheckResult
Player2InCheck = 4 Player2InCheck = 4
} }
internal enum GameOverResult public enum GameOverResult
{ {
GameIsNotOver, GameIsNotOver,
Player1Wins, Player1Wins,

View File

@@ -9,7 +9,7 @@ namespace Shogi.Domain.ValueObjects;
public sealed class ShogiBoard public sealed class ShogiBoard
{ {
private readonly StandardRules rules; private readonly StandardRules rules;
private static readonly Vector2 BoardSize = new Vector2(9, 9); private static readonly Vector2 BoardSize = new(9, 9);
public ShogiBoard(BoardState initialState) public ShogiBoard(BoardState initialState)
{ {
@@ -19,6 +19,8 @@ public sealed class ShogiBoard
public BoardState BoardState { get; } public BoardState BoardState { get; }
private static readonly int[] zeroToEight = [0, 1, 2, 3, 4, 5, 6, 7, 8];
/// <summary> /// <summary>
/// Move a piece from a board position to another board position, potentially capturing an opponents piece. Respects all rules of the game. /// Move a piece from a board position to another board position, potentially capturing an opponents piece. Respects all rules of the game.
/// </summary> /// </summary>
@@ -27,7 +29,7 @@ public sealed class ShogiBoard
/// validate legal vs illegal moves without having to worry about reverting board state. /// validate legal vs illegal moves without having to worry about reverting board state.
/// </remarks> /// </remarks>
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
public MoveResult Move(string from, string to, bool isPromotion = false) public MoveResult Move(string from, string to, bool isPromotion)
{ {
// Validate the move // Validate the move
var moveResult = IsMoveValid(Notation.FromBoardNotation(from), Notation.FromBoardNotation(to)); var moveResult = IsMoveValid(Notation.FromBoardNotation(from), Notation.FromBoardNotation(to));
@@ -51,15 +53,8 @@ public sealed class ShogiBoard
return moveResult; return moveResult;
} }
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. // Look for threats against the kings.
InCheckResult inCheckResult = IsEitherPlayerInCheck(simState, kings); InCheckResult inCheckResult = IsEitherPlayerInCheck(simState);
var playerPutThemselfInCheck = BoardState.WhoseTurn == WhichPlayer.Player1 var playerPutThemselfInCheck = BoardState.WhoseTurn == WhichPlayer.Player1
? inCheckResult.HasFlag(InCheckResult.Player1InCheck) ? inCheckResult.HasFlag(InCheckResult.Player1InCheck)
@@ -67,7 +62,7 @@ public sealed class ShogiBoard
if (playerPutThemselfInCheck) if (playerPutThemselfInCheck)
{ {
return new MoveResult(false, "This move puts the moving player in check, which is illega."); return new MoveResult(false, "This move puts the moving player in check, which is illegal.");
} }
var playerPutOpponentInCheck = BoardState.WhoseTurn == WhichPlayer.Player1 var playerPutOpponentInCheck = BoardState.WhoseTurn == WhichPlayer.Player1
? inCheckResult.HasFlag(InCheckResult.Player2InCheck) ? inCheckResult.HasFlag(InCheckResult.Player2InCheck)
@@ -82,105 +77,93 @@ public sealed class ShogiBoard
: WhichPlayer.Player1; : WhichPlayer.Player1;
} }
// TODO: Look for check-mate.
return new MoveResult(true); 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) public MoveResult Move(WhichPiece pieceInHand, string to)
{ {
//var index = BoardState.ActivePlayerHand.FindIndex(p => p.WhichPiece == pieceInHand); var index = BoardState.ActivePlayerHand.FindIndex(p => p.WhichPiece == pieceInHand);
//if (index == -1) if (index == -1)
//{ {
// throw new InvalidOperationException($"{pieceInHand} does not exist in the hand."); return new MoveResult(false, $"{pieceInHand} does not exist in the hand.");
//} }
//if (BoardState[to] != null) if (BoardState[to] != null)
//{ {
// throw new InvalidOperationException("Illegal placement of piece from the hand. Destination is not empty."); return new MoveResult(false, $"Tried to play a piece from the hand to an occupied position.");
//} }
//var toVector = Notation.FromBoardNotation(to); var toVector = Notation.FromBoardNotation(to);
//switch (pieceInHand) switch (pieceInHand)
//{ {
// case WhichPiece.Knight: case WhichPiece.Knight:
// { {
// // Knight cannot be placed onto the farthest two ranks from the hand. // Knight cannot be placed onto the farthest two ranks from the hand.
// if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y > 6 if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y > 6
// || BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y < 2) || BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y < 2)
// { {
// throw new InvalidOperationException("Illegal move. Knight has no valid moves after placement."); return new MoveResult(false, "Illegal move. Knight has no valid moves after placement.");
// } }
// break; break;
// } }
// case WhichPiece.Lance: case WhichPiece.Lance:
// case WhichPiece.Pawn: case WhichPiece.Pawn:
// { {
// // Lance and Pawn cannot be placed onto the farthest rank from the hand. // Lance and Pawn cannot be placed onto the farthest rank from the hand.
// if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y == 8 if (BoardState.WhoseTurn == WhichPlayer.Player1 && toVector.Y == 8
// || BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y == 0) || BoardState.WhoseTurn == WhichPlayer.Player2 && toVector.Y == 0)
// { {
// throw new InvalidOperationException($"Illegal move. {pieceInHand} has no valid moves after placement."); return new MoveResult(false, $"Illegal move. {pieceInHand} has no valid moves after placement.");
// } }
// break; break;
// } }
//} }
//var tempBoard = new BoardState(BoardState); if (pieceInHand == WhichPiece.Pawn)
//var simulation = new StandardRules(tempBoard); {
//var moveResult = simulation.Move(pieceInHand, to); // Pawns cannot be placed into a column with another unpromoted pawn controlled by the moving player.
//if (!moveResult.Success) var columnAlreadyHasPawn = zeroToEight
//{ .Select(y => BoardState[new Vector2(toVector.X, y)])
// throw new InvalidOperationException(moveResult.Reason); .Where(piece => piece?.WhichPiece == WhichPiece.Pawn)
//} .Where(piece => piece?.Owner == BoardState.WhoseTurn)
.Where(piece => piece?.IsPromoted == false)
.Any();
//// If already in check, assert the move that resulted in check no longer results in check. if (columnAlreadyHasPawn)
//if (BoardState.InCheck == BoardState.WhoseTurn {
// && simulation.IsOpposingKingThreatenedByPosition(BoardState.PreviousMove.To)) return new MoveResult(false, "A player may not have two unpromoted pawns in the same file.");
//{ }
// throw new InvalidOperationException("Unable to drop piece becauase you are still in check."); }
//}
//if (simulation.DidPlayerPutThemselfInCheck()) var simState = new BoardState(BoardState);
//{ var moveResult = simState.Move(pieceInHand, to);
// throw new InvalidOperationException("Illegal move. This move places you in check."); if (!moveResult.IsSuccess)
//} {
return moveResult;
}
var inCheckResult = IsEitherPlayerInCheck(simState);
var playerPutThemselfInCheck = BoardState.WhoseTurn == WhichPlayer.Player1
? inCheckResult.HasFlag(InCheckResult.Player1InCheck)
: inCheckResult.HasFlag(InCheckResult.Player2InCheck);
if (playerPutThemselfInCheck)
{
return new MoveResult(false, "This move puts the moving player in check, which is illegal.");
}
var playerPutOpponentInCheck = BoardState.WhoseTurn == WhichPlayer.Player1
? inCheckResult.HasFlag(InCheckResult.Player2InCheck)
: inCheckResult.HasFlag(InCheckResult.Player1InCheck);
// Move is legal; mutate the real state.
BoardState.Move(pieceInHand, to);
if (playerPutOpponentInCheck)
{
BoardState.InCheck = BoardState.WhoseTurn == WhichPlayer.Player1
? WhichPlayer.Player2
: WhichPlayer.Player1;
}
//// Update the non-simulation board.
//var otherPlayer = tempBoard.WhoseTurn == WhichPlayer.Player1
// ? WhichPlayer.Player2
// : WhichPlayer.Player1;
//_ = rules.Move(pieceInHand, to);
//if (rules.IsOpponentInCheckAfterMove()) //if (rules.IsOpponentInCheckAfterMove())
//{ //{
// BoardState.InCheck = otherPlayer; // BoardState.InCheck = otherPlayer;
@@ -191,20 +174,24 @@ public sealed class ShogiBoard
// } // }
//} //}
//var kingPosition = otherPlayer == WhichPlayer.Player1 return new MoveResult(true);
// ? tempBoard.Player1KingPosition
// : tempBoard.Player2KingPosition;
//BoardState.WhoseTurn = otherPlayer;
} }
public GameOverResult EvaluateGameOver() public GameOverResult EvaluateGameOver()
{ {
return GameOverResult.GameIsNotOver;
} }
private static InCheckResult IsEitherPlayerInCheck(BoardState simState, KeyValuePair<string, Piece>[] kings) private static InCheckResult IsEitherPlayerInCheck(BoardState simState)
{ {
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.");
return simState.State return simState.State
.Where(kvp => kvp.Value != null) .Where(kvp => kvp.Value != null)
.Cast<KeyValuePair<string, Piece>>() .Cast<KeyValuePair<string, Piece>>()

View File

@@ -59,15 +59,15 @@
} }
.board .tile { .tile {
display: grid; display: grid;
place-content: center; place-content: center;
aspect-ratio: var(--ratio); aspect-ratio: var(--ratio);
background-color: beige; background-color: beige;
transition: filter linear 0.25s; transition: filter linear 0.25s;
} }
.board .tile[data-selected] { .tile[data-selected] {
filter: invert(0.8); filter: invert(0.8);
} }