/// <summary> /// Runs MinMax on a unit. /// </summary> /// <returns>A result-UnitAction pair.</returns> private KeyValuePair<Result, UnitAction> MinMax() { List<Node.NodePointer> targets = pathManager.getCurrentTargets(subjectRef); MinPriorityQueue<UnitAction> bestMoves = new MinPriorityQueue<UnitAction>(); int realclay = subjectRef.getClay(); // The "max-y" part: maximize (damage/counter-damage) foreach (Node.NodePointer candidateAttackTarget in targets) { int moveCost = candidateAttackTarget.getDist(); Unit curEnemy = candidateAttackTarget.getTarget().Occupier; AttackRound util = new AttackRound(subjectRef, moveCost, curEnemy); UnitAction roundMove = new UnitAction(subjectRef, curEnemy, candidateAttackTarget.getStart()); int totDmg = 0; // Will very likely die, enqueue this move as bad, and don't move to next step. //TODO: utility = 0 if (util.attackerDies() || util.getUtility() == 0) { bestMoves.Enqueue(roundMove, double.PositiveInfinity); util.resetBack(); subjectRef.setClay(realclay); continue; } totDmg += util.getExpectedDamage(); // Loop through all things that could attack this position, and continue testing attacks. // The "min-y" part. Enemy tries to maximize their (damage/counter-damage) // Technically could just do damage maximization, since counter-damage is fixed. foreach (Unit enemy in subjectsEnemiesRef) { if (enemy.getClay() > 0 && pathManager.canAttack(enemy, candidateAttackTarget.getStart())) { //gets the closest move. This will be the move that maxes damage. int enemyMoveCost = pathManager.maxDamageMoveCost(enemy, candidateAttackTarget.getStart()); AttackRound subRound = new AttackRound(enemy, enemyMoveCost, subjectRef); int roundClay = subjectRef.getClay(); subRound.resetBack(); subjectRef.setClay(roundClay); // If we die, break early, as usual. if (util.defenderDies()) { break; } totDmg += subRound.getExpectedCounterDamage(); } } // Died. Enqueue with +inf again. if (subjectRef.getClay() == 0) bestMoves.Enqueue(roundMove, double.PositiveInfinity); // enqueue move! min pri queue, so invert answer. else bestMoves.Enqueue(roundMove, -((double)totDmg / subjectRef.getClay())); util.resetBack(); subjectRef.setClay(realclay); } subjectRef.setClay(realclay); // no local targets... if (bestMoves.Count == 0) return new KeyValuePair<Result, UnitAction>(Result.NothingFound, null); // all moves die. only move is not to play. else if (bestMoves.currentInversePriority(bestMoves.Peek()) == double.PositiveInfinity) return new KeyValuePair<Result, UnitAction>(Result.WillDie, bestMoves.Peek()); // found good target. return new KeyValuePair<Result, UnitAction>(Result.Success, bestMoves.Peek()); }