So what's your village management style?

Really interesting seeing everyone’s code! Here is the bulk of mine: ec-bot-java/BotService.java at master · RobotSquid/ec-bot-java · GitHub

Featuring such Java 8 stream monstrosities like:

List<UUID> nodes = gameState.getWorld().getMap().getNodes().stream()
                .filter(node -> !(territoryOwner.get(node.getId()) != null && territoryOwner.get(node.getId()).equals(bot.getId())))
                .sorted(Comparator.comparingDouble(node -> -resNodeScoreMemo.get(node.getId())))
                .map(this::trackToBorder)
                .filter(node -> node != null && !currentlyOccupying.contains(node) && territoryOwner.get(node) != null && !territoryOwner.get(node).equals(bot.getId()))
                .limit(1).collect(Collectors.toList());

Here’s the bit that made the magic happen in my bot.

 public PlayerCommand computeNextPlayerAction(GameState gameState) {
        List<StrategicObjective> strategicObjectives = strategicDecisionLayer.generatePrioritizedStrategicObjectives(gameState);
        UnitAllocation unitAllocation = tacticalDecisionLayer.generateTacticalAllocation(gameState, strategicObjectives);
        List<CommandAction> actions = operationalDecisionLayer.executeActions(gameState, unitAllocation);

        PlayerCommand playerCommand = new PlayerCommand();
        playerCommand.setPlayerID(botId);
        playerCommand.setActions(actions);

        return playerCommand;
    }

The first layer was quite rudimentary, doing stuff like this.

public List<StrategicObjective> generatePrioritizedStrategicObjectives(GameState gameState) {
        List<StrategicObjective> strategicObjectives = new ArrayList<>();

        strategicObjectives.add(createFoodObjective(gameState));
        strategicObjectives.add(createWoodObjective(gameState));
        strategicObjectives.add(createHeatObjective(gameState));
        strategicObjectives.add(createStoneObjective(gameState));
        strategicObjectives.add(createGoldObjective(gameState));
        strategicObjectives.add(createBuildingObjective(gameState));
        strategicObjectives.add(createClaimLandObjective(gameState));

        Collections.sort(strategicObjectives);

        return strategicObjectives;
    }

    private FoodObjective createFoodObjective(GameState gameState) {
        FoodObjective foodObjective = new FoodObjective(gameState);

        double importance = 1.05;

        if (gameState.getFood() >= gameState.getPopulation() * 6) {
            importance -= 0.75;
        }

        if (gameState.getFood() < gameState.getHeat()) {
            importance += 0.1;
        }

        foodObjective.setImportance(importance);

        return foodObjective;

The tactical layer populated an object that tracks a counter of available units and units allocated to specific actions. It determines the minimum number of units required for a particular action, and if units are left over, allocates them to the highest priority actions.

@Override
    public void allocateRequiredUnits(UnitAllocation unitAllocation) {
        double foodGap = foodTarget - gameState.getFood();

        if (shouldScout(unitAllocation)) {
            unitAllocation.addScoutUnits(1);
        }

        if (foodGap > 0 && canGather()) {
            int foodUnitCapacityRemaining = gameState.getRemainingFoodGatheringUnitCapacity();

            int requiredUnits = (int) Math.ceil(foodGap / EXPECTED_FOOD_PER_UNIT);
            if (requiredUnits > foodUnitCapacityRemaining) {
                requiredUnits = foodUnitCapacityRemaining;
            }

            int unitsToAllocate = unitAllocation.getMaxAvailableUnits(requiredUnits);

            unitAllocation.addFarmUnits(unitsToAllocate);
        }
    }

    @Override
    public void allocateReserveUnits(UnitAllocation unitAllocation) {
        int currentTier = gameState.getCurrentTierLevel();

        double gapToMax = (Bot.MAX_FOOD_BY_TIER[currentTier]) - gameState.getFood();

        if (gapToMax > 0 && canGather()) {
            int maxUnits = (int) Math.ceil(gapToMax / EXPECTED_FOOD_PER_UNIT);

            int unitsToAllocate = unitAllocation.getMaxAvailableUnits(maxUnits);

            unitAllocation.addFarmUnits(unitsToAllocate);
        }
    }

Finally, the operational layer tries to find the best way to use the allocated units for each action. For resources, it looks at the distance, reward, and travel time to determine the highest “per tick” yield.

public List<CommandAction> executeActions(GameState gameState, UnitAllocation unitAllocation) {
        List<CommandAction> actions = new ArrayList<>();

        if (unitAllocation.getFarmUnits() > 0) {
            actions.addAll(gather(gameState, unitAllocation.getFarmUnits(), ResourceType.FOOD));
        }

        if (unitAllocation.getLumberUnits() > 0) {
            actions.addAll(gather(gameState, unitAllocation.getLumberUnits(), ResourceType.WOOD));
        }

Did the rest of you try and keep your population under control or just expand blindly? I think my bot’s downfall was in growing its population so quickly that it ran out of resources and could no longer sustain it. I saw it shoot up to the top a few times, stay there for a while, and then start losing score rapidly. It worked well when testing against previous versions of itself, but it’s always been super aggressive - so as long as the latest version could stay ahead of the previous versions when the population started crashing, it would do okay.

As far as I know both @WillieTheron and @kobus-v-schoor had some sort of population growth management - it was especially visible with how Willie’s bot caught up and overtook other bots right at the end of each game when all the resources were gone.

My bot just blindly expanded population as efficiently as possible, with the hope that I would have more wood/heat stored up than other bots and be able to keep going for longer before the decline sets in.

2 Likes

I could actually control my bot’s population growth (e.g. I could specify a target growth of zero, which would then use food to keep the population stable at the same value) however I ended up not using it because I ran some simulations and realized that growing the population as fast as possible for as long as possible ended up with more units in the end than properly controlling/stabilizing the population. I suspect that this was the case due to the population drop per round being capped at a maximum of 5% regardless of how little heat you had left. If the population decline wasn’t capped I think a proper population control algorithm would’ve outperformed an always growing one.

@rfnel I like your objective-based approach, I did a similar thing with mine (layers as you called it, in my case it was allocation priorities) except I didn’t do any manual weightings (e.g. I saw you discounted the food gathering once the bot had enough food) as I didn’t want to optimize any weights. By making sure each priority didn’t allocate more units than needed I could greedily allocate (without weighting) to each priority without fear of over-allocating, and this led to most units ending up in the wood burning/gathering priority most of the time (which is where I wanted to be)

1 Like

Yeah,

My own population control took stock at around tick 1600, Then I would calculate when my population would end and start starving out.

It probably would have worked a bit better if I had better measures in place for conquest. As I basically stopped playing. And sadly in round 160 the game was still very much alive.

That said. I told my fiance that if I make 2nd with all the changes I made I will be happy.
Kobus just had something intense about his bot.

So essentially the concept is simple:
Lets say I have 30 000 population. and I starve 10 ticks.

Starving here instead of at endgame means instead of running up to 350 000 Population and then falling like a hammer, I drop my population early. so if I kill 5000 units in tick 1600.I save close to 6 Million on my endgame heat requirements.

Its much cheaper than just running till endgame.
But I had a bit of territory control problems. But yeah, Very happy. Kobus really saved our skin with engine updates.

Heres some code that will make your lexturers and textbooks run wild with my 4 dimentional arrays…

But I dont mind, I just do the challenge that way…
Like every artist has their style.

1 Like
//Simplified Stone (Rewrite 11 September 2022 after seeing the Matrix) Rebuilt the way I handle Stone, wood and gold. 

I have to ask, what inspiration did you divine from watching the Matrix? :slight_smile:

Oh no,

I just had a moment where I could see the number patterns.

I essentially use to have 4 or so different resource need calculations.

Checked my needs
Then Minimum storages
Then Maximum Storages
Then I would try and future farm res that will be minable if by the time its done my population tier should uptick
Then I would also try and farm the res that buildings will burn ahead of time.

I also had an issue where I was calculating my needs based on my current resources. With some offsets that just messed me up.

So I shifted things a bit and merged all of this and made my res gathering real time and set essentially a single need.

I did rewrite a lot of my bot on 11 September.
But my endgame on my 3rd event was much stronger.

Still very happy.
It was more a reference on the scene that Neo had when he finally saw the truth.
Probably more a way to amp myself up.

1 Like