public StateUndo NextTurn(Direction[] commands, bool withUndo, int commandsStart = 0) { StateUndo undo = null; if (withUndo) { undo = undos.Get(); undo.Before(this); } ApplyCommands(commands, commandsStart); var timeDelta = RenewArriveTime(); time += timeDelta; if (time > Env.MAX_TICK_COUNT) { isGameOver = true; if (withUndo) { undo.After(this); } return(undo); } Move(timeDelta); CheckIntermediateCollisions(); Capture(); CheckLoss(); CheckIsAte(); CaptureBonuses(undo); capture.ApplyTo(this, undo); MoveDone(); Done(); if (withUndo) { undo.After(this); } return(undo); }
public void ApplyTo(State state, StateUndo undo) { for (var player = 0; player < territoryCaptureCount.Length; player++) { if (state.players[player].status == PlayerStatus.Loser) { for (var i = 0; i < territoryCaptureCount[player]; i++) { var v = territoryCapture[player, i]; var mask = territoryCaptureMask[v]; territoryCaptureMask[v] = mask & ~(1 << player); } } } for (var player = 0; player < territoryCaptureCount.Length; player++) { if (state.players[player].status == PlayerStatus.Eliminated) { continue; } for (var i = 0; i < territoryCaptureCount[player]; i++) { var v = territoryCapture[player, i]; var mask = territoryCaptureMask[v] & 0xFF; if ((mask & ~(1 << player)) == 0) { var owner = state.territory[v]; if (owner != player) { if (owner != 0xFF && state.players[owner].status != PlayerStatus.Eliminated) { state.players[player].tickScore += Env.ENEMY_TERRITORY_SCORE - Env.NEUTRAL_TERRITORY_SCORE; state.players[player].opponentTerritoryCaptured++; state.players[owner].territory--; } state.players[player].territory++; undo?.NotifyCapture(state); state.territory[v] = (byte)player; state.territoryVersion++; } } } } }
private void CaptureBonuses(StateUndo undo) { for (var i = 0; i < players.Length; i++) { if (players[i].status == PlayerStatus.Eliminated || players[i].status == PlayerStatus.Broken) { continue; } if (players[i].arriveTime == 0 && players[i].arrivePos != ushort.MaxValue) { players[i].TickAction(); for (var b = 0; b < bonusCount;) { if (bonuses[b].pos != players[i].arrivePos && !capture.BelongsTo(bonuses[b].pos, i)) { b++; } else { if (bonuses[b].type == BonusType.N) { var bonusTime = bonuses[b].ActiveTicks(i); if (players[i].nitroLeft > 0) { players[i].nitroLeft += bonusTime; } else { players[i].nitroLeft = bonusTime; } players[i].UpdateShiftTime(); players[i].nitrosCollected++; } else if (bonuses[b].type == BonusType.S) { var bonusTime = bonuses[b].ActiveTicks(i); if (players[i].slowLeft > 0) { players[i].slowLeft += bonusTime; } else { players[i].slowLeft = bonusTime; } players[i].UpdateShiftTime(); players[i].slowsCollected++; } else if (bonuses[b].type == BonusType.Saw) { players[i].sawsCollected++; var sawStatus = 0ul; var v = players[i].arrivePos; while (true) { v = v.NextCoord(players[i].dir.Value); if (v == ushort.MaxValue) { break; } for (var k = 0; k < players.Length; k++) { if (k == i || players[k].status == PlayerStatus.Eliminated) { continue; } if (players[k].arrivePos == v || players[k].pos == v && players[k].arriveTime > 0) { sawStatus |= 0xFFul << (k * 8); players[k].status = PlayerStatus.Loser; players[k].killedBy = (byte)(players[k].killedBy | (1 << i)); players[i].tickScore += Env.SAW_KILL_SCORE; } } var owner = territory[v]; if (owner != 0xFF && owner != i) { if (players[owner].status != PlayerStatus.Eliminated) { sawStatus |= 1ul << (owner * 8); } } } for (var k = 0; k < players.Length; k++) { if (k == i || players[k].status == PlayerStatus.Eliminated) { continue; } if (((sawStatus >> (k * 8)) & 0xFF) != 1) { continue; } players[i].tickScore += Env.SAW_SCORE; var vx = players[i].arrivePos % Env.X_CELLS_COUNT; var vy = players[i].arrivePos / Env.X_CELLS_COUNT; if (players[i].dir.Value == Direction.Up || players[i].dir.Value == Direction.Down) { if (players[k].arrivePos % Env.X_CELLS_COUNT < vx) { var pos = 0; for (var y = 0; y < Env.Y_CELLS_COUNT; y++) { pos += vx; for (var x = vx; x < Env.X_CELLS_COUNT; x++, pos++) { if (territory[pos] == k) { players[k].territory--; undo?.NotifyCapture(this); territory[pos] = 0xFF; territoryVersion++; } } } } else { var pos = 0; for (var y = 0; y < Env.Y_CELLS_COUNT; y++) { for (var x = 0; x <= vx; x++, pos++) { if (territory[pos] == k) { players[k].territory--; undo?.NotifyCapture(this); territory[pos] = 0xFF; territoryVersion++; } } pos += Env.X_CELLS_COUNT - vx - 1; } } } else { if (players[k].arrivePos / Env.X_CELLS_COUNT < vy) { var pos = vy * Env.X_CELLS_COUNT; for (var y = vy; y < Env.Y_CELLS_COUNT; y++) { for (var x = 0; x < Env.X_CELLS_COUNT; x++, pos++) { if (territory[pos] == k) { players[k].territory--; undo?.NotifyCapture(this); territory[pos] = 0xFF; territoryVersion++; } } } } else { var pos = 0; for (var y = 0; y <= vy; y++) { for (var x = 0; x < Env.X_CELLS_COUNT; x++, pos++) { if (territory[pos] == k) { players[k].territory--; undo?.NotifyCapture(this); territory[pos] = 0xFF; territoryVersion++; } } } } } } } if (b != bonusCount - 1) { var tmp = bonuses[b]; bonuses[b] = bonuses[bonusCount - 1]; bonuses[bonusCount - 1] = tmp; } bonusCount--; } } } } }
public void Undo(StateUndo undo) { undo.Undo(this); undos.Return(undo); }
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); }