Jump to content

OSBot Scripting Decision Tree Framework


TheJacob

Recommended Posts

 

 

Hey folks, here's a decision tree framework for writing Scripts on OSBot. My intent is to simplify the process of making iterative decisions and provide flexibility to the user should they wish to change the decision model. I will be using something similar to this in order to build a F2P PKing Script, so let me know your thoughts on where it can be improved!

OUTDATED: CLICK HERE (for a behavior tree framework)

Source

Download: FastUpload - 1.1 (25 11:00 Jan 2023)

Changes

  • 1.1 - removed redundant if condition in deserialize method.
  • 1.0 - initial release.

Setup

  1. Create a "DeepButler" folder in OSBot's Data folder (getDirectoryData).
  2. Create a "deepbutler.json" file in the "DeepButler" folder.
  3. Use the example decision tree (and later have fun making your own!)
  4. Modify your OSBot to allow reflection (add "-allow reflection" to run.bat).
  5. Enter W326 and a secondary world to test the framework.

Remarks

  • This framework has not been tested on any scripts yet. I intend to use a similar iteration of this framework on a F2P PKing Script.
  • Create new Conditions and Tasks, and change the decision tree as required.
  • The decision tree doesn't necessarily have to be processed in "onLoop". In fact, I think there's great potential by listening for game tick events.
  • Every Task is a Condition. It makes sense to simply return "true" in Tasks that do not branch anywhere (prompt a re-loop), otherwise use them as a condition & a task if there's subsequent decisions to be processed.
  • The "Cond" annotation is something I was trialing in lieu of reflection. Not integrated at the moment.

Snippets

deepbutler.json

Spoiler
{
  "name": "conditions.CheckWorld",
  "no": {
    "name": "conditions.tasks.LogToHop",
    "no": null,
    "yes": null
  },
  "yes": {
    "name": "conditions.IsMoving",
    "no": {
      "name": "conditions.tasks.LogNotMoving",
      "no": null,
      "yes": null
    },
    "yes": {
      "name": "conditions.tasks.LogMoving",
      "no": null,
      "yes": null
    }
  }
}

 

DeepButler.java

Spoiler
public final class DeepButler extends Script {
    private Condition dto;
    @Override
    public void onStart() {
        DecisionTreeParser dtp = new DecisionTreeParser(getBot());
        dtp.load(getDirectoryData());
        dto = dtp.parse();
    }
    @Override
    public int onLoop() {
        Condition parser = dto;
        int opcode = 1;
        // Terminate by reaching end of decision tree
        // or on any opcode of 0 or less.
        while ((parser != null) && opcode > 0) {
            if (parser.check()) {
                if (parser instanceof Task) {
                    opcode = ((Task) parser).execute();
                    // Affect decision tree based on opcode.
                    // ...
                }
                parser = parser.getYes();
            } else {
                parser = parser.getNo();
            }
        }
        return 600;
    }
}

 

Decision Tree Parser (deserialize method)

Spoiler
private Condition deserialize(JsonElement node) {
    if (node.isJsonNull()) {
        return null;
    }
    Condition task;
    JsonObject jObj = node.getAsJsonObject();
    try {
        task = (Condition) Class.forName(jObj.get("name").getAsString()).
                getConstructor(Bot.class).
                newInstance(bot);
    } catch (ClassNotFoundException e) {
        bot.getMethods().log("Reflection: Mistake in class name or type, see deepbutler.json.");
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        bot.getMethods().log("Reflection: Condition or Task instructor implemented incorrectly.");
        throw new RuntimeException(e);
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
        bot.getMethods().log("Reflection: Other issue.");
        throw new RuntimeException(e);
    }
    task.setNo(deserialize((jObj.get("no"))));
    task.setYes(deserialize((jObj.get("yes"))));
    return task;
}

 

Edited by TheJacob
Released a different framework that's much more practical. Linked.
  • Like 2
Link to comment
Share on other sites

8 hours ago, Czar said:

I will check this out, respect for going away from the norm in scripting and trying new (+ better) stuff :doge: 

Thanks @Czar! I'm still figuring out a few more things about the OSBot api, but I'm aiming within the next two weeks to have a script released demonstrating this framework (more complicated than the simple one I included with this release).

I'm sure there's still some drawbacks with it's current design, so definitely give it a look and let me know what you think. Open to all feedback.

For example, I don't know how well this framework will perform when more than one decision is needed in a tick, or how to handle running concurrent tasks (such as moving and eating -- maybe hooking in to OSBot's Event API).

Edited by TheJacob
  • Like 1
Link to comment
Share on other sites

Interesting concept! My only concerns are the following. 
 

1. I don’t think reflection will work on the SDN. So this would only be useful for scripts not going on OSBots repository. (I experienced this issue when I made Script Factory).
 

2. Make sure to cache the entire JSON during runtime instead of calling the JSON in a loop. This will fix any bogging of the script if either your JSON file gets extremely large, or needing to loop your script faster.

  • Like 1
Link to comment
Share on other sites

17 minutes ago, ProjectPact said:

Interesting concept! My only concerns are the following. 
 

1. I don’t think reflection will work on the SDN. So this would only be useful for scripts not going on OSBots repository. (I experienced this issue when I made Script Factory).
 

2. Make sure to cache the entire JSON during runtime instead of calling the JSON in a loop. This will fix any bogging of the script if either your JSON file gets extremely large, or needing to loop your script faster.

Oh ok, I didn't know about that! I think I can change the design such that it doesn't use reflection. The reason I decided to go with that design in the first place was to reduce code clutter while parsing the decision tree (making the assumption that the script does not know about all the conditions & tasks). In order to get rid of reflection, I can make the script aware of all conditions & tasks while still leaving the decision tree to be dynamic to the user.

The 'parser' variable in 'onLoop' should be holding a reference to a spot in the tree at all times rather than a copy of the tree. If I'm not mistaken, this line passes a reference by value rather than copying the whole tree.

Condition parser = dto;

I might be wrong though, where do you see it copying the whole decision tree or JSON parser every loop?

  • Like 1
Link to comment
Share on other sites

3 hours ago, TheJacob said:

Oh ok, I didn't know about that! I think I can change the design such that it doesn't use reflection. The reason I decided to go with that design in the first place was to reduce code clutter while parsing the decision tree (making the assumption that the script does not know about all the conditions & tasks). In order to get rid of reflection, I can make the script aware of all conditions & tasks while still leaving the decision tree to be dynamic to the user.

The 'parser' variable in 'onLoop' should be holding a reference to a spot in the tree at all times rather than a copy of the tree. If I'm not mistaken, this line passes a reference by value rather than copying the whole tree.

Condition parser = dto;

I might be wrong though, where do you see it copying the whole decision tree or JSON parser every loop?

I was just referring to your comment:

  • The decision tree doesn't necessarily have to be processed in "onLoop". In fact, I think there's great potential by listening for game tick events.

:D But still, a really cool concept and a different way to look at processing.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...