private Result getMin(int curDepth, int agentIdx, ClientGameState curState) { double min = Double.MaxValue; ActionSet bestActions = new ActionSet(curState.Me.units.Count); int opponentIdx = (curState.myPlayerIdx + 1) % 2; List<ActionSet> legalActions = curState.GetLegalActions(opponentIdx); foreach (ActionSet action in legalActions) { ClientGameState successor = new ClientGameState(curState); for (int unitIdx = 0; unitIdx < successor.Opponent.units.Count; unitIdx++) { successor.Opponent.units[unitIdx].action = action[unitIdx]; } successor.UpdateGameState(); int nextAgentIdx = (agentIdx + 1) % 2; Result result = this.getNode(curDepth, nextAgentIdx, successor); if (result.Score < min) { min = result.Score; action.CopyTo(bestActions); } } return new Result(min, bestActions); }
private Result getMax(int curDepth, int agentIdx, ClientGameState curState, double alpha, double beta) { List<ActionSet> legalActions; double max = Double.MinValue; ActionSet bestActions = new ActionSet(curState.Me.units.Count); if (curDepth < this.searchDepth) { legalActions = curState.GetLegalActions(curState.myPlayerIdx); } else { legalActions = new List<ActionSet>(); legalActions.Add(bestActions); } if (legalActions.Count < 1) { return new Result(-1000, bestActions); } foreach (ActionSet action in legalActions) { //Program.abDebug.Write("[" + action.ToString() + "]"); ClientGameState successor = new ClientGameState(curState); for (int unitIdx = 0; unitIdx < successor.Me.units.Count; unitIdx++) { successor.Me.units[unitIdx].action = action[unitIdx]; } int nextAgentIdx = (agentIdx + 1) % 2; moveStack.Push(action); Result result = this.getNode(curDepth, nextAgentIdx, successor, alpha, beta); if (curDepth == 0) { debugWindow.AddLog(action.ToString() + " : " + result.Score); } if (result.Score > max) { max = result.Score; //Program.abDebug.WriteLine("Max at " + curDepth + " : " + max); action.CopyTo(bestActions); alpha = max; if (curDepth == 0) { for( int idx=0; idx<curState.Me.units.Count; idx++) { Program.MainForm.SetAction(curState.myPlayerIdx, curState.Me.units[idx].id, bestActions[idx]); } } } moveStack.Pop(); if (max > beta) { //Program.abDebug.WriteLine("Beta Cutoff"); this.cutoffs++; break; } } return new Result(max, bestActions); }
public void CopyTo(ActionSet target) { for (int idx = 0; idx < this.actions.Length; idx++) { if (idx >= target.actions.Length) break; target.actions[idx] = this.actions[idx]; } }
public List<ActionSet> GetLegalActions(int playerIdx) { List<ActionSet> result = new List<ActionSet>(); Player player = this.players[playerIdx]; if (player.units.Count < 1) { return result; } bool test; ActionSet actionSet; foreach (Action act1 in Enum.GetValues(typeof(Action))) { actionSet = new ActionSet(act1, Action.NONE); test = this.isPseudoLegal(playerIdx, actionSet); if (test == false) { continue; } result.Add(actionSet); if (player.units.Count > 1) { foreach (Action act2 in Enum.GetValues(typeof(Action))) { if (act2 == Action.NONE) { continue; } actionSet = new ActionSet(act1, act2); test = this.isPseudoLegal(playerIdx, actionSet); if (test == false) { continue; } result.Add(actionSet); } } } //result.Reverse(); return result; }
private void applyPlayerMovement(int playerIdx, ActionSet actions) { int newX, newY; bool blocked = false; //bool test = isPseudoLegal(playerIdx, actions); for (int unitIdx = 0; unitIdx < this.players[playerIdx].units.Count; unitIdx++) { Unit tank = this.players[playerIdx].units[unitIdx]; blocked = false; switch (actions[unitIdx]) { case Action.NONE: case Action.FIRE: break; case Action.LEFT: newX = tank.x - 1; newY = tank.y; if (newX < 2) throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move LEFT(Left Border Reached)"); for (int uy = newY - 2; uy <= newY + 2; uy++) { if (this.blocks[newX - 2, uy] != State.EMPTY) { if (tank.direction == Direction.LEFT) { throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move LEFT(Wall Hit and Already facing Left)"); } blocked = true; } } if (anyUnitAt(newX, newY, tank) != null) blocked = true; if (!blocked) this.players[playerIdx].units[unitIdx].x--; this.players[playerIdx].units[unitIdx].direction = Direction.LEFT; break; case Action.RIGHT: newX = tank.x + 1; newY = tank.y; if (newX >= this.BoardWidth - 2) throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move RIGHT(Right Border Reached)"); for (int uy = newY - 2; uy <= newY + 2; uy++) { if (this.blocks[newX + 2, uy] != State.EMPTY) { if (tank.direction == Direction.RIGHT) { throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move RIGHT(Wall Hit and Already facing Right)"); } blocked = true; } } if (anyUnitAt(newX, newY, tank) != null) blocked = true; if (!blocked) this.players[playerIdx].units[unitIdx].x++; this.players[playerIdx].units[unitIdx].direction = Direction.RIGHT; break; case Action.UP: newX = tank.x; newY = tank.y - 1; if (newY < 2) throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move UP(Top Border Reached)"); for (int ux = newX - 2; ux <= newX + 2; ux++) { if (this.blocks[ux, newY - 2] != State.EMPTY) { if (tank.direction == Direction.UP) { throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move UP(Wall Hit and Already facing Up)"); } blocked = true; } } if (anyUnitAt(newX, newY, tank) != null) blocked = true; if (!blocked) this.players[playerIdx].units[unitIdx].y--; this.players[playerIdx].units[unitIdx].direction = Direction.UP; break; case Action.DOWN: newX = tank.x; newY = tank.y + 1; if (newY >= this.BoardHeight - 2) throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move DOWN(Bottom Border Reached)"); for (int ux = newX - 2; ux <= newX + 2; ux++) { if (this.blocks[ux, newY + 2] != State.EMPTY) { if (tank.direction == Direction.DOWN) { throw new InvalidOperationException("Player " + playerIdx + " Tank " + unitIdx + " : Illegal Move DOWN(Wall Hit and Already facing Down)"); } blocked = true; } } if (anyUnitAt(newX, newY, tank) != null) blocked = true; if (!blocked) this.players[playerIdx].units[unitIdx].y++; this.players[playerIdx].units[unitIdx].direction = Direction.DOWN; break; } } }
private void applyPlayerFire(int playerIdx, ActionSet actions) { for (int unitIdx = 0; unitIdx < this.players[playerIdx].units.Count; unitIdx++) { Unit sourceTank = this.players[playerIdx].units[unitIdx]; switch (actions[unitIdx]) { case Action.FIRE: switch (sourceTank.direction) { case Direction.UP: this.players[playerIdx].bullets.Add(new Bullet(sourceTank.x, sourceTank.y - 3, Direction.UP)); break; case Direction.DOWN: this.players[playerIdx].bullets.Add(new Bullet(sourceTank.x, sourceTank.y + 3, Direction.DOWN)); break; case Direction.LEFT: this.players[playerIdx].bullets.Add(new Bullet(sourceTank.x - 3, sourceTank.y, Direction.LEFT)); break; case Direction.RIGHT: this.players[playerIdx].bullets.Add(new Bullet(sourceTank.x + 3, sourceTank.y, Direction.RIGHT)); break; } break; } } }
public void UpdateGameState() { this.moveBullets(0); this.checkCollisions(false); this.moveBullets(1); this.checkCollisions(false); this.moveBullets(0); this.checkCollisions(true); this.moveBullets(1); this.checkCollisions(true); for (int playerIdx = 0; playerIdx < 2; playerIdx++) { ActionSet actions = new ActionSet(this.players[playerIdx].units.Count); for (int unitIdx = 0; unitIdx < this.players[playerIdx].units.Count; unitIdx++) { actions[unitIdx] = this.players[playerIdx].units[unitIdx].action; } this.applyPlayerMovement(playerIdx, actions); } this.checkCollisions(false); for (int playerIdx = 0; playerIdx < 2; playerIdx++) { ActionSet actions = new ActionSet(this.players[playerIdx].units.Count); for (int unitIdx = 0; unitIdx < this.players[playerIdx].units.Count; unitIdx++) { actions[unitIdx] = this.players[playerIdx].units[unitIdx].action; } this.applyPlayerFire(playerIdx, actions); } this.checkCollisions(false); }
public bool isPseudoLegal(int playerIdx, ActionSet actions) { int newX, newY; for (int unitIdx = 0; unitIdx < this.players[playerIdx].units.Count; unitIdx++) { Unit tank = this.players[playerIdx].units[unitIdx]; switch (actions[unitIdx]) { case Action.NONE: case Action.FIRE: break; case Action.LEFT: newX = tank.x - 1; newY = tank.y; if (newX < 2) return false; for (int uy = newY - 2; uy <= newY + 2; uy++) { if ((this.blocks[newX - 2, uy] != State.EMPTY) && (tank.direction == Direction.LEFT)) return false; } break; case Action.RIGHT: newX = tank.x + 1; newY = tank.y; if (newX >= this.BoardWidth - 2) return false; for (int uy = newY - 2; uy <= newY + 2; uy++) { if ((this.blocks[newX + 2, uy] != State.EMPTY) && (tank.direction == Direction.RIGHT)) return false; } break; case Action.UP: newX = tank.x; newY = tank.y - 1; if (newY < 2) return false; for (int ux = newX - 2; ux <= newX + 2; ux++) { if ((this.blocks[ux, newY - 2] != State.EMPTY) && (tank.direction == Direction.UP)) return false; } break; case Action.DOWN: newX = tank.x; newY = tank.y + 1; if (newY >= this.BoardHeight - 2) return false; for (int ux = newX - 2; ux <= newX + 2; ux++) { if ((this.blocks[ux, newY + 2] != State.EMPTY) && (tank.direction == Direction.DOWN)) return false; } break; default: return false; } } return true; }
private Result getNode(int curDepth, int agentIdx, ClientGameState curState) { Player me = curState.Me; Player opponent = curState.Opponent; this.nodesSearched++; if (agentIdx == 0) { curDepth++; if (nodesSearched % 1000 == 0) { this.debugWindow.AddLog("Searched " + this.nodesSearched + " nodes..."); } } else { me = curState.Opponent; opponent = curState.Me; } if ((curDepth == this.searchDepth) || (curState.Won == true) || (curState.Lost == true)) { double score = curState.Evaluate(); ActionSet actions = new ActionSet(me.units.Count); for (int idx = 0; idx < me.units.Count; idx++) { actions[idx] = Action.NONE; } return new Result(score, actions); } Result result; if (agentIdx == 0) { result = this.getMax(curDepth, agentIdx, curState); } else { result = this.getMin(curDepth, agentIdx, curState); } return result; }
public Result(double score, ActionSet actions) { this.Score = score; this.Actions = actions; }
private Result getNode(int curDepth, int agentIdx, ClientGameState curState, double alpha, double beta) { Player me = curState.Me; Player opponent = curState.Opponent; this.nodesSearched++; if (agentIdx == 0) { curDepth++; if (nodesSearched % 1000 == 0) { this.debugWindow.AddLog("Searched " + this.nodesSearched + " nodes..."); } } else { me = curState.Opponent; opponent = curState.Me; } if (me.bullets.Count < 1 || curDepth > 15) { #if DEBUG if ((curDepth >= this.searchDepth) || (curState.Won == true) || (curState.Lost == true)) #else if ((curDepth == this.searchDepth) || (curState.Won == true) || (curState.Lost == true) || (searchDuration.ElapsedMilliseconds > 2800)) #endif { double score = curState.Evaluate(); //Program.abDebug.WriteLine(String.Join(",", moveStack) + " - " + curDepth.ToString() + ":" + score.ToString()); ActionSet actions = new ActionSet(me.units.Count); for (int idx = 0; idx < me.units.Count; idx++) { actions[idx] = Action.NONE; } return new Result(score, actions); } } Result result; if (agentIdx == 0) { result = this.getMax(curDepth, agentIdx, curState, alpha, beta); } else { result = this.getMin(curDepth, agentIdx, curState, alpha, beta); } return result; }
private Result getMin(int curDepth, int agentIdx, ClientGameState curState, double alpha, double beta) { List<ActionSet> legalActions; double min = Double.MaxValue; ActionSet bestActions = new ActionSet(curState.Opponent.units.Count); int opponentIdx = (curState.myPlayerIdx + 1) % 2; if (curDepth < this.searchDepth) { legalActions = curState.GetLegalActions(opponentIdx); } else { legalActions = new List<ActionSet>(); legalActions.Add(bestActions); } if (legalActions.Count < 1) { return new Result(1000, bestActions); } foreach (ActionSet action in legalActions) { //Program.abDebug.Write("[" + action.ToString() + "]"); ClientGameState successor = new ClientGameState(curState); for (int unitIdx = 0; unitIdx < successor.Opponent.units.Count; unitIdx++) { successor.Opponent.units[unitIdx].action = action[unitIdx]; } successor.UpdateGameState(); int nextAgentIdx = (agentIdx + 1) % 2; moveStack.Push(action); Result result = this.getNode(curDepth, nextAgentIdx, successor, alpha, beta); if (result.Score < min) { min = result.Score; //Program.abDebug.WriteLine("Min at " + curDepth + " : " + min); action.CopyTo(bestActions); beta = min; } moveStack.Pop(); if (min < alpha) { //Program.abDebug.WriteLine("Alpha Cutoff"); this.cutoffs++; break; } } return new Result(min, bestActions); }