Level 6: Challenge 2 - IncrediCoders/Python1 GitHub Wiki

Queen Cobra added this page on June 6, 2025


Let's begin your second challenge for Level 6!

In this challenge, you are going to add in another plasma ball projectile for the Cryptic Creeper to attack with!

To find the Level 6 Challenge 2 code template, open the Level 6 folder, the Challenges folder, and then the Challenge 2 folder. You should already have the files downloaded onto your computer (see Load the IncrediCoders Files). Open the BossBattle_Challenge2.py file in Visual Studio Code to build the program by following along with my instructions below!

I broke these instructions down into a few sections:

Explaining the Code

Line 1 includes the code comment, # Runs the init.py file and imports the libraries. This explains what Line 2 does.

On Line 2, from init import * initializes the program. This runs code from the init.py file that sets up the file path for the images and text file, it gets the fonts ready for this game, it sets the window size, it loads the images, and it includes the code that handles collision in the game (when one object touches another). This Line 2 statement allows you to use all the information in that file, for the rest of your program.

On Line 4, def update(delta_time): updates each time a key is pressed or a button is clicked (these are called events). This method will continuously check for these events while the game is running.

The Line 5 comment, # Check if Paul collides with the walls, explains that Lines 6-13 are going to check if Paul runs into any of the walls (on the top of the screen, bottom, left, or right).

On Line 6, if MY.player.location.x < MY.wall_height: checks to see if the current location of the player sprite is less than the maximum height from the dimensions of the wall. Because this code checks the X-axis, it's looking at the left side of the screen. In other words, Lines 6-7 make sure Paul doesn't run through the left wall.

On Line 7, MY.player.location.x = MY.wall_height is run when the if statment on Line 6 is true. It sets the player's location to the maximum height set by the dimensions of the wall (again, to make sure Paul doesn't go through the left wall).

On Line 8, if MY.player.location.x > WINDOW_WIDTH - MY.wall_height: checks to see if the current location of the player sprite is greater than the width of the screen (minus the height from the dimensions of the wall). This makes sure Paul doesn't run through the right wall.

On Line 9, MY.player.location.x = WINDOW_WIDTH - MY.wall_height is run if the if statment on Line 8 is true, and it sets the player's location to the location that is the width of the window screen, minus the height of the wall.

On Line 10, if MY.player.location.y < MY.wall_height: checks to see if the current location of the player sprite is less than the maximum height from the dimensions of the wall. This makes sure Paul doesn't run through the top wall (and go off the screen).

On Line 11, MY.player.location.y = MY.wall_height is run when the if statment on Line 10 is true, and it sets the player's location to the maximum height set by the dimensions of the wall.

On Line 12, if MY.player.location.y > WINDOW_LENGTH - (MY.wall_height + 15): checks to see if the current location of the player sprite is greater than the length of the window, subtracted by the height of the wall plus 15. The extra amount of space (15 units) is added to make sure that Paul doesn't go through the bottom wall on the screen. The bottom wall pushes up a little higher from the bottom of the screen, because Paul can't overlap over some it, like he can on the top of the screen. (It would look like he's standing on the wall.)

On Line 13, MY.player.location.y = WINDOW_LENGTH - (MY.wall_height + 15) is run when the if statment on Line 12 is true, and it sets the player's location to the length of the window, subtracted by the height of the wall, plus 15. Again, we add the 15 units so that Paul doesn't stand on the bottom wall when he reaches the bottom of the screen.

On Line 15, we run the handle_pillar_collision() function. When Paul runs into a pillar, the code in this function checks to see the location of Paul's sprite. The code for this function is stored in the init.py file.

On Line 18, if MY.player.collides_with_boss(): checks to see if Paul has collided with the Creeper, which is the boss. (Afterall, this game is called Boss Battle!) If Paul collides with the Creeper, then we run Lines 19-21.

If that statement is true, on Line 19, the player_pain_anim() function runs, which displays the player's pain animation. In other words, if Paul runs into the Creeper, he gets hurt!

On Line 20, MY.player_health -= 1 subtracts one health from the player's current health, so if the player had been hit before, it would go down one more from that value. Paul loses some health!

On Line 21, MY.player_hitbox.active = False allows the player time to recover and move away from the boss by removing the active hitbox for a few seconds.

On Line 24, if MY.player_dir == UP: checks to see if the direction that the player is going is up.

On Lines 25-26, the code MY.player_hitbox.location = pygame.math.Vector2(MY.player.location.x + 20, MY.player.location.y - 20) runs if the Line 24 statement is true. This code sets the hitbox in front of Paul when he is facing up. The reason why this code is on two lines, is so that it makes more sense when describing it in the book.

On Line 27, elif MY.player_dir == DOWN: checks to see if the direction that the player is going is down. In other words, the player presses the down arrow to move Paul facing down on the screen.

On Lines 28-29, MY.player_hitbox.location = pygame.math.Vector2(MY.player.location.x - 10, MY.player.location.y + 25) runs if the Line 27 statement is true. It sets the hitbox for when the player goes down. Again, we have the code on two lines because it's a long line of code and didn't fit in the book (or in my text messages that were transferred into the book).

On Line 30, elif MY.player_dir == LEFT: checks to see if the direction that the player is facing is to the left.

On Lines 31-32, MY.player_hitbox.location = pygame.math.Vector2(MY.player.location.x - 20, MY.player.location.y) runs if the Line 30 statement is true. It sets the hitbox for when the player faces or moves to the left.

On Line 33, elif MY.player_dir == RIGHT: checks if the direction that the player is facing is to the right.

On Lines 34-35, MY.player_hitbox.location = pygame.math.Vector2(MY.player.location.x + 20, MY.player.location.y) runs if the Line 33 statement is true. It sets the hitbox for when the player goes to the left. Again, we have the code on two lines so that it fits better in the book.

Line 37 includes the comment, # Reduce Creeper's health when he gets attacked. This describes Lines 38-40.

On Line 38, we run if MY.player_hitbox.active and MY.boss.collides_with_hitbox(): This statement checks to see if the Creeper has been attacked by Paul. It means Paul's melee attack (with his Laser Blade) has hit the Creeper.

On Line 39, MY.boss_health -= 1 runs after the Creeper is hit. Creeper's health goes down by 1. The symbol "-=" reduces the value of the variable (the total number) by the amount on the right (which is 1 in this case). And then it updates the variable with the new value (which is one less).

Note that the Cryptic Creeper has more health than your character (Paul Python). So the Creeper's health bar goes down more slowly than Paul's health bar.

On Line 40, MY.player_hitbox.active = False makes it so that the Creeper can't hurt Paul while Paul is attacking him. This is so the projectiles don't hurt Paul repeatedly while he's hurting the Creeper.

Also, the player will want to run away from the Creeper after attacking him, so that the Creeper doesn't hit Paul with a plasma bolt.

NEW CODE: On Lines 42-55, you are going to write your own code to change the projectile path of the Creeper's plasma bolts. You'll make them shoot out in a square path instead. See the next section, "Write Your Own Code", for guidance.

Line 56 includes the comment, # Paul loses health if he collides with a projectile. This describes Lines 57-60.

On Line 57, you'll find the code if projectile.collides_with(MY.player): It checks to see if Paul is hit by a projectile. If he is, then the program runs Lines 58-60.

On Line 58, the code MY.player_health -= MY.proj_damage subtracts health from the player (Paul), after being hit by the projectile. The amount of health is stored in the MY.proj_damage variable, so that it's easy to change that amount in one place. (The default amount of damage is set to 0.1 in the init.py file.) The minus-equals symbol (-=) subtracts that amount from the variable on the left (MY.player_health) and then updates it to the new value. So, after this line runs, the MY.player_health variable contains the new value of Paul's health, after the projectile hits him.

On Line 59, we run the player_pain_anim() function, which displays the animation for when the player (Paul) is hit by a projectile. We define that function in the init.py file, which plays the correct animation, depending on which direction Paul is facing.

On Line 60, the code MY.player.hit = True makes the player temporarily invincible once hit by the projectile. This code is used in the init.py file to run an if block, which makes sure Paul has time to run away before he can be attacked again.

On Line 62, player_attack_update() runs a function that's defined in the init.py file. The function includes an if statement that checks to see if the player presses the spacebar. If so, it turns on the player's hitbox, so that Paul can attack, and it plays the attack animation while Paul attacks.

On Line 64, update_assets(delta_time) updates all the animations and other information about the level, using the delta_time variable, which keeps all the animations on the level in sync. The function is defined in the init.py file.

On Line 66, the check_win() function checks to see if the player has won. If so, the game ends with the "You Win" overlay. The function is defined in the init.py file, and it contains if statements that check whether the Creeper ran out of health.

On Line 68, the check_events() function checks whether the boss attacks and whether you closed the game window. This function is also in the init.py file, and it contains a for loop with embedded if, elif, and else statements.

Line 70 includes the comment, # Register the game states. This describes Lines 71-74.

Lines 71-74 register the different game states:

  • On Line 71, Manager.register(sys.modules[__name__]) sets up the state machine for the current file, so that the game launches.
  • On Line 72, Manager.register(GameOver) moves the game into the Game Over state. This is the overlay on the screen that says, "Game Over" or "You Win." It times out after a few seconds, and the Play Again screen appears.
  • On Line 73, Manager.register(PlayAgain) opens the Play Again state, where the player can click "Click here to play again." This screen shows Paul playing the simulation (the game) on his tablet. paul says, "I'm starting to get better at this boss simulator! I'm going to be ready to take on the Cryptic Creeper!" The player can click anywhere on the screen to close the screen and open the starting screen again.
  • On Line 74, Manager.register(Intro) opens the Start Screen when the game loads. This screen includes some basic instructions and allows the player a time to prepare themself before they start the game. The player can click anywhere on the screen to start the game.

Line 76 includes the comment, # Run the game. This describes what Line 77 does.

On Line 77, Manager.run(SCREEN, WINDOW, BLACK, "CHALLENGE2") opens a new window and runs the game!

Write Your Own Code

Lines 43-60 make the projectiles follow a square path around the Creeper. You're going to write Lines 43-55.

Let's start by writing out Line 43:

  • On Line 43, type for to start a for loop. This is a continuous loop that could occur multiple times, for the number of projectiles on the screen. This means that the for loop runs once for every projectile on the screen.
  • Next, enter a space, and then write the variable projectile on the same line. This is going to cycle through and access all the elements in the MY.shield.projectiles list, one at a time.
  • Type in MY.shield_projectiles next. You're looking in the MY.shield.projectiles list (also called an array) to process each projectile.
  • Since this is a for loop, to end the line, type in a : symbol (a colon).

Next, check out the comment on Line 44, #TODO: Write code to move the projectile to the right. This comment explains that Lines 45-46 (which you're about to write) will move the Cryptic Creeper's new projectile to the right, along the X axis.

Under the comment on Line 44, write the following if statement on Line 45:

  • if(projectile.location.x >= 320 and projectile.location.x < 480 and projectile.location.y == 240):

Line 45 is an if statement that checks if the projectile is in place to be moved to the right. First, it looks at the range for the x-axis. In this case, the projectile location needs to be greater than or equal to 320 for the x-position. You wrote projectile.location.x >= 320 in the parentheses, followed by the and operator. The projectile's x-position would also have to be less than 480, so you wrote projectile.location.x < 480 next, followed by the and operator. Finally, for this if statement, the y-position needs to be equal to 240, so you wrote at the end of the line, projectile.location.y == 240):

Note: Do not forget to put your parameters in parentheses, after the if statement. Then, make sure to add a colon at the end of the line, after the closing parenthesis.

On Line 46:

  • Remember to indent to the right if it isn't done yet, so the new line begins further to the right than Line 45. This is because Line 46 is the code that runs if the if statement on Line 45 is true.
  • Type in projectile.location.x += MY.projectile_velocity to add the projectile velocity to the location of the projectile on the x-axis.

You took the velocity of the player's projectile, which is held in the MY.projectile_velocity variable, and then you added it to the value of the projectile.location.x variable. The += operator takes the most recent value of the velocity variable and adds that to x-position variable to update the x-position variable with the new value. This is what moves the Creeper projectile to the right.

Next, check out the code comment on Line 47, #TODO: Write code to move the projectile down. This is the code that you're about to write on Lines 48-49.

Below the code comment on Line 47 (at the same indentation level as Line 47), write the code for Line 48:

  • elif(projectile.location.x == 480 and projectile.location.y >= 240 and projectile.location.y < 400):

On Line 48, you started a nested elif loop, which stands for "else-if". It presents an alternative if statement, if the first condition (on Line 45) isn't true. This statement checks if the projectile is ready to be moved down. In this case, the projectile's location needs to be equal to 480 for the x-position. The projectile's y-position would have to be greater than or equal to 240. And finally, for this elif statement, the y-position will also need to be less than 400. If these conditions are true (Line 48), then Line 49 runs (which you'll write next).

Note: Do not forget to use parentheses after the elif conditional statement, and be sure to add a colon at the end of the line, after the end parenthesis.

On Line 49:

  • Remember to indent first. Line 49 needs to be indented to the right of Line 48, because it's inside the elif block.
  • Type in the code that adds the projectile velocity to the y location of the projectile: projectile.location.y += MY.projectile_velocity

This is very similar to Line 46, but you replace the x coordinate with the y coordinate in the projectile variable name.

If the Line 48 elif statement is true, then Line 49 adds the projectile velocity to the location of the projectile on the y-axis. As the code comment on Line 47 describes, Line 49 moves Creeper's projectile down the screen.

Check out the code comment on Line 50, #TODO: Write code to move the projectile to the left. This describes what Lines 51-52 are going to do.

On Line 51, type in a similar elif statement (at the same indentation level as Line 50):

  • elif(projectile.location.x <= 480 and projectile.location.x > 320 and projectile.location.y == 400):

This elif statement checks if the projectile is ready to be moved to the left. Don't forget to include the colon at the end of this elif statement!

On Line 52, indent to the right. You're going to subtract the projectile velocity from the location of the projectile on the x-axis. Type this code:

  • projectile.location.x -= MY.projectile_velocity

Similarly, the -= operator subtracts the value of the MY.projectile_velocity variable from the projectile.location.x variable and saves it back into the projectile.location.x variable. This is what moves Creeper's projectile to the left.

Check out the code comment on Line 53, #TODO: Write code to move the projectile up. This describes the code that you'll write on Lines 54-55.

On Line 54, you're going to start our final nested elif statement, which checks if the projectile is ready to be moved up. Type in the code:

  • elif(projectile.location.x == 320 and projectile.location.y > 240 and projectile.location.y <= 400):

In this case, the projectile needs to be equal to 320 for the x-position. The projectile's y-position would have to be greater than 240. Finally, for this elif statement, the y-position will also need to be less than or equal to 400. The code is checking to see if the projectile is in place in order to move it up and complete the square path that it takes. Be sure to end the line with a colon!

Line 55 runs if Line 54 is true. You'll subtract the projectile velocity from the location of the projectile on the y-axis. On Line 55, indent to the right, and then type in this code:

  • projectile.location.y -= MY.projectile_velocity

Line 55 is what moves Creeper's projectile up and completes the square path!

You did it! It's time to run your code, and make sure it works!

If you're playing the game, one strategy is to hang out in a section of the screen (like to the lower left of the Creeper), and then when the Creeper fires a projectile at you, you dodge it (like going to the right), then go up and hit the Creeper a few times with your melee weapon (Paul's Laser Blade). Then run away again (dodge left) and get back in position to wait for Creeper to attack again. In Challenge 2, you also have the challenge of this new projectile moving around the Creeper in a square path. So, you'll likely just have to wait longer before you can go in to strike the Creeper. You might need to dodge once or twice without moving up (just go to the right and back into position on the left, for example), before you see an opening to go up and attack the Creeper.

The Final Code

I included a solution file for Level 6: Boss Battle Challenge 2. This file has all the code filled in, so if you run into any issues with your code (for example, it doesn't compile/run, or something isn't working correctly in your program), then you can take a look at the final code file to see what you did differently:

IMPORTANT: Please don't cheat yourself! Finish the game first!

Next Steps

When you're done, you can move on to Level 7, IncrediCards!

More Level 6 Resources

In addition to this Online Articles page and the instructions for our Level 6 challenges, we also have a Help Page, a Learning Quiz, an Unplugged Activity, and a Rewards article:

  • Level 6: Help - This page helps you complete the instructions in the book, in case you get stuck.

  • Level 6: Learning Quiz - I wrote some questions in case you want to quiz yourself about what you learned. Or you can teach others and quiz them!

  • Level 6: Unplugged Activity - I wrote this page with more details than what you saw in the book. In this game,

  • Level 6: Rewards - If you completed the Boss Battle program that we talked about, then I set up this page to act as a reward. You can see some illustrations of me and learn more about who I am! You'll also find the Mech Award digital download, to show off your accomplishment!

Level 7

After you're completely done with Level 6 (did you do both challenges and get your reward?), then it's time to move on to Level 7! While you read through Level 7 in your book, you can check out the resources from Grafika & Syntax, as they teach you how to build the IncrediCards program:

Thank you for helping build the Boss Battle simulation, so that Paul Python could have a fighting chance!

--Queen Cobra

⚠️ **GitHub.com Fallback** ⚠️