public double Estimate(State state, int player, InterestingFacts facts) { if (state.players[player].status == PlayerStatus.Eliminated) { return(double.MinValue); } if (!killWithMinimax) { return(double.MaxValue); } if (state.isGameOver && facts.places[player] == 0) { return(double.MaxValue); } var score = 1; for (var i = 0; i < state.players.Length; i++) { if (i != player && state.players[i].status == PlayerStatus.Eliminated && (state.players[i].killedBy & (1 << player)) != 0) { score += killScore; } } return(score); }
public double Estimate(State state, InterestingFacts facts, int player, int pathStartLen) { var score = 0; if (state.players[player].status == PlayerStatus.Eliminated) { score -= 1_000_000_000; } else { for (var i = 0; i < state.players.Length; i++) { if (state.players[i].status == PlayerStatus.Eliminated && (state.players[i].killedBy & (1 << player)) != 0) { score += 1_000_000; } } } score += state.players[player].score; // if (state.time < Env.MAX_TICK_COUNT - 100) // { // score += state.players[player].nitrosCollected * 20; // score -= state.players[player].slowsCollected * 50; // } // score += state.players[player].nitrosCollected * 30 * (Env.TICKS_PER_REQUEST - Env.NITRO_TICKS_PER_REQUEST) / Env.TICKS_PER_REQUEST; // score -= state.players[player].slowsCollected * 30 * (Env.SLOW_TICKS_PER_REQUEST - Env.TICKS_PER_REQUEST) / Env.TICKS_PER_REQUEST; return(score); }
public double Estimate(State state, InterestingFacts facts, int player, int pathStartLen) { double baseScore; if (state.players[player].status == PlayerStatus.Eliminated) { baseScore = -1_000_000_000; } else { baseScore = 0; for (var i = 0; i < state.players.Length; i++) { if (i != player && state.players[i].status == PlayerStatus.Eliminated && (state.players[i].killedBy & (1 << player)) != 0) { baseScore += 10_000_000; } } } var nitroTimeBonus = state.players[player].nitrosCollected * 30 * (Env.TICKS_PER_REQUEST - Env.NITRO_TICKS_PER_REQUEST); var nitroScoreBonus = nitroTimeBonus / Env.TICKS_PER_REQUEST; var slowTimePenalty = state.players[player].slowsCollected * 50 * (Env.SLOW_TICKS_PER_REQUEST - Env.TICKS_PER_REQUEST); var slowScorePenalty = slowTimePenalty / Env.TICKS_PER_REQUEST; var opponentCaptured = state.players[player].opponentTerritoryCaptured - prevCaptured; if (pathStartLen > 0 && opponentCaptured == 0) { return(int.MinValue); } var score = state.players[player].score - prevScore; var time = state.time - prevTime; if (state.time < Env.MAX_TICK_COUNT - 100) { time += slowTimePenalty - nitroTimeBonus; score += nitroScoreBonus - slowScorePenalty; } if (opponentCaptured > 0) { return(baseScore + 100_000.0 * opponentCaptured - 100.0 * time + score); } if (state.playersLeft == 1) { return(baseScore + score); } var totalTicks = prevLineCount * Env.TICKS_PER_REQUEST + time; if (limitEmptyTicks != -1 && totalTicks > limitEmptyTicks) { baseScore -= totalTicks - limitEmptyTicks; } return(baseScore + score); }
public double Estimate(State state, InterestingFacts facts, int player, int pathStartLen) { double baseScore; baseScore = 0; for (var i = 0; i < state.players.Length; i++) { if (i != player && state.players[i].status == PlayerStatus.Eliminated && (state.players[i].killedBy & (1 << player)) != 0) { baseScore += 1_000_000_000; } } var nitroTimeBonus = state.players[player].nitrosCollected * 30 * (Env.TICKS_PER_REQUEST - Env.NITRO_TICKS_PER_REQUEST); var nitroScoreBonus = nitroTimeBonus / Env.TICKS_PER_REQUEST; var slowTimePenalty = state.players[player].slowsCollected * 30 * (Env.SLOW_TICKS_PER_REQUEST - Env.TICKS_PER_REQUEST); var slowScorePenalty = slowTimePenalty / Env.TICKS_PER_REQUEST; var opponentCaptured = state.players[player].opponentTerritoryCaptured - prevCaptured; if (pathStartLen > 0 && opponentCaptured == 0) { return(int.MinValue); } var score = state.players[player].score - prevScores[player]; var time = state.time - prevTime; if (state.time < Env.MAX_TICK_COUNT - 100) { time += slowTimePenalty - nitroTimeBonus; score += nitroScoreBonus - slowScorePenalty; } if (opponentCaptured > 0) { #if DEBUG backup.Backup(state); for (int i = 0; i < state.players.Length; i++) { pathsBackup[i] = facts.pathsToOwned[i].len; } #endif while (!state.isGameOver) { var ended = 0; for (var i = 0; i < state.players.Length; i++) { if (state.players[i].status == PlayerStatus.Eliminated || state.players[i].status == PlayerStatus.Broken) { ended++; continue; } if (state.players[i].arriveTime != 0) { if (facts.pathsToOwned[i].len < 0) { ended++; } continue; } if (facts.pathsToOwned[i].len <= 0) { ended++; } commands[i] = facts.pathsToOwned[i].ApplyNext(state, i); } if (ended == state.players.Length) { break; } state.NextTurn(commands, false); } var sumOpponentScore = 0; for (int i = 0; i < state.players.Length; i++) { if (i == player || state.players[i].status == PlayerStatus.Eliminated || facts.places[i] > 2) { continue; } var opponentScore = state.players[i].score - prevScores[i] - facts.potentialScores[i]; sumOpponentScore += opponentScore; } #if DEBUG backup.Restore(state); for (int i = 0; i < state.players.Length; i++) { facts.pathsToOwned[i].len = pathsBackup[i]; } #endif return(baseScore + 10_000_000.0 + 100_000.0 * opponentCaptured - sumOpponentScore / 5.0 * 10_000.0 - 100.0 * time + score); } return(baseScore + score); }
public byte GetAllowedDirectionsMask(ITimeManager timeManager, State state, int player, DistanceMap distanceMap, InterestingFacts facts) { var result = (byte)0; minimax.Alphabeta(timeManager, state, player, facts.pathsToOwned, distanceMap, facts); for (int i = 0; i < 4; i++) { if (minimax.bestResultScores[i] > 0) { result = (byte)(result | (1 << i)); } } return(result); }
public void Alphabeta(ITimeManager timeManager, State state, int player, PlayerPath[] skipPaths, DistanceMap distanceMap, InterestingFacts facts) { if (skipPaths != null && (skipPlayers == null || skipPlayers.Length < state.players.Length)) { skipPlayers = new bool[state.players.Length]; } bestScore = double.MinValue; bestDepth = 0; estimations = 0; bestAction = default(Direction); for (int i = 0; i < 4; i++) { bestResultScores[i] = double.NegativeInfinity; } var depth = 1; while (!timeManager.IsExpired && depth <= maxDepth) { for (int i = 0; i < 4; i++) { resultScores[i] = double.NegativeInfinity; } if (skipPaths != null) { for (int i = 0; i < state.players.Length; i++) { skipPlayers[i] = true; if (i == player || state.players[i].status == PlayerStatus.Eliminated || state.players[i].status == PlayerStatus.Broken) { continue; } var active = distanceMap.nearestOpponentActive[i, player]; if (active != ushort.MaxValue) { var dist = distanceMap.distances1[i, active]; if (dist != -1 && dist != int.MaxValue && dist <= 2 * depth) { skipPlayers[i] = false; continue; } dist = distanceMap.distances2[i, active]; if (dist != -1 && dist != int.MaxValue && dist <= 2 * depth) { skipPlayers[i] = false; continue; } } if (facts.sawCollectDistance[i] != int.MaxValue) { if (facts.sawCollectDistance[i] <= depth) { skipPlayers[i] = false; continue; } } } } var score = Alphabeta(timeManager, state, player, depth, player, double.MinValue, double.MaxValue, resultScores, out var action, 0, skipPaths, facts); if (double.IsNegativeInfinity(score)) { break; } bestScore = score; bestAction = action; bestDepth = depth; for (int i = 0; i < 4; i++) { bestResultScores[i] = resultScores[i]; } depth++; } }
private double Alphabeta( ITimeManager timeManager, State state, int player, int depth, int activePlayer, double a, double b, double[] rootScores, out Direction resultAction, int commandsStart, PlayerPath[] skipPaths, InterestingFacts facts) { resultAction = default(Direction); if (timeManager.IsExpired) { return(double.NegativeInfinity); } if (state.isGameOver || state.players[player].status == PlayerStatus.Eliminated || depth == 0 && activePlayer == player) { estimations++; var score = estimator.Estimate(state, player, facts); return(score); } if (activePlayer == player) { depth--; } var top = state.players[activePlayer].dir == null ? 6 : 5; var bestRootScore = double.MinValue; for (byte d = 3; d <= top; d++) { var action = (Direction)(((byte)(state.players[activePlayer].dir ?? Direction.Up) + d) % 4); var ne = state.players[activePlayer].arrivePos.NextCoord(action); if (ne == ushort.MaxValue || (state.lines[ne] & (1 << activePlayer)) != 0) { continue; } ulong skippedMask = 0; commands[commandsStart + activePlayer] = action; var nextPlayer = activePlayer == player ? 0 : activePlayer + 1; for (; nextPlayer < state.players.Length; nextPlayer++) { if (nextPlayer == player || state.players[nextPlayer].status == PlayerStatus.Eliminated || state.players[nextPlayer].status == PlayerStatus.Broken || state.players[nextPlayer].arriveTime > 0) { continue; } if (skipPaths != null && skipPlayers[nextPlayer]) { commands[commandsStart + nextPlayer] = skipPaths[nextPlayer].ApplyNext(state, nextPlayer); skippedMask += 1ul << (nextPlayer * 8); continue; } break; } StateUndo undo = null; if (nextPlayer == state.players.Length) { while (true) { var nextUndo = state.NextTurn(commands, withUndo: true, commandsStart: commandsStart); if (undo != null) { nextUndo.prevUndo = undo; } undo = nextUndo; commandsStart += 6; if (state.isGameOver || state.players[player].status == PlayerStatus.Eliminated) { nextPlayer = state.players.Length; break; } if (state.players[player].arriveTime == 0) { nextPlayer = player; break; } var done = false; for (nextPlayer = 0; nextPlayer < state.players.Length; nextPlayer++) { if (nextPlayer == player || state.players[nextPlayer].status == PlayerStatus.Eliminated || state.players[nextPlayer].status == PlayerStatus.Broken || state.players[nextPlayer].arriveTime > 0) { continue; } if (skipPaths != null && skipPlayers[nextPlayer]) { commands[commandsStart + nextPlayer] = skipPaths[nextPlayer].ApplyNext(state, nextPlayer); skippedMask += 1ul << (nextPlayer * 8); continue; } done = true; break; } if (done) { break; } } } var score = Alphabeta(timeManager, state, player, depth, nextPlayer, a, b, null, out _, commandsStart, skipPaths, facts); while (undo != null) { var prevUndo = undo.prevUndo; state.Undo(undo); undo = prevUndo; commandsStart -= 6; } if (skippedMask != 0) { for (int i = 0; i < state.players.Length; i++) { while ((skippedMask & (0xFFul << (i * 8))) != 0) { skippedMask -= 1ul << (i * 8); skipPaths[i].Revert(); } } } if (double.IsNegativeInfinity(score)) { return(double.NegativeInfinity); } if (rootScores != null) { rootScores[(int)action] = score; if (score > bestRootScore) { bestRootScore = score; } } else { if (player == activePlayer) { if (score > a) { a = score; resultAction = action; } } else { if (score < b) { b = score; resultAction = action; } } if (a >= b) { break; } } } return(rootScores != null ? bestRootScore : player == activePlayer ? a : b); }