private int Evaluate(HexAIMove move, Game game) { int score = random.Next(0, 25); // avoid making every run the same var state = game.GetCurrentBoardState(); Team ourTeam = state.currentMove; Team enemy = ourTeam.Enemy(); (BoardState newState, List <Promotion> newPromotions) = move.Speculate(game); bool weAreChecking = MoveValidator.IsChecking(ourTeam, newState, newPromotions); bool enemyHasMoves = MoveValidator.HasAnyValidMoves(enemy, newPromotions, newState, state); if (weAreChecking && !enemyHasMoves) { return(int.MaxValue); } if (weAreChecking) { score += CheckBonus; } if (!enemyHasMoves) { score -= StalematePenalty; } if (move.moveType == MoveType.Attack) { var attacker = HexachessagonEngine.GetRealPiece(move.start, state, game.promotions); var victim = HexachessagonEngine.GetRealPiece(move.target, state, game.promotions); score += AttackBonus; score += GetPieceValue(victim) - GetPieceValue(attacker); } else if (move.moveType == MoveType.EnPassant) { score += EnPassantBonus; } else if (move.moveType == MoveType.Move) { var mover = state.allPiecePositions[move.start]; if (mover.piece.IsPawn()) { int ranksForward = move.target.GetNumber() - move.start.GetNumber(); if (ourTeam == Team.Black) { ranksForward = -ranksForward; } ranksForward *= 5; score *= ranksForward; } } else { // Defend is pretty worthless for a bloodthirsty AI score -= 10; } return(score); }
/// <summary> /// Is a piece from <paramref name="checkForTeam"/> attacking the enemy king? /// </summary> /// <param name="checkForTeam"></param> /// <returns>true if the enemy king is threatened</returns> public static bool IsChecking(Team checkForTeam, BoardState state, List <Promotion> promotions) { Team enemy = checkForTeam.Enemy(); if (!state.allPiecePositions.TryGetValue((enemy, Piece.King), out Index enemyKingLoc)) { return(false); } foreach (var rayDirection in EnumArray <HexNeighborDirection> .Values) { Index?hex = enemyKingLoc; (Team team, Piece piece)occupier; bool isBishopDirection = rayDirection switch { HexNeighborDirection.Up => false, HexNeighborDirection.Down => false, _ => true }; bool isRookDirection = !isBishopDirection; bool isPawnDirection = rayDirection switch { HexNeighborDirection.DownLeft => checkForTeam == Team.White, HexNeighborDirection.DownRight => checkForTeam == Team.White, HexNeighborDirection.UpLeft => checkForTeam != Team.White, HexNeighborDirection.UpRight => checkForTeam != Team.White, _ => false }; for (int distance = 1; distance < 20; distance++) { hex = hex.Value.GetNeighborAt(rayDirection); if (!hex.HasValue) { break; } if (state.allPiecePositions.TryGetValue(hex.Value, out occupier)) { if (occupier.team == checkForTeam) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (distance == 1) { if (isPawnDirection && realPiece.IsPawn()) { return(true); } if (realPiece == Piece.King) { return(true); } } if (isBishopDirection && (realPiece.IsBishop() || realPiece == Piece.Queen)) { return(true); } if (isRookDirection && (realPiece.IsRook() || realPiece == Piece.Queen)) { return(true); } } break; } } } foreach ((Index target, MoveType moveType)move in MoveGenerator.GetAllPossibleMoves(enemyKingLoc, Piece.BlackSquire, enemy, state, promotions)) { if (move.moveType == MoveType.Attack && state.TryGetPiece(move.target, out var occupier)) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsSquire()) { return(true); } } } foreach ((Index target, MoveType moveType)move in MoveGenerator.GetAllPossibleMoves(enemyKingLoc, Piece.KingsKnight, enemy, state, promotions)) { if (move.moveType == MoveType.Attack && state.TryGetPiece(move.target, out var occupier)) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsKnight()) { return(true); } } } for (int i = 1; i < 20; ++i) // Queen/Rook slide left { Index hex = new Index(enemyKingLoc.row, enemyKingLoc.col - i); if (!hex.IsInBounds) { break; } if (state.TryGetPiece(hex, out (Team team, Piece piece)occupier)) { if (occupier.team == checkForTeam) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsRook() || realPiece == Piece.Queen) { return(true); } } break; } } for (int i = 1; i < 20; i++) // Queen/Rook slide right { Index hex = new Index(enemyKingLoc.row, enemyKingLoc.col + i); if (!hex.IsInBounds) { break; } if (state.TryGetPiece(hex, out (Team team, Piece piece)occupier)) { if (occupier.team == checkForTeam) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsRook() || realPiece == Piece.Queen) { return(true); } } break; } } return(false); }