michaelzangl / minebot

Minebot
GNU General Public License v3.0
144 stars 48 forks source link

How Do I Limit Lumberjack? #155

Closed opticyclic closed 4 years ago

opticyclic commented 7 years ago

Background: I want enough tree blocks to craft a crafting table, some sticks then a wooden tool so that I don't keep breaking things with my hands. I'm ultimately aiming for a /minebot createtool strategy

Is it currently possible to limit the Lumberjack so that it stops when you have chopped a certain number?

If not, what is the best way to code this? Do we overload the current strategy with a block limit parameter or do we modify the existing method and add an optional parameter?

Edgaras1 commented 7 years ago

getInventory() and loop through items

opticyclic commented 7 years ago

The custom stop condition example here looks like it should be doable in javascript. https://github.com/michaelzangl/minebot/wiki/Javascript-Examples

That made me wonder about modifying the Java code instead.

I was initially going to add an if statement somewhere in the TreePathFinder but then I considered adding a StopOnConditionStrategy to the CommandLumberjack (see below for example code).

However, I'm not sure whether this is the right way to architect it or not. e.g. Is it OK for a command to return a StrategyStack or should it just return one and stop based on the parameters passed?

If the extra strategy is correct, should the StopOnConditionStrategy be refactored to its own class and made generic to count any type of block?

net.famzangl.minecraft.minebot.ai.commands.CommandLumberjack

    @AICommandInvocation(safeRule = SafeStrategyRule.DEFEND_MINING)
    public static AIStrategy run(
            AIHelper helper,
            @AICommandParameter(type = ParameterType.FIXED, fixedName = "lumberjack", description = "") String nameArg,
            @AICommandParameter(type = ParameterType.ENUM, description = "wood type", optional = true) WoodType type,
            @AICommandParameter(type = ParameterType.NUMBER, description = "no. tree blocks", optional = true) final Integer treeBlocks,
            @AICommandParameter(type = ParameterType.FIXED, fixedName = "replant", description = "", optional = true) String replant) {
        TreePathFinder pathFinder = new TreePathFinder(type, replant != null);
        String wood;
        if (type == null) {
            wood = "wood";
        } else {
            wood = type.toString().toLowerCase();
        }
        final StrategyStack stack = new StrategyStack();
        if (treeBlocks > 0) {
            StopOnConditionStrategy strategy = new StopOnConditionStrategy(new StopOnConditionStrategy.StopCondition() {
                @Override
                public boolean shouldStop(AIHelper helper) {
                    InventoryDefinition inventory = new InventoryDefinition(helper.getMinecraft().thePlayer.inventory);
                    for (int i = 0; i < 36; i++) {
                        InventoryDefinition.InventorySlot slot = inventory.getSlot(i);
                        if (isWood(slot)) {
                            if (slot.amount > treeBlocks) {
                                return true;
                            }
                        }
                    }
                    return false;
                }

                private boolean isWood(InventoryDefinition.InventorySlot slot) {
                    if (!slot.isEmpty()) {
                        try {
                            WoodType.getFor(slot.getItem().getBlockType());
                            return true;
                        } catch (IllegalArgumentException e) {
                            //this isn't wood
                        }
                    }
                    return false;
                }
            }, true, "Stop on number of tree blocks");
            stack.addStrategy(strategy);
        }
        TreePathFinderStrategy treePathFinderStrategy = new TreePathFinderStrategy(pathFinder, "Getting some " + wood);
        stack.addStrategy(treePathFinderStrategy);
        return new StackStrategy(stack);
    }
michaelzangl commented 7 years ago

Hi,

I added those stop conditions to be as universal as possible. This means: Do a new command. Have a look at the one that stops on full inventory.

About your inventory counting: Have a look at the BlockSets class. It contains a block set for Wood Blocks. That set has a method to check if the given block id is wood. try/catch is not good.

Currently, you only check one stack of wood. You should sum up all wood stacks and compare that value.

opticyclic commented 7 years ago

I fixed the summing up.

I know you said do a new command but I feel like we should be able to add a limit parameter to the lumberjack and add a check periodically that the count is not more than the limit.

However, I am still struggling with the architecture. If I don't use a stopstrategy like above then I need a way to stop the tasks from repeating. Even when I look at the tunnel command that takes a length parameter I don't follow what stops it.

How does a command know to stop? I think it relates to the number of tasks but I am not sure which method to put the limit check in and how to empty any outstanding tasks.

Its also not clear on why a command would stop. The commands generally end up using a Stack with things like eat and don't die. How does one strategy in the stack finishing close the entire stack? e.g. if you eat whilst tunnelling, it finishes eating but carries on tunnelling but when you finish tunnelling the whole stack ends. What is the difference that causes this?

michaelzangl commented 7 years ago

The command does not really know how to stop. The command knows what to do next. The tunnel-command is quite simple: It digs a tunnel between point A and B. And the length parameter defines point (together with the direction). The tunneling command can dig backwards as well - simply because the strategy searches for places that have not been cleared and schedules tasks to clear them.

Including this in the lumberjack is quite difficult: You would need to check the number of wood destruction tasks that succeeded (you would need to capture task results for this). And you would need to check how many wood blocks have been destroyed at the current state the stategy is planing.

The strategies plan on a future world (WorldWithDelta). That world contains all modifications the bot will have done when the current tasks are executed. This is due to the asynchronous nature of strategies: They use a lot of computation power and therefore always compute ~5 seconds in advance (so they simulate the scheduled gameplay until then and then plan starting at the state the bot is at after that state).

Strategies can modify the stack in several ways:

Stack construction is done by evaluating the command annotations - annotations are heavily used during command evaluation.