Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
wastedbro

Overview of Script Decision-Making Methodology

Recommended Posts

Decision-Making Methodology

This post is meant to provide an overview of the different techniques one can utilize in script development. These concepts evolve largely by video game AI programming, and have their uses in both simple and complex decision making.

Scripts largely represent many of the paradigms one may see in the video game industry, and depending on their scope, it's worth considering what methodology you adopt to accomplish your goals. Using a basic understanding of the underlying theory behind your logic programming, you can better organize your code and possible create logic that is less prone to bugs.

Note: This post is targeted at intermediate to advanced developers

Finite State Machines

Detailed Information

Java/TRiBot Frameworks

Summary

Finite State Machines (FSM) are the most common form of decision making, and take on many forms. A finite state machine can be described as a system that, when executed, outputs a single state from a finite number of predetermined states. Once the state is calculated, an action is to be performed. 

FSM technique requires nothing special. This is technically an FSM:
 

if(isAtBank())
{
  if(Banking.isBankScreenOpen())
  {
    // Do banking
  }
  else
    Banking.openBank();
}
else if(isAtTrees())
{
  // Chop trees
}

FSM implementations can be as simple as complex as you make them.

A decision tree is perhaps the most complex version of a FSM that people use for developing scripts. The decision tree decides its state in a hierarchy. What that means is that each state is a composition of smaller states. A child-state can only be active if its parent state is active.

The benefit of decision trees is that you never have to repeat logic. If two states both require that the player is in the bank, you can have a state for bank location checking, and then child states to check for common conditions that only apply within the bank. Doing so reduces condition computation and better organizes the code. 

Pros

  • Easy to conceptualize, learn, and implement.
  • Reacts very well to sudden changes in the game. Since the FSM is repeatedly executed, it can evaluate the state from a neutral context each time
  • Works without the overhead of implementing a large framework.

Cons

  • As the number of states grows, the code can get messy without a framework.
  • FSM usually has no concern for past events, and therefore throws away information about its actions. While this can be mitigated, the FSM is simply not the best methodology to use if you want to implement such behavior.
  • Reacts poorly to modification. Even though states are independent, they still depend on each other in order for the whole system to work. Introducing new states can adversely affect others. FSM operates at a global-level, so altering it has a ripple effect. The Decision Tree is less vulnerable to this problem than other implementations.

 

Behavior Trees

Detailed Information

Java/TRiBot Frameworks

  • Fluent Behavior Tree (by @wastedbro[Coming soon!]TM

Summary

The Behavior Tree is a decision-making technique in which you have a tree of nodes, with each node required to return SUCCESS or FAILURE. Therefore, each action your script performs is a precursor to the next. 

Let's take this example:

if(isAtBank())
{
  if(Banking.isBankScreenOpen())
  {
    // Do banking
  }
  else
    Banking.openBank();
}
else
{
  // Go to bank
}

If we run this code twice at the bank, we will see the script open the bank, but let's look at how it got to that decision.

  1. See that we're at the bank
  2. See that the bank screen isn't open
  3. Opens the bank
  4. ** Restart main loop **
  5. See that we're at the bank
  6. See that the bank screen is open
  7. ........ 

Notice how we repeated this code: isAtBank()

Why? Banking.openBank() has the capability of telling us if it successfully opened the bank, so why aren't we using that data?

Here's the same code in a behavior tree:

.sequence("Ensure bank is open")
  .selector("Ensure we are at the bank")
    .condition(()-> isAtBank())
    // Action to walk to the bank
  .end()
  .selector("Open bank")
    .condition(Banking::isBankScreenOpen)
    .condition(Banking::openBank)
  .end()
.end();

Now, if you aren't too familiar with behavior trees, this will look like nonsense. That's fine. Behavior trees take time to learn and become comfortable with. But this code will:

  1. Check if we're at the bank
    1. If not, walk to the bank
  2. Check if the banking screen is open
    1. If not, open the bank

The whole thing is wrapped in a sequence. The top-level sequence node Ensures the bank is open. That's its contract. We can use this block of code to open the bank and depend on it to do that.

As you might be thinking, this seems pretty crazy for something so simple! And you're right. Behavior trees don't shine too much with simple logic. As the logic gets more complex, behavior trees start showing their advantages.

While ensuring condition checking is a nice feature of this methodology, it's not the best part. The greatest advantage is ease of modification. Every node is sectioned off and independent. When modifying a piece of logic, all you need to do is ensure you don't modify the contract of the immediate parent, and you can be sure you're not affecting the rest of the script.

That is the reason @Naton and I chose to use this for nwSlayer.

Pros

  • Executes logic with contextual knowledge. This allows us to chain together actions based on the outcome of a previous action, without re-evaluation. The result is increased logic stability.
  • Very open to modification. Adding logic to a behavior tree is simple because you can place logic at the part of the tree where the context makes sense, without altering the global state. Doing so allows you to avoid altering any other parts of the logic that you didn't intend to.
    • Since each node of the tree has a contract, all you have to do is ensure the parent node of your new logic doesn't alter its parents contract.

Cons

  • Complex to learn and implement.
  • Pretty much requires a framework.
  • Since much of the execution of a behavior tree exists in a local context and not the global state, it can be difficult to handle abnormal events. For example, implementing Anti-PK into a behavior tree is difficult (thought it's still very possible).

 

Utility AI (Goal-Based-Logic)

Detailed Information

Java/TRiBot Frameworks

  • N/A

Summary

Utility AI is a technique in which you program a series conditions that are checked constantly, similar to a FSM. However, the difference comes in the fact that the conditions don't directly determine a specific state.

Using Utility AI, we will use these terms:

  • Actions - Things that the script will do.
  • Scorers - These can be thought of as conditions.

Every time the Utility AI system runs, it determines a score for each action. The action with the highest score is the one that gets executed. This allows for a much simpler development process and the result is a script that is capable of handling situations in which you didn't quite anticipate. It also allows you to easily add actions and scorers.

However, the result is virtually non-deterministic algorithm. That kind of behavior doesn't really work for Runescape bots because bots need to be deterministic in order to perform the automation we want to perform.

However, there are certain script ideas that could benefit a lot from this kind of logic technique. For example, let's say you wanted to create a script that performs various actions around Runescape to simulate a real player. Perhaps do some goldfarming methods if the player is low on gold, training stats that are low, and taking into account many other factors, such as distance to the task, last time the task was performed, etc.

In such a script, Utility AI would be incredibly useful.

Pros

  • Can make complex decisions very easily.
  • Can perform under unexpected circumstances (fuzzy logic).
  • Easy to implement and maintain.

Cons

  • Not suitable for most Runescape script decision making.
  • Easy to accidentally program weird logic that is difficult to determine how it occurred. 
  • Fewer frameworks and support.
  • Like 1

Share this post


Link to post
Share on other sites

Nice write-up! 

Maybe you could touch up on when a certain sequence in a behaviour tree is executed? Your example includes a sequence to open the bank, but not the process of which sequence is chosen to be executed.


Personally, my framework would fall under the Utility AI part. Although i've taken a slightly different approach and wrote it from a bottoms-up perspective. For example, I have a class for Aubury, the rune shop owner in Varrock. Within this class contains the logic for buying runes, having him teleport you to the essence mine, as well as the dialog required for Rune Mysteries. Depending on the goal you are executing, he will do one of those things. Basically, from a script perspective, the NPC (or object, or interface, etc) knows how the player should interact with it.

This gives great decoupled flexibility, and makes sense for Runescape to me since all entities have a fixed way of interaction. A con would be that it is harder for the logic to do script specific things, Think having a GUI option to only buy 15 runes. To remedy this, i created an event framework based on the mediator pattern (influenced by the Symfony Event Dispatcher) which allows me to plug into anything from anywhere.

Share this post


Link to post
Share on other sites
12 minutes ago, Laniax said:

Nice write-up! 

Maybe you could touch up on when a certain sequence in a behaviour tree is executed? Your example includes a sequence to open the bank, but not the process of which sequence is chosen to be executed.


Personally, my framework would fall under the Utility AI part. Although i've taken a slightly different approach and wrote it from a bottoms-up perspective. For example, I have a class for Aubury, the rune shop owner in Varrock. Within this class contains the logic for buying runes, having him teleport you to the essence mine, as well as the dialog required for Rune Mysteries. Depending on the goal you are executing, he will do one of those things. Basically, from a script perspective, the NPC (or object, or interface, etc) knows how the player should interact with it.

This gives great decoupled flexibility, and makes sense for Runescape to me since all entities have a fixed way of interaction. A con would be that it is harder for the logic to do script specific things, Think having a GUI option to only buy 15 runes. To remedy this, i created an event framework based on the mediator pattern (influenced by the Symfony Event Dispatcher) which allows me to plug into anything from anywhere.

Good point. I should really release something open-source that uses the framework. But generally, that logic has an infinitely flexible scope. The sequence is simply part of a larger sequence, which might be part of a large one, and so on, until eventually, you have 1 single node that represents the execution of your entire script. I'll see if I can clear that up.

 

Your framework sounds very interesting. I've never considered a bottom-up approach to script logic. I'm having some trouble understanding exactly how it forms together. Do you have any resources or examples of such structure?

 

32 minutes ago, Naton said:

Interesting stuff, I actually recently wrote my own binary decision tree framework inspired by your original decision tree idea.

I'm glad my Decision Tree Framework was able to help/inspire people. I definitely want to rewrite into something a lot more usable and add functional paradigms to the API, similar to how we build behavior trees.

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Our picks

    • This release will:

      Fix LG for both OSBuddy and RuneLite


      Fix issue where the resizable client isn't able to be made smaller (Thanks @JoeDezzy1)


      Fix detection of the logout game tab when resizable mode and side panels are enabled (Thanks @JoeDezzy1)


      Add initial support for Sentry to allow us to identify and easily debug exceptions happening with all TRiBot users


      Add methods to determine if the bank is actually loaded, and not just the overarching interface (Thanks @wastedbro)



      Upcoming updates:

      Improved CLI support


      Full Sentry support


      Much more
        • Like
      • 50 replies
    • This release will:

      Fix NPE in Camera API (Thanks @wastedbro)


      Update deposit box interface ids (Thanks @Encoded)


      Add various bank methods (Thanks @wastedbro)


      Banking#getWithdrawXQuantity


      Banking#getDefaultWithdrawQuantity


      Banking#arePlaceholdersOn




      Fix resizeable minimap bug (Thanks @wastedbro)


      Remove Java 8 requirement


      Please note: TRiBot is not yet fully compatible with Java 10+




      Fix the break handler issues by ensuring the break handler thread never gets paused


      Fix broken settings hooks



      Upcoming updates:

      Improved CLI support


      Much more



      Note: If you are using LG, please restart both the RS client and TRiBot
        • Like
      • 68 replies
    • This release will:

      Add support for using custom F key bindings to switch between game tabs (Thanks @erickho123)


      Fix tab opening for "Skills" and "Kourend Tasks" (Thanks @erickho123)



      Note: If you are using LG, please restart both the RS client and TRiBot
        • Like
      • 34 replies
    • This release will:

      Fix an issue where breaks would stop firing


      Fix Combat#getWildernessLevel, use dynamic search for text and cache ID for later calls


      Fix an NPE in the Combat API


      Fix Mouse#leaveGame bug where the mouse wouldn't actually leave the game screen
        • Like
      • 21 replies
    • This release will:

      Add LG support for Runelite


      Fix NPCChat issues


      Fix a bug where the camera angle setter would just hold down a key for 5 seconds (the timeout)


      Slightly adjust the rotation via keys to be more accurate


      Add the ability for asynchronous camera movement via keys


      Make Camera rotation via mouse more fluid, with more antiban, and work much better in resizable mode


      Add a "Camera#setCamera" method, allowing the rotation and angle to be set in parallel


      Increase the likelihood of using the mouse for camera movements


      Add support for adjusting the camera to positionable entities (Positionable#adjustCameraTo)



      Upcoming updates:

      Improved CLI support


      Much more



      Note: If you are using LG, please restart both the RS client and TRiBot
        • Thanks
        • Like
      • 59 replies
  • Recently Browsing   0 members

    No registered users viewing this page.

×