Example #1
0
    /// <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());
    }