edu.cwru.sepia.model.LessSimpleModel.java Source code

Java tutorial

Introduction

Here is the source code for edu.cwru.sepia.model.LessSimpleModel.java

Source

/**
 *  Strategy Engine for Programming Intelligent Agents (SEPIA)
Copyright (C) 2012 Case Western Reserve University
    
This file is part of SEPIA.
    
SEPIA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
SEPIA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with SEPIA.  If not, see <http://www.gnu.org/licenses/>.
 */
package edu.cwru.sepia.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.configuration.Configuration;

import edu.cwru.sepia.action.Action;
import edu.cwru.sepia.action.ActionQueue;
import edu.cwru.sepia.action.ActionResult;
import edu.cwru.sepia.action.ActionResultType;
import edu.cwru.sepia.action.ActionType;
import edu.cwru.sepia.action.DirectedAction;
import edu.cwru.sepia.action.ProductionAction;
import edu.cwru.sepia.action.TargetedAction;
import edu.cwru.sepia.model.state.Direction;
import edu.cwru.sepia.model.state.ResourceNode;
import edu.cwru.sepia.model.state.ResourceType;
import edu.cwru.sepia.model.state.State;
import edu.cwru.sepia.model.state.StateCreator;
import edu.cwru.sepia.model.state.Template;
import edu.cwru.sepia.model.state.Unit;
import edu.cwru.sepia.model.state.UnitTemplate;
import edu.cwru.sepia.model.state.UpgradeTemplate;
import edu.cwru.sepia.pathing.DurativePlanner;
import edu.cwru.sepia.util.DistanceMetrics;
import edu.cwru.sepia.util.SerializerUtil;

/**
 * A less simple model that allows consistency, turn-taking and durative actions.
 * No longer supports unit tasks
 * Recalculates compound actions automatically every step.
 * Supports consistent actions* according to the following principles:
 * The order of actions does not affect their results,
 * Sets of actions may not cause invalid states (so some must fail),
 * The effects of one action succeeding may not change based on the actions of others**,
 * An set of actions that fails cannot be made to succeed by adding actions for other units.
 * 
 * From this, compound actions may only calculate primitives once in a turn, and only based on the previous state.
 * This makes compound actions much much less effective than in SimpleModel.
 * 
 * 
 * The particular solution in this makes sure that successes based on ranges (including directional actions with proximity) are calculated based on the previous state.
 * It also ensures that an action may not fail due to a unit involved being attacked for enough damage to kill it.
 * * Production/build actions that make new units fail consistency.  The units are placed sequentially in places that were empty last state in such a way as to not disrupt move actions.  In the situation where more productions exist than empty spaces that are not being moved into, then only the minimum number of production actions will fail
 * ** More generally, where s is the event "action has succeeded" and E is a set of possible results, and O is actions sent to other units, E given s must be independant of O, IE: for all O, P(E|s) = P(E|s,O)***
 * *** as an example that works, attacks, being based on sequential Random variables from a seed, will change exact result, but the distribution of successful attacks does not change based on other actions.  As a failing example, random movements that only fail on an actual collision wouldn't work, as the action of the other unit affects the distribution of effects. 
 * 
 */
public class LessSimpleModel extends AbstractDurativeModel {
    private static final long serialVersionUID = -8289868580233478749L;

    public LessSimpleModel(State init, StateCreator restartTactic, Configuration configuration) {
        super(init, restartTactic, configuration, Logger.getLogger(LessSimpleModel.class.getCanonicalName()));
    }

    /**
     * The main loop of the engine.
     * Removes the old views
     */
    @Override
    public void executeStep() {

        //if for some reason you start getting views before you are done changing things, then you will need to deprecate them again

        //Run the Actions
        //This is based on 3 factors: the last state, spaces/resources/nodes with pending actions that are not known to be failures, and spaces/resources/nodes that are known to be failures

        //The basic procedure is (list and set are used colloquially, and may be implemented as other things):
        //if compound, check if you can do the action in the last state, recalculate if needed
        //if it is the last one in a compound,
        //check if you can do the action in general/based on last state
        //if you can't, put yourself on a fail list and stop processing this action
        //if you are a durative action that won't complete this step, put yourself on the successful list then stop processing this action
        //check if what you would effect is in the list of those with known problematic claims
        //if it is problematic, then put yourself on the fail list and stop processing this action
        //add your effect to the claims list
        //check if the claims including yourself will now cause a problem
        //if it will, mark yourself and all others claiming it on the failed list (removing them from the successful list too), and remove it from the claims list and put it on the known problematic list
        //if it will not cause a problem, mark yourself on the successful list and stop processing this action
        //after all actions have been checked and claimed
        //execute and log all actions remaining in the successful list
        //log all failed actions, and remove them from the queue

        //interpret coordinates as integers
        Map<Integer, ActionQueue> claimedspaces = new HashMap<Integer, ActionQueue>(); //merge the boolean for whether it has claimed with the set of actions that claimed it, and since one is known to be enough, don't need a set
        Set<Integer> problemspaces = new HashSet<Integer>();//places that you know are problems
        Map<Integer, Integer> claimedgathering = new HashMap<Integer, Integer>();//<nodeid,amountclaimed>
        Map<Integer, Set<ActionQueue>> claimedgatheringactions = new HashMap<Integer, Set<ActionQueue>>();//<nodeid,claimingactions>
        Set<Integer> problemgatherings = new HashSet<Integer>();//<problemnodeids>
        Map<Integer, Map<ResourceType, Integer>> claimedcosts = new HashMap<Integer, Map<ResourceType, Integer>>();//<player,<resourcetype,amountclaimed>>
        Map<Integer, Map<ResourceType, Set<ActionQueue>>> claimedcostactions = new HashMap<Integer, Map<ResourceType, Set<ActionQueue>>>();//<player,<resourcetypes,claimingactions>>
        Map<Integer, Set<ResourceType>> problemcosts = new HashMap<Integer, Set<ResourceType>>();//<player, problemresourcetypes>
        Map<Integer, Integer> claimedfoodcosts = new HashMap<Integer, Integer>();//<player,amountclaimed>
        Map<Integer, Set<ActionQueue>> claimedfoodcostactions = new HashMap<Integer, Set<ActionQueue>>();//<player,claimingactions>
        Set<Integer> problemfoodcosts = new HashSet<Integer>();//<problemplayer>
        Set<ActionQueue> failed = new HashSet<ActionQueue>();
        Set<ActionQueue> successfulsofar = new HashSet<ActionQueue>();
        /** Track all units of active players, to reset their progress if they failed or weren't moved*/
        Set<Integer> unsuccessfulUnits = new HashSet<Integer>();
        Set<ActionQueue> productionsuccessfulsofar = new HashSet<ActionQueue>();
        for (Integer player : queuedActions.keySet()) {
            if (turnTracker == null || turnTracker.isPlayersTurn(player)) {
                //Gather all units of that player, so that we can remove the ones that were successful later
                for (Integer id : state.getUnits(player).keySet()) {
                    unsuccessfulUnits.add(id);
                }
                Iterator<Entry<Integer, ActionQueue>> playerActions = queuedActions.get(player).entrySet()
                        .iterator();
                while (playerActions.hasNext()) {
                    Entry<Integer, ActionQueue> entry = playerActions.next();
                    ;
                    ActionQueue aq = entry.getValue();
                    //            if (a==null) //Then it failed to calculate primitives, so it fails
                    int uid = entry.getKey();
                    Unit u = state.getUnit(uid);
                    if (u == null || uid != aq.getFullAction().getUnitId()) {
                        //unit is dead or never existed
                        playerActions.remove();
                        history.recordCommandFeedback(player, state.getTurnNumber(),
                                new ActionResult(aq.getFullAction(), ActionResultType.INVALIDUNIT));

                    } else {
                        aq.resetPrimitives(calculatePrimitives(aq.getFullAction()));
                        Action a = aq.peekPrimitive();
                        if (a == null) //This happens when you try to compound move to where you are, not sure about other cases
                        {
                            playerActions.remove();
                            history.recordCommandFeedback(player, state.getTurnNumber(),
                                    new ActionResult(aq.getFullAction(), ActionResultType.COMPLETED));
                        } else if (!ActionType.isPrimitive(a.getType())) {
                            throw new RuntimeException(
                                    "This should never happen, all subactions should be primitives");
                        } else if (a.getType() == ActionType.PRIMITIVEATTACK && !(a instanceof TargetedAction)
                                || a.getType() == ActionType.PRIMITIVEGATHER && !(a instanceof DirectedAction)
                                || a.getType() == ActionType.PRIMITIVEDEPOSIT && !(a instanceof DirectedAction)
                                || a.getType() == ActionType.PRIMITIVEPRODUCE && !(a instanceof ProductionAction)
                                || a.getType() == ActionType.PRIMITIVEBUILD && !(a instanceof ProductionAction)
                                || a.getType() == ActionType.PRIMITIVEMOVE && !(a instanceof DirectedAction)) {//shouldn't have to do this, should make actions so it is never possible to have the types not match
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       //log a wrong type thing
                            history.recordCommandFeedback(player, state.getTurnNumber(),
                                    new ActionResult(aq.getFullAction(), ActionResultType.INVALIDTYPE));
                            //remove it from the queues
                            playerActions.remove();
                        } else {

                            if (a.getType() == ActionType.FAILED || a.getType() == ActionType.FAILEDPERMANENTLY) {
                                failed.add(aq);
                                //recalcAndStuff();//This marks a place where recalculation would be called for
                            }
                            //check if it is a move
                            if (a.getType() == ActionType.PRIMITIVEMOVE) {
                                //if it can't move, that is a problem
                                if (!u.canMove()) {
                                    logger.log(Level.FINER,
                                            "Unit " + u + " unable to move, action " + a + " failed");
                                    failed.add(aq);
                                    //recalcAndStuff();//This marks a place where recalculation would be called for
                                } else //hasn't failed yet
                                {
                                    //find out where it will be next

                                    DirectedAction da = (DirectedAction) a;
                                    Direction d = da.getDirection();
                                    int xdest = u.getXPosition() + d.xComponent();
                                    int ydest = u.getYPosition() + d.yComponent();

                                    //if it is not empty there is a problem
                                    if (!accessible(u, xdest, ydest)) {
                                        failed.add(aq);
                                        //recalcAndStuff();//This marks a place where recalculation would be called for
                                    } else //hasn't failed yet
                                    {
                                        int newdurativeamount;
                                        if (da.equals(u.getActionProgressPrimitive())) {
                                            newdurativeamount = u.getActionProgressAmount() + 1;
                                        } else {
                                            newdurativeamount = 1;
                                        }
                                        boolean willcompletethisturn = newdurativeamount == DurativePlanner
                                                .calculateMoveDuration(u, u.getXPosition(), u.getYPosition(), d,
                                                        state);
                                        //if it will finish, then verify claim stuff
                                        if (willcompletethisturn) {
                                            Integer dest = getCoordInt(xdest, ydest);
                                            //check if the space is a problem
                                            if (problemspaces.contains(dest)) {
                                                failed.add(aq);
                                            } else //not a problem space
                                            {
                                                //check if it is claimed
                                                ActionQueue priorclaimant = claimedspaces.get(dest);
                                                if (priorclaimant != null) {//it is claimed
                                                    successfulsofar.remove(priorclaimant);
                                                    failed.add(priorclaimant);
                                                    failed.add(aq);
                                                    problemspaces.add(dest);
                                                    claimedspaces.remove(dest); //remove all claims, as it is now a problem, not a claim, may be pointless
                                                } else {//it is not claimed
                                                        //so claim it
                                                    claimedspaces.put(dest, aq);
                                                    successfulsofar.add(aq);
                                                }
                                            }
                                        } else //won't complete, so passes all claims
                                        {
                                            successfulsofar.add(aq);
                                        }
                                    }
                                }
                            }

                            else if (a.getType() == ActionType.PRIMITIVEDEPOSIT) {
                                if (!u.canGather() || u.getCurrentCargoAmount() <= 0) {//if can't gather or isn't carrying anything, then this isn't an acceptible action
                                    failed.add(aq);
                                    //recalcAndStuff();//This marks a place where recalculation would be called for
                                } else {
                                    DirectedAction da = (DirectedAction) a;
                                    Direction d = da.getDirection();
                                    int xdest = u.getXPosition() + d.xComponent();
                                    int ydest = u.getYPosition() + d.yComponent();
                                    Unit townHall = state.unitAt(xdest, ydest);
                                    if (townHall == null || townHall.getPlayer() != u.getPlayer()) {//no unit there on your team
                                        failed.add(aq);
                                        //recalcAndStuff();//This marks a place where recalculation would be called for
                                    } else //there is a unit on your team
                                    {
                                        //check if the unit can accept the kind of resources that you have
                                        boolean canAccept = townHall.getTemplate()
                                                .canAccept(u.getCurrentCargoType());
                                        if (!canAccept) {//then it isn't a town hall of the right type
                                            failed.add(aq);
                                            //recalcAndStuff();//This marks a place where recalculation would be called for
                                        } else //there is an appropriate town hall there
                                        {
                                            //deposit has no chance of conflicts, so this works
                                            successfulsofar.add(aq);
                                        }
                                    }
                                }
                            } else if (a.getType() == ActionType.PRIMITIVEATTACK) {
                                //make sure you can attack and the target exists and is in range in the last state
                                if (!u.canAttack()) {
                                    failed.add(aq);
                                    //recalcAndStuff();//This marks a place where recalculation would be called for
                                } else {
                                    TargetedAction ta = (TargetedAction) a;
                                    Unit target = state.getUnit(ta.getTargetId());
                                    if (target == null || target.getCurrentHealth() <= 0 || !inRange(u, target)) {
                                        failed.add(aq);
                                        //recalcAndStuff();//This marks a place where recalculation would be called for
                                    } else //target exists and is in range
                                    {
                                        //no possibility for conflict, so this succeeds
                                        successfulsofar.add(aq);
                                    }
                                }
                            } else if (a.getType() == ActionType.PRIMITIVEPRODUCE
                                    || a.getType() == ActionType.PRIMITIVEBUILD) {//currently, this adds to productionsuccessfulsofar because they are not processed consistantly
                                                                                                                                        //consistancy could be restored by making unit production and building actions require a place or direction for the new unit to go, and then processing it as a move

                                //last state check:
                                ProductionAction pa = (ProductionAction) a;
                                Template<?> t = state.getTemplate(pa.getTemplateId());
                                if (a.getType() == ActionType.PRIMITIVEPRODUCE && u.canBuild()
                                        || a.getType() == ActionType.PRIMITIVEBUILD && !u.canBuild()) {//if it should build and is trying to produce or should produce and is trying to build
                                    failed.add(aq);
                                    //recalcAndStuff();//This marks a place where recalculation would be called for
                                } else if (t == null || !u.getTemplate().canProduce(t)
                                        || !(prerequisitesMet(t, u.getPlayer()))) {//if the template does not exist or the unit cannot make the template or the template's prerequisites are not met
                                    failed.add(aq);
                                    //recalcAndStuff();//This marks a place where recalculation would be called for
                                } else //template exists, is producable by the unit, and has it's tech-tree prerequisites met
                                {
                                    int newdurativeamount;
                                    if (pa.equals(u.getActionProgressPrimitive())) {
                                        newdurativeamount = u.getActionProgressAmount() + 1;
                                    } else {
                                        newdurativeamount = 1;
                                    }
                                    boolean willcompletethisturn = newdurativeamount == DurativePlanner
                                            .calculateProductionDuration(u, t);
                                    if (willcompletethisturn) {
                                        if (!problemcosts.containsKey(player))
                                            problemcosts.put(player, new HashSet<ResourceType>());
                                        if (!claimedcosts.containsKey(player))
                                            claimedcosts.put(player, new HashMap<ResourceType, Integer>());
                                        if (!claimedcostactions.containsKey(player))
                                            claimedcostactions.put(player,
                                                    new HashMap<ResourceType, Set<ActionQueue>>());
                                        boolean failedaclaim = false;

                                        //check all the resources, including supply for problems and claims
                                        //note that if you don't need any, it doesn't matter if it is overdrawn
                                        //do all even if one fails, because if you stop checking when you fail one resource and don't claim the others, then another production that should conflict will not be detected as such
                                        {
                                            int goldneeded = t.getGoldCost();
                                            //if you have a cost, then check the claims
                                            if (goldneeded > 0) {
                                                //check if it is a problem
                                                if (problemcosts.get(player).contains(ResourceType.GOLD)) {
                                                    failedaclaim = true;
                                                } else {//not a problem already, check claims
                                                        //get the amount of the resource that you had before
                                                    int previousamount = state.getResourceAmount(player,
                                                            ResourceType.GOLD);
                                                    // get the previous claim (if there is none, that is zero)
                                                    Integer previousclaim = claimedcosts.get(player)
                                                            .get(ResourceType.GOLD);
                                                    if (previousclaim == null)
                                                        previousclaim = 0;
                                                    int updatedclaim = previousclaim + goldneeded;

                                                    //check if the total claim is more than the amount the player has
                                                    if (updatedclaim > previousamount) {
                                                        //if the claim is more, then this and all others with claims on this resource fail
                                                        Set<ActionQueue> otherclaimants = claimedcostactions
                                                                .get(player).get(ResourceType.GOLD);
                                                        if (otherclaimants != null) {
                                                            for (ActionQueue otherclaimant : otherclaimants) {
                                                                productionsuccessfulsofar.remove(otherclaimant);
                                                                failed.add(otherclaimant);
                                                            }
                                                            //since we are marking this as a problem, don't need the claim anymore
                                                            claimedcostactions.get(player)
                                                                    .remove(ResourceType.GOLD);
                                                        }
                                                        failedaclaim = true;
                                                        problemcosts.get(player).add(ResourceType.GOLD);
                                                    } else {//not too much, so claim it
                                                        claimedcosts.get(player).put(ResourceType.GOLD,
                                                                updatedclaim);
                                                        if (!claimedcostactions.get(player)
                                                                .containsKey(ResourceType.GOLD)) {
                                                            claimedcostactions.get(player).put(ResourceType.GOLD,
                                                                    new HashSet<ActionQueue>());
                                                        }
                                                        claimedcostactions.get(player).get(ResourceType.GOLD)
                                                                .add(aq);
                                                    }
                                                }
                                            }
                                        }
                                        {
                                            int woodneeded = t.getWoodCost();
                                            //if you have a cost, then check the claims
                                            if (woodneeded > 0) {
                                                //check if it is a problem
                                                if (problemcosts.get(player).contains(ResourceType.WOOD)) {
                                                    failedaclaim = true;
                                                } else {//not a problem already, check claims
                                                        //get the amount of the resource that you had before
                                                    int previousamount = state.getResourceAmount(player,
                                                            ResourceType.WOOD);
                                                    // get the previous claim (if there is none, that is zero)
                                                    Integer previousclaim = claimedcosts.get(player)
                                                            .get(ResourceType.WOOD);
                                                    if (previousclaim == null)
                                                        previousclaim = 0;
                                                    int updatedclaim = previousclaim + woodneeded;

                                                    //check if the total claim is more than the amount the player has
                                                    if (updatedclaim > previousamount) {
                                                        //if the claim is more, then this and all others with claims on this resource fail
                                                        Set<ActionQueue> otherclaimants = claimedcostactions
                                                                .get(player).get(ResourceType.WOOD);
                                                        if (otherclaimants != null) {
                                                            for (ActionQueue otherclaimant : otherclaimants) {
                                                                productionsuccessfulsofar.remove(otherclaimant);
                                                                failed.add(otherclaimant);
                                                            }
                                                            //since we are marking this as a problem, don't need the claim anymore
                                                            claimedcostactions.get(player)
                                                                    .remove(ResourceType.WOOD);
                                                        }
                                                        failedaclaim = true;
                                                        problemcosts.get(player).add(ResourceType.WOOD);
                                                    } else {//not too much, so claim it
                                                        claimedcosts.get(player).put(ResourceType.WOOD,
                                                                updatedclaim);
                                                        if (!claimedcostactions.get(player)
                                                                .containsKey(ResourceType.WOOD)) {
                                                            claimedcostactions.get(player).put(ResourceType.WOOD,
                                                                    new HashSet<ActionQueue>());
                                                        }
                                                        claimedcostactions.get(player).get(ResourceType.WOOD)
                                                                .add(aq);
                                                    }
                                                }
                                            }
                                        }
                                        {
                                            int foodneeded = t.getFoodCost();
                                            //if you have a cost, then check the claims
                                            if (foodneeded > 0) {
                                                //check if it is a problem
                                                if (problemfoodcosts.contains(player)) {
                                                    failedaclaim = true;
                                                } else {//not a problem already, check claims
                                                        //get the amount of the resource that you had before
                                                    int previousamount = state.getSupplyCap(player)
                                                            - state.getSupplyAmount(player);
                                                    // get the previous claim (if there is none, that is zero)
                                                    Integer previousclaim = claimedfoodcosts.get(player);
                                                    if (previousclaim == null)
                                                        previousclaim = 0;
                                                    int updatedclaim = previousclaim + foodneeded;

                                                    //check if the total claim is more than the amount the player has
                                                    if (updatedclaim > previousamount) {
                                                        //if the claim is more, then this and all others with claims on this resource fail
                                                        Set<ActionQueue> otherclaimants = claimedfoodcostactions
                                                                .get(player);
                                                        if (otherclaimants != null) {
                                                            for (ActionQueue otherclaimant : otherclaimants) {
                                                                productionsuccessfulsofar.remove(otherclaimant);
                                                                failed.add(otherclaimant);
                                                            }
                                                            //since we are marking this as a problem, don't need the claim anymore
                                                            claimedfoodcostactions.remove(player);
                                                        }
                                                        failedaclaim = true;
                                                        problemfoodcosts.add(player);
                                                    } else {//not too much, so claim it
                                                        claimedfoodcosts.put(player, updatedclaim);
                                                        if (!claimedfoodcostactions.containsKey(player)) {
                                                            claimedfoodcostactions.put(player,
                                                                    new HashSet<ActionQueue>());
                                                        }
                                                        claimedfoodcostactions.get(player).add(aq);
                                                    }
                                                }
                                            }
                                        }

                                        if (failedaclaim) {
                                            failed.add(aq);
                                        } else {
                                            productionsuccessfulsofar.add(aq);
                                        }

                                    } else //won't complete, so passes all claims
                                    {
                                        successfulsofar.add(aq);
                                    }
                                }

                            } else if (a.getType() == ActionType.PRIMITIVEGATHER) {
                                //check if it can gather at all
                                if (!u.canGather()) {
                                    failed.add(aq);
                                    //recalcAndStuff();//This marks a place where recalculation would be called for
                                } else //it can gather
                                {
                                    //find the node you want to gather from, and make sure it exists
                                    DirectedAction da = (DirectedAction) a;
                                    Direction d = da.getDirection();
                                    int xdest = u.getXPosition() + d.xComponent();
                                    int ydest = u.getYPosition() + d.yComponent();
                                    ResourceNode rn = state.resourceAt(xdest, ydest);
                                    //check if the node exists and was not exhausted last turn
                                    if (rn == null || rn.getAmountRemaining() <= 0) {
                                        failed.add(aq);
                                        //recalcAndStuff();//This marks a place where recalculation would be called for
                                    } else //there is a node and it has resources
                                    {
                                        int newdurativeamount;
                                        if (da.equals(u.getActionProgressPrimitive())) {
                                            newdurativeamount = u.getActionProgressAmount() + 1;
                                        } else {
                                            newdurativeamount = 1;
                                        }
                                        boolean willcompletethisturn = newdurativeamount == DurativePlanner
                                                .calculateGatherDuration(u, rn);
                                        if (willcompletethisturn) {

                                            //then check if the node will be a problem
                                            if (problemgatherings.contains(rn.id)) {
                                                failed.add(aq);
                                            } else //no problem yet
                                            {

                                                //so test out the new claim
                                                int previousamount = rn.getAmountRemaining();
                                                boolean isotherclaimant = true;
                                                Integer otherclaims = claimedgathering.get(rn.id);
                                                //if there is no other claim, then the other claim is 0, and it should be noted that noone else is claiming it
                                                if (otherclaims == null) {
                                                    isotherclaimant = false;
                                                    otherclaims = 0;
                                                }
                                                int updatedclaim = otherclaims
                                                        + u.getTemplate().getGatherRate(rn.getType().getResource());
                                                //if the claim is too much, then the node has a problem
                                                //but don't fail if this is the only claimant
                                                //in that case, the result should be that this mines out the resource
                                                if (updatedclaim > previousamount && isotherclaimant) {
                                                    //the node is a problem, so make all claimants fail and mark it as such
                                                    problemgatherings.add(rn.id);
                                                    for (ActionQueue otherclaimant : claimedgatheringactions
                                                            .get(rn.id)) {
                                                        successfulsofar.remove(otherclaimant);
                                                        failed.add(otherclaimant);
                                                    }
                                                    failed.add(aq);
                                                    claimedgatheringactions.remove(rn.id);
                                                } else //the state isn't a problem
                                                {
                                                    //so make the claim and succeed
                                                    claimedgathering.put(rn.id, updatedclaim);
                                                    //make sure the set is initialized
                                                    if (!claimedgatheringactions.containsKey(rn.id))
                                                        claimedgatheringactions.put(rn.id,
                                                                new HashSet<ActionQueue>());
                                                    claimedgatheringactions.get(rn.id).add(aq);
                                                    successfulsofar.add(aq);
                                                }
                                            }

                                        } else //won't complete, so passes all claims
                                        {
                                            successfulsofar.add(aq);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        //to make production spawning as consistant yet sequential as possible, find positions that weren't occupied before and which weren't claimed by moves or other production actions
        //need to avoid claimed spaces so that the inconsistancy doesn't break move's consistancy
        //so calculate now and use later
        Map<ActionQueue, int[]> productionplaces = new HashMap<ActionQueue, int[]>();

        {
            Set<Integer> productionclaimedspaces = new HashSet<Integer>();
            boolean nomorespaces = false;//once you run out of spaces, no further productions that make units will succeed
            Set<Integer> moveclaimedspaces = claimedspaces.keySet(); //grab the spaces claimed by move, it shouldn't change during this
            for (ActionQueue aq : productionsuccessfulsofar) {
                //only production actions that will complete should be in productionsuccessfulsofar
                ProductionAction a = (ProductionAction) aq.peekPrimitive();
                Unit u = state.getUnit(a.getUnitId());
                Template<?> producedTemplate = state.getTemplate(a.getTemplateId());
                //check if it is an upgrade and thus doesn't risk failure and can just succeed
                if (producedTemplate instanceof UpgradeTemplate) {
                    successfulsofar.add(aq);
                } else //will be a unit/building, needs to reserve a space
                {
                    if (nomorespaces) {//if you ran out of spaces before, then there is no point trying again, as it will be no better
                        failed.add(aq);
                    } else {
                        //find the nearest open position, which will be null if there is none
                        int[] newposition = getClosestEmptyUnclaimedPosition((UnitTemplate) producedTemplate,
                                u.getXPosition(), u.getYPosition(), moveclaimedspaces, productionclaimedspaces);
                        if (newposition == null) {//if no place for new unit
                                                  //then this fails
                            failed.add(aq);
                            nomorespaces = true;
                        } else //there is a place for the new unit
                        {
                            //so reserve the new position and mark as successful
                            productionplaces.put(aq, newposition);
                            productionclaimedspaces.add(getCoordInt(newposition[0], newposition[1]));
                            successfulsofar.add(aq);
                        }
                    }

                }
            }
        }

        //Take all of the actions that haven't failed yet and execute them
        for (ActionQueue aq : successfulsofar) {
            //Mark it's unit as having moved successfully
            unsuccessfulUnits.remove(aq.getFullAction().getUnitId());

            //execute it without further checking, logging it
            Action a = aq.popPrimitive();
            int uid = a.getUnitId();
            Unit u = state.getUnit(uid);
            boolean willcompletethisturn = true;
            {
                //check if it is a move
                if (a.getType() == ActionType.PRIMITIVEMOVE) {
                    //if it can't move, that is a problem
                    {
                        //find out where it will be next

                        DirectedAction da = (DirectedAction) a;
                        Direction d = da.getDirection();
                        //calculate the amount of duration
                        int newdurativeamount;
                        if (da.equals(u.getActionProgressPrimitive())) {
                            newdurativeamount = u.getActionProgressAmount() + 1;
                        } else {
                            newdurativeamount = 1;
                        }
                        willcompletethisturn = newdurativeamount == DurativePlanner.calculateMoveDuration(u,
                                u.getXPosition(), u.getYPosition(), d, state);
                        //if it will finish, then execute the atomic action
                        if (willcompletethisturn) {
                            //do the atomic action
                            state.moveUnit(u, d);
                            //you did the action, so reset the progress
                            u.resetDurative();
                        } else {
                            //increment the duration
                            u.setDurativeStatus(da, newdurativeamount);
                        }
                    }
                }

                if (a.getType() == ActionType.PRIMITIVEDEPOSIT) {
                    DirectedAction da = (DirectedAction) a;
                    Direction d = da.getDirection();
                    int xdest = u.getXPosition() + d.xComponent();
                    int ydest = u.getYPosition() + d.yComponent();
                    Unit townHall = state.unitAt(xdest, ydest);

                    //calculate the amount of duration
                    int newdurativeamount;
                    if (da.equals(u.getActionProgressPrimitive())) {
                        newdurativeamount = u.getActionProgressAmount() + 1;
                    } else {
                        newdurativeamount = 1;
                    }
                    willcompletethisturn = newdurativeamount == DurativePlanner.calculateDepositDuration(u,
                            townHall);
                    //if it will finish, then execute the atomic action
                    if (willcompletethisturn) {
                        //do the atomic action
                        int player = townHall.getPlayer();
                        history.recordResourceDropoff(u, townHall, state);
                        state.addResourceAmount(player, u.getCurrentCargoType(), u.getCurrentCargoAmount());
                        u.clearCargo();
                        //you completed the action, so reset the durative progress
                        u.resetDurative();
                    } else {
                        //increment the duration
                        u.setDurativeStatus(da, newdurativeamount);
                    }
                }
                if (a.getType() == ActionType.PRIMITIVEATTACK) {
                    //make sure you can attack and the target exists and is in range in the last state
                    TargetedAction ta = (TargetedAction) a;
                    Unit target = state.getUnit(ta.getTargetId());
                    int newdurativeamount;
                    if (ta.equals(u.getActionProgressPrimitive())) {
                        newdurativeamount = u.getActionProgressAmount() + 1;
                    } else {
                        newdurativeamount = 1;
                    }
                    willcompletethisturn = newdurativeamount == DurativePlanner.calculateAttackDuration(u, target);
                    //if it will finish, then execute the atomic action
                    if (willcompletethisturn) {
                        //do the atomic action
                        int damage = calculateDamage(u, target);
                        history.recordDamage(u, target, damage, state);
                        target.setHP(Math.max(target.getCurrentHealth() - damage, 0));
                        //you have finished the primitive, so progress resets
                        u.resetDurative();
                    } else {
                        //increment the duration
                        u.setDurativeStatus(ta, newdurativeamount);
                    }
                }
                if (a.getType() == ActionType.PRIMITIVEPRODUCE || a.getType() == ActionType.PRIMITIVEBUILD) {
                    //last state check:
                    ProductionAction pa = (ProductionAction) a;
                    @SuppressWarnings("rawtypes")
                    Template t = state.getTemplate(pa.getTemplateId());
                    //the willcomplete is somewhat related to the production amount
                    int newdurativeamount;
                    if (pa.equals(u.getActionProgressPrimitive())) {
                        newdurativeamount = u.getActionProgressAmount() + 1;
                    } else {
                        newdurativeamount = 1;
                    }
                    willcompletethisturn = newdurativeamount == DurativePlanner.calculateProductionDuration(u, t);
                    //if it will finish, then execute the atomic action
                    if (willcompletethisturn) {
                        //do the atomic action
                        if (t instanceof UnitTemplate) {
                            Unit produced = ((UnitTemplate) t).produceInstance(state);
                            int[] newxy = productionplaces.get(aq);
                            if (u.canBuild()) {
                                int oldx = u.getXPosition();
                                int oldy = u.getYPosition();
                                state.transportUnit(u, newxy[0], newxy[1]);
                                if (state.tryProduceUnit(produced, oldx, oldy)) {
                                    history.recordBirth(produced, u, state);
                                }
                            } else {
                                if (state.tryProduceUnit(produced, newxy[0], newxy[1])) {
                                    history.recordBirth(produced, u, state);
                                }
                            }
                        } else if (t instanceof UpgradeTemplate) {
                            UpgradeTemplate upgradetemplate = ((UpgradeTemplate) t);
                            if (state.tryProduceUpgrade(upgradetemplate.produceInstance(state))) {
                                history.recordUpgrade(upgradetemplate, u, state);
                            }
                        }
                        //you have finished the primitive, so progress resets
                        u.resetDurative();
                    } else {
                        //increment the duration
                        u.setDurativeStatus(pa, newdurativeamount);
                    }
                }
                if (a.getType() == ActionType.PRIMITIVEGATHER) {
                    //check if it can gather at all
                    //find the right node
                    DirectedAction da = (DirectedAction) a;
                    Direction d = da.getDirection();
                    int xdest = u.getXPosition() + d.xComponent();
                    int ydest = u.getYPosition() + d.yComponent();
                    ResourceNode rn = state.resourceAt(xdest, ydest);
                    int newdurativeamount;
                    if (da.equals(u.getActionProgressPrimitive())) {
                        newdurativeamount = u.getActionProgressAmount() + 1;
                    } else {
                        newdurativeamount = 1;
                    }
                    willcompletethisturn = newdurativeamount == DurativePlanner.calculateGatherDuration(u, rn);
                    //if it will finish, then execute the atomic action
                    if (willcompletethisturn) {
                        //do the atomic action
                        int amountPickedUp = rn
                                .reduceAmountRemaining(u.getTemplate().getGatherRate(rn.getType().getResource()));
                        u.setCargo(rn.getResourceType(), amountPickedUp);
                        history.recordResourcePickup(u, rn, amountPickedUp, state);
                        //you have finished the primitive, so progress resets
                        u.resetDurative();
                    } else {
                        //increment the duration
                        u.setDurativeStatus(da, newdurativeamount);
                    }

                }
            }

            ActionResultType compoundFeedback;
            ActionResultType primitiveFeedback = willcompletethisturn ? ActionResultType.COMPLETED
                    : ActionResultType.INCOMPLETE;
            if (!aq.hasNext()) {
                if (willcompletethisturn) {
                    queuedActions.get(u.getPlayer()).remove(aq.getFullAction().getUnitId());
                    compoundFeedback = ActionResultType.COMPLETED;
                } else {
                    compoundFeedback = ActionResultType.INCOMPLETE;
                }

            } else {
                compoundFeedback = ActionResultType.INCOMPLETE;
            }
            history.recordCommandFeedback(state.getUnit(aq.getFullAction().getUnitId()).getPlayer(),
                    state.getTurnNumber(), new ActionResult(aq.getFullAction(), compoundFeedback));
            history.recordPrimitiveFeedback(state.getUnit(aq.getFullAction().getUnitId()).getPlayer(),
                    state.getTurnNumber(), new ActionResult(a, primitiveFeedback));
        }
        for (ActionQueue aq : failed) {
            //should be safe to get the unitid, as it should have not been put into failed if the player was bad
            history.recordCommandFeedback(state.getUnit(aq.getFullAction().getUnitId()).getPlayer(),
                    state.getTurnNumber(), new ActionResult(aq.getFullAction(), ActionResultType.FAILED));
            history.recordPrimitiveFeedback(state.getUnit(aq.getFullAction().getUnitId()).getPlayer(),
                    state.getTurnNumber(), new ActionResult(aq.peekPrimitive(), ActionResultType.FAILED));
            queuedActions.get(state.getUnit(aq.getFullAction().getUnitId()).getPlayer())
                    .remove(aq.getFullAction().getUnitId());
        }

        //Take all the dead units and clear them
        //Find the dead units
        Map<Integer, Unit> allunits = state.getUnits();
        List<Integer> dead = new ArrayList<Integer>(allunits.size());
        for (Unit u : allunits.values()) {
            if (u.getCurrentHealth() <= 0) {
                history.recordDeath(u, state);
                dead.add(u.id);
            }
        }
        //Remove them
        for (int uid : dead) {
            state.removeUnit(uid);
        }
        //Take all of the used up resources and get rid of them
        List<ResourceNode> allnodes = state.getResources();
        List<Integer> usedup = new ArrayList<Integer>(allnodes.size());
        for (ResourceNode r : allnodes) {
            if (r.getAmountRemaining() <= 0) {
                history.recordResourceNodeExhaustion(r, state);
                usedup.add(r.id);
            }
        }
        //Remove the used up resource nodes
        for (int rid : usedup) {

            state.removeResourceNode(rid);
        }

        //Reset the progress of any unit of an active player that was unable to successfully move
        for (Integer id : unsuccessfulUnits) {
            //Grab the unsuccessful unit
            Unit slacker = state.getUnit(id);
            //Reset the progress of the unsuccessful unit if it didn't die or something
            if (slacker != null) {
                slacker.resetDurative();
            }
        }

        state.incrementTurn();
        for (Unit u : state.getUnits().values()) {
            u.deprecateOldView();
        }
        //Set each template to not keep the old view
        for (Integer player : state.getPlayers())
            for (@SuppressWarnings("rawtypes")
            Template t : state.getTemplates(player).values())
                t.deprecateOldView();
    }

    private boolean prerequisitesMet(Template<?> t, int playerNumber) {

        boolean prerequisitesMet = true;
        //check if the prerequisites for the template's production are met
        for (String buildingtemplateid : t.getBuildPrerequisites()) {
            if (!state.hasUnit(playerNumber, buildingtemplateid)) {
                return false;
            }
        }
        if (prerequisitesMet) {
            for (String upgradetemplateid : t.getUpgradePrerequisites()) {
                if (!state.hasUpgrade(playerNumber, upgradetemplateid)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * More or less duplicates the functionality of getClosestPosition in state, with claims.
     * Also returns null instead of -1,-1 if nothing is available.
     * @param producedTemplate 
     * @param x
     * @param y
     * @param claims
     * @param otherclaims
     * @return The closest in bounds position, null if there is none.
     */
    private int[] getClosestEmptyUnclaimedPosition(UnitTemplate producedTemplate, int x, int y, Set<Integer> claims,
            Set<Integer> otherclaims) {
        //This is fairly inefficient so that getCoordInt can be altered without fear
        //It could be somewhat more efficient if it didn't check as many out-of-bounds positions

        //if the space in question is already open
        Integer xy = getCoordInt(x, y);
        if (accessible(producedTemplate, x, y) && !claims.contains(xy) && !otherclaims.contains(xy))
            return new int[] { x, y };
        int xextent = state.getXExtent();
        int yextent = state.getYExtent();
        int maxradius = Math.max(Math.max(x, xextent - x), Math.max(y, yextent - y));
        for (int r = 1; r <= maxradius; r++) {
            //go up/left diagonal
            x = x - 1;
            y = y - 1;

            //go down
            for (int i = 0; i < 2 * r; i++) {
                y = y + 1;
                xy = getCoordInt(x, y);
                if (accessible(producedTemplate, x, y) && !claims.contains(xy) && !otherclaims.contains(xy))
                    return new int[] { x, y };
            }
            //go right
            for (int i = 0; i < 2 * r; i++) {
                x = x + 1;
                xy = getCoordInt(x, y);
                if (accessible(producedTemplate, x, y) && !claims.contains(xy) && !otherclaims.contains(xy))
                    return new int[] { x, y };
            }
            //go up
            for (int i = 0; i < 2 * r; i++) {
                y = y - 1;
                xy = getCoordInt(x, y);
                if (accessible(producedTemplate, x, y) && !claims.contains(xy) && !otherclaims.contains(xy))
                    return new int[] { x, y };
            }
            //go left
            for (int i = 0; i < 2 * r; i++) {
                x = x - 1;
                xy = getCoordInt(x, y);
                if (accessible(producedTemplate, x, y) && !claims.contains(xy) && !otherclaims.contains(xy))
                    return new int[] { x, y };
            }
        }
        return null;
    }

    private boolean inRange(Unit u, Unit target) {
        return DistanceMetrics.chebyshevDistance(u.getXPosition(), u.getYPosition(), target.getXPosition(),
                target.getYPosition()) <= u.getTemplate().getRange();
    }

    private Integer getCoordInt(int xdest, int ydest) {
        return xdest * state.getYExtent() + ydest;
    }

    public void save(String filename) {
        SerializerUtil.storeState(filename, state);
    }
}