Navigation

    CreatiCode Scratch Forum

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • CreatiCode

    Game AI for <Candy Truck Battle> - Part 2 (Difficulty: 5)

    Tutorials
    1
    1
    17
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • info-creaticode
      CreatiCode last edited by

       

      Introduction

       

      In part 1 of this tutorial, we built a basic AI controller for the game. That AI could already move the truck to random locations, and even seek and attack the opponent.

      In this second part, we’re going to supercharge our AI! We’ll add two major new skills: collecting power-ups and avoiding tree obstacles. With these upgrades, our AI will become a much tougher competitor in battle.

      Here is a demo of what we’ll be building:

      detour2.gif

       
       
       
       
       
       
       
       
       
       
       

      Version 4 - Collecting Powerups (Difficulty: 4)

       

      To get our truck to collect powerups, it first needs to know where they are. Then, it has to drive over to them. A key part of the logic will be deciding when to go for a powerup versus when to keep attacking the opponent.

       
       

      Step 1 - Duplicate the Previous AI Code

       

      We’ll be building on the AI controller we made in Part 1 (Version 3). Go ahead and open that project, and then save a copy of it. This way, you’ll always have the original version to go back to if you need it.

       
       

      Step 2 - Check for Powerups

       

      To collect powerups, our AI first needs to know that they’ve appeared on the map. It only makes sense to change the truck’s behavior after a powerup is available. The best spot for this check is right at the start of our main forever loop.

      Let’s create a new custom block and name it check powerups. Important: Make sure to check the box for “Run without screen refresh.” This gives our code a nice little speed boost.

      cbb87297-8004-41a3-a631-2aa8acb22c26-image.png

       
      And we will place this new block at the beginning of the main loop:

      45833864-1dcc-432a-a1e6-ece31fffedb9-image.png

       
       

      Step 3 - Check Our Life Count

       

      Remember, there are two types of powerups in the game:

      • Sugar: Increases our truck’s life count (up to a max of 3).
      • Gas: Boosts our truck’s speed (up to a max of 120).

      It makes sense to prioritize survival. If our truck has fewer than 3 lives, getting a Sugar powerup should be the top priority. Let’s start by adding a check for that.

      6719afe5-2e75-42b8-90b3-9256da7445a2-image.png

       
      Note that the ☁ my lives variable keeps track of the life count for our truck, which is identified by the my team ID. This sensing block is very handy when you need to access any private property of a clone so long as you know its ID.

       
       

      Step 4 - Look for Sugar Powerups

       

      If our truck needs health (meaning it has less than 3 lives), it’s time to scan the area for any Sugar powerups. We can do this using the special find clones of sensing block.

      b4b1e8a3-906c-416f-a351-1fada5611c7f-image.png

       
      This block is incredibly useful. It searches for all visible clones of a specific sprite within a certain distance. Since we want to find Sugar anywhere on the map, let’s use a large distance like 1000. Any Sugar clones it finds will be listed in the table1 variable.

       
       

      Step 5 - Check If Table1 is Not Empty

       

      If the find clones of block successfully located one or more Sugar sprites, it will add them as rows to table1. So, to see if we found anything, we just need to check if the number of rows in table1 is greater than 0.

      c4cdd455-ad89-4022-a13b-8432f10c8804-image.png

       
       

      Step 6 - Update Mode and Target Location

       

      Okay, we’ve found a Sugar powerup! The next step is to drive to it. The table1 is conveniently sorted by distance, meaning the very first row always points to the closest clone. For example, this screenshot shows that one Sugar clone was found. It has a clone ID of 1, is located at X: 160 and Y: -65, and is 96.43 units away from our truck.

      9703166c-6275-4db8-b8bf-1bdf4fa5acb0-image.png
       
      Now, we could create a whole new mode like “collect sugar,” but there’s a clever, simpler way. We can just reuse our existing wander mode! All we have to do is update the target x and target y variables with the location of the Sugar item. The existing code in the wander block will then automatically navigate our truck right to it.

      4e730dc1-66f1-4de3-9955-88612152a8fa-image.png

       
       

      Step 7 - Return from “check powerups”

       

      Once we’ve spotted a Sugar clone and decided to go for it, our job inside the check powerups block is done for this cycle. There’s no need to continue and check for Gas powerups. We can use a return block to exit the custom block immediately.

      1e04af61-9176-4c8e-ad32-c4316474f250-image.png

      Pro Tip: It’s a common misunderstanding that the return block is only for reporter blocks (the ones that return a value). You can also use it in regular stack blocks like this one to simply stop the block from running any further. Its return value is just ignored. This is equivalent of using the stop [this script] block.

       
       

      Step 8 - Look for Gas Powerups

       

      If our truck is at full health, or if there were no Sugar powerups to find, the code continues. Now we can check for Gas powerups. But, there’s no point in grabbing a Gas powerup if our speed is already maxed out at 120. So, let’s first check our ☁ my speed variable.

      a794da49-9c47-4c70-9dd0-9cd7a8a22943-image.png

       
       

      Step 9 - Complete the Rest

       

      The logic for finding and collecting Gas clones is nearly identical to what we just did for Sugar. See if you can build the rest of the code yourself! It’s great practice.

       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
      Here is the complete solution for the check powerups block:

      d2e9237d-676c-4501-b8f7-e548cec0e7d2-image.png

       
       

      Step 10 - Test This Version

       

      Time to test our new powerup-collecting AI! To focus just on this new skill, let’s keep the tree count at 0 for now (you can change this in the Setup sprite).

      It’s also really helpful to make the table1 variable visible on the stage. This way, you can see exactly when the AI finds a powerup.

      One last tip for faster testing: in the Sugar sprite, you can shorten the time it waits before reappearing. For example, try changing the random wait to between 3 and 9 seconds.

      a5d7f091-2195-474d-ad4d-c21cdff56bd8-image.png

       
      Now you can test your AI by opening the project in two browser tabs and letting them battle. It should look something like this:

      collect.gif

       
       
       
       
       
       
       
       
       
       
       
       

      Version 5 - Avoiding Trees (Difficulty: 5)

       

      Now for a real challenge: obstacle avoidance! Right now, our truck will just drive straight into trees and get stuck, completely stopping it from chasing the opponent or grabbing powerups. We’re going to fix this by teaching our AI how to detect when a tree is in the way and then plot a “detour” to get around it.

       
       

      Step 1 - Export Version 4 for Backup

       

      First thing’s first. Let’s make a backup of our working powerup-collecting version. Export your Controller sprite as a file (it will be named something like sprite3). This way, if anything goes wrong, you can easily import this version and start again.

      a8868a00-4c11-4e0d-9e16-769f250b99c2-image.png

       
       

      Step 2 - Define a New Reporter Block

       

      To avoid trees, our AI first needs a way to “see” if its path is blocked. We’ll create a new custom block that can answer a simple question: “Is this direction blocked?”. Let’s name it direction is blocked, and it will take the command as an input. Make sure to select “Run without screen refresh” and define it as a reporter block. A reporter block returns a value; ours will return 1 if the path is blocked and 0 if it’s clear.

      44a2c9ee-ca6a-4c17-9804-997c0ab06b35-image.png

       
       

      Step 3 - Discussion On How to Detect Tree Obstacles

       

      Before we jump into coding, let’s talk strategy. How can our Controller sprite, which only sends commands, know if the Truck sprite is about to hit a tree?

      In standard Scratch, you might try a “ghosting” technique: quickly move the sprite forward, check for a collision, and move it back before the screen refreshes. But that won’t work here, because our Controller sprite and Truck sprite are separate. The Controller can’t directly move the Truck to test for collisions.

      So, we need a different, more clever approach:

      1. We’ll temporarily move our invisible Controller sprite to the exact location of our Truck.
      2. We’ll point the Controller sprite in the direction we want to move.
      3. From that position, we’ll use a new sensing block to scan a rectangular “detection area” in front of the sprite to see if any trees are inside it.

      f4986eed-18f1-4579-bb80-c3b2c3ffc0f1-image.png

       
      Let’s implement this idea in the next few steps.

       
       

      Step 4 - Move the AI sprite to the Truck

       

      First, let’s get the current X and Y position of our truck using its my team clone ID. Then, we’ll use a go to x y block to instantly move our Controller sprite to that same spot.

      175785d7-a156-4a1c-8912-5bf7c015687b-image.png

       
      Remember, the Controller sprite is invisible, so you won’t see anything change on the stage. The whole point of this move is to establish a starting point for our tree-detection scan.

       
       

      Step 5 - Face Upward if Command is 1

       

      If the command we’re checking is 1 (move up), we need to check for trees above the truck. To do this, we’ll point our Controller sprite upwards (direction 0). This step is crucial because the detection area we’re about to create is always relative to the direction the sprite is facing.

      7685dd78-7648-4247-91ed-471070c53c5d-image.png

       
      Again, we’re just using the invisible sprite’s direction to set up our detection area.

       
       

      Step 6 - Handle the Other 3 Directions

       

      Similarly, we’ll handle the other three possible commands (2 for down, 3 for left, and 4 for right), pointing the Controller sprite in the corresponding direction each time.

      fbccfb61-6302-48cd-bf48-84755e14ddb7-image.png

       
       

      Step 7 - Detect Tree Clones in a Rectangle Area

       

      With our Controller sprite now at the truck’s location and pointing in the direction of intended movement, we’re ready to scan for trees. We’ll use the find clones of sprite in rectangle block. Here’s how this powerful block works for our setup:

      c23887b5-3c95-4658-b4dc-95616a62c3ce-image.png

       
      Explanations:

      • We are looking for visible clones of the Tree sprite. The original Tree sprite is hidden, so this check will correctly ignore it.
      • The rectangle is defined relative to the Controller sprite’s forward direction. So if the command is 1 (up), the Controller is facing upward, and the rectangle will extend 60 units forward and expand 90 units wide, like this:
        e53ff571-b6ed-40ee-bf1a-ac615cddc752-image.png
         
      • We will store information about all clones found in the table1 variable.

       
      Feel free to fine-tune the distance and width values. Think of distance as how far ahead the AI can “see,” and width as its “peripheral vision,” or how wide you want to scan.

      If the center of any Tree clone is inside this rectangle (i.e. its center point falls into this rectangle), it will be listed in the table, which includes columns for clone ID, x, y, and distance.

      884533a1-10be-42c5-b409-1fab2058027b-image.png

       
      If more than one clone is found, the table will be sorted by distance, with the closest one listed first.

       
       

      Step 8 - Check If Table is Empty

       

      We don’t need the specific details of the trees, just whether any tree was found. So, we can simply check if table1 has any rows. If the row count is greater than 0, a tree is blocking the path, and our block should return 1. Otherwise, the path is clear, and it should return 0.

      85f2ebe6-6b1d-4a3c-8d31-42ce5156a618-image.png

       
       

      Step 9 - Check if Command Direction is Blocked

       

      Now we can put our new reporter block to use! Back in the main loop, right before we set the new command, we’ll call direction is blocked to check the path.

      Also, to make our AI more responsive and quicker to react to obstacles, let’s reduce the wait time in the main loop to 0.1 seconds. This allows it to check for blockages more frequently.

      dd6c0245-3d1d-4259-bab7-207b0f9a1321-image.png

       
       

      Step 10 - Calculate a Detour Command

       

      If our check finds that the intended direction is blocked, we need to calculate a detour. We’ll create another new custom block, calculate detour, to handle this logic. This block will figure out a new, unblocked detour command which will then temporarily override the new command.

      3fb16588-9fe2-4fb8-b205-ac2716cf1e9c-image.png

       
       

      Step 11 - Discussion on How to Make a Detour

       

      So, how do we calculate a smart detour? There are many complex pathfinding algorithms, but we’re looking for a solution that is effective, fast, and simple to build.

      Imagine the truck needs to get the Gas on the top right, but a tree is blocking the path to the right.

      a412f367-612a-4b1e-b283-0d401eaac2ed-image.png

       
      We have a few command options:

      • new command: The original, blocked command (e.g., move right).
      • alt command: The other direction towards the target. Since the Gas is also above the truck, moving up would be a good alternative.
      • alt command 2: The opposite of the alt command. This is a backup plan if the alt command is also blocked.
      • back command: The opposite of the new command. If all forward and side paths are blocked, our only choice is to back up to escape the “trap”.

      Our strategy will be this: when we’re blocked, we will check these alternatives in order. Once we find a clear path, we’ll execute a two-step detour (e.g., move sideways for 1 second, then move toward the target for 1 second) before returning to our normal logic. This simple strategy will handle most situations very effectively. Let’s build it!

       
       

      Step 12 - Initialize Detour

       

      Inside our new calculate detour block, the first thing we’ll do is set up a couple of variables to manage the detour process:

      • in detour: A “flag” variable. We’ll set this to 1 to signal that the AI is currently executing a detour.
      • detour start time: We’ll record the current timer value here. This will help us time each step of the detour.

      f6ecfbf9-17a3-44ef-b20c-dd5562b93e8e-image.png

       
       

      Step 13 - Calculate the Alt Command

       

      Next, we need to determine the best alternative command (alt command). This command should still move the truck generally closer to its ultimate target.

      If our original new command was vertical (up/down), the alt command should be horizontal (left/right). We can pick the correct horizontal direction by looking at distance x. If distance x is positive, the target is to the right, so our alt command should be 4 (move right).

      3a1a1f08-0484-49ec-a60d-4118f1640434-image.png

       
      Similarly, if the new command was horizontal (left/right), the alt command should be vertical (up/down). We can use distance y to decide whether to move up or down.

      c407b2cf-b9f4-472f-8e39-683bd1184d14-image.png

       
       

      Step 14 - Calculate the Alt Command 2

       

      The alt command 2 is always the opposite of the alt command. We can use a neat little math trick for this: since (left=3) + (right=4) = 7, we can calculate the opposite by subtracting from 7. This logic works whether the new command was vertical or horizontal.

      6d2c1877-59ee-4beb-adb6-64e82dbb4d8c-image.png

       
       

      Step 15 - Calculate the Back Command

       

      The back command is always the opposite of the new command. We can use a similar calculation based on whether the command is vertical or horizontal.

      f3e0c2e3-979f-4070-90bc-3d5acd43ecb2-image.png

       
       

      Step 16 - Take the Alt Command If Available

       

      Now we’ll decide which detour path to take. Our first choice is always the alt command. We use our direction is blocked reporter to check if that path is clear. If it is, we’ve found our detour! We’ll set up a two-step plan:

      1. detour command: Set to the alt command.
      2. detour command 2: Set to the original new command.

      This means the truck will first move sideways, then try to resume its original path. Since we’ve found a plan, we can use a return block to exit the calculate detour script.

      d8dd9db1-4783-496e-9570-1dfe91045fa0-image.png

       
       

      Step 17 - Take the Alt Command 2 If Available

       

      But what if the alt command path is also blocked? This means obstacles are in front of us AND on one side. Our next best option is to try alt command 2 (the other side). If that path is clear, we’ll set our detour plan to use alt command 2 first, followed by the original new command.

      11d44863-29a4-4af4-9391-9e0010c78439-image.png

       
       

      Step 18 - Take the Back Command as a Last Resort

       

      In the worst-case scenario, the paths forward, left, and right are all blocked. The truck is trapped! The only way out is to back up. We’ll set the detour plan to use the back command first, and then follow it up with the alt command to try and steer around the trap.

      bbc8c789-da7f-4c2e-94e8-b7e3486d8d39-image.png

       
       

      Step 19 - Add a New Branch for Detour

       

      Now we need to modify our main loop to actually execute the detour. When the in detour flag is set to 1, we need to bypass all our normal logic (checking powerups, finding the opponent, etc.). Instead, we will run a separate branch of code dedicated to the detour.

      The animation below shows how to restructure your main loop with an if/else block based on the in detour variable. Notice that the block that sends the command to the truck is now outside and after the if/else, so it runs in every situation.

      restructure.gif

       
       

      Step 20 - Stop Detour in 2 Seconds

       

      Inside the new “detour” branch of our if/else block, we’ll manage the two-step detour sequence. The whole detour will last for 2 seconds. By default, the new command will be our detour command. If the timer shows that 2 seconds have passed since the detour start time, the detour is over. We’ll set the in detour flag back to 0, so that on the next loop, the AI will go back to its normal behavior.

      c01a326f-df6f-49de-b426-52a67b4b9c54-image.png

       
       

      Step 21 - Switch to Second Detour Command in 1 Second

       

      The detour itself has two parts. For the first second, the truck will follow detour command. After one second has passed, we’ll switch the new command to detour command 2 for the second half of the detour. This creates the “go sideways, then go forward” maneuver.

      f3fed8c3-0722-4602-82f5-f816c64a6093-image.png

       
      And that’s it! Our detour logic is complete. In short, when the AI “sees” a tree, it will now intelligently try to move sideways or even back up for a moment to get around it.

       
       

      Step 22 - Test This Version

       

      It’s time for the final test! Go to the Setup sprite and set the tree count to 6 (or even more to make it really challenging). Run the project and watch how your AI truck now cleverly navigates around the trees instead of getting stuck. You can also speed up the powerup spawn times again for a more action-packed test.

      Here is a demo:

      detour2.gif

       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       

      Further Improvements

       

      This AI controller is already much smarter, but there’s always room for improvement! If you want to take your AI to the next level and win battles, consider these enhancements:

      • Smarter Attack and Dodge: Have you noticed your truck sometimes has a perfect shot lined up but decides to wander off instead? Improve the attack mode to recognize when it’s aligned with the opponent, take the shot, and then immediately move sideways to dodge any return fire.

      • Strategic Powerup Denial: Right now, your AI only grabs powerups it needs. But what if your opponent is low on health and a Sugar powerup appears? A truly smart AI would grab that powerup just to prevent the opponent from getting it! Modify your check powerups logic to consider the opponent’s status, too.

      • Don’t Waste Ammo: Firing donuts into a tree is a waste! Before shooting, you can use a similar detection method to the one we built for avoiding trees. Check if the line of sight to the opponent is clear. If it’s blocked by a tree, hold your fire and reposition.

      • Press the Advantage: When your truck is stronger (more lives, more speed), it should be more aggressive! Instead of firing one shot and then wandering away, why not make it stay in attack mode and keep firing as long as it has the upper hand?

       
      These are just a few ideas to get you started. The best way to improve your AI is to watch it battle. What mistakes does it make? When does it get outsmarted? Every battle is a chance to learn and come up with your own unique strategies to build the ultimate champion

      1 Reply Last reply Reply Quote 0
      • Pinned by  info-creaticode info-creaticode 
      • First post
        Last post