Link to the reference page

For project 1, you’ll be writing a video game in MIPS assembly. It’s a bit of a pastiche of a few games - a classic game called Boulderdash, a little Minecraft, a little of my own ideas.

You’ll continue to use the LED Keypad and Display plugin that you used in lab 4. To the right is a video demonstrating how the game will look and work when you’re done.

Brief game description

There is a tilemap on which all the action takes place. This is a 2D grid of “tiles,” small square images which make up the walls, vines, dirt etc.

The user controls the player, a yellow blob with a pointy blue hat. The player can move in all four directions, but not diagonally. The player can walk on some tiles, but cannot move through others.

There are dirt tiles which the player cannot move through, but can “dig up”, after which the player gets the dirt in their inventory. They can then place dirt tiles elsewhere.

There are diamonds which the player can collect for fun. There are vines which push the player back and prevent them from progressing. There are boulders which the player can push from the sides and will fall down the screen, but also function as obstructions.

There are bugs which are not enemies, but friends! Bugs automatically follow along walls and boulders, and can eat vines. Bugs cannot move boulders. So the player has to move boulders to help the bugs, and the bugs can eat vines to help the player. (They’re supposed to look like ladybugs, not lanternflies. Lol.)

The player’s objective is to rescue all the bugs by guiding them into the goal, a checkerboard-colored tile. Once all bugs have been rescued, the player can step onto the goal themselves to win the game.


Grading Rubric

Note: if you submit on the late due date, 10 points will be deducted after all other grading has been done.

Also note: if you submit the wrong thing (submit a file that isn’t your project, or an outdated version of your project) and it isn’t found until the grader looks at it, you will be allowed to resubmit, but you will lose 20 points. Be careful about what you are submitting. (But if you make a mistake in what you submit before the deadline, it’s no problem. Just resubmit with the correct file. You have infinite resubmissions before the deadline.)

Code style: although there is no point category for code style, the grader may take off up to 10 points if your code is very poorly-written. Poor indentation may lose you a couple points, but mostly it’s about using the calling convention we learned; using the right registers for the right purposes; and writing/using functions correctly. Remember: keep your code neat and tidy while you write it, not at the end.


Stuff to download

Right-click and download this ZIP file. Your browser may automatically extract it to a folder. If not, open it and extract its contents to a new folder.

Now:

  1. Rename abc123_proj1.asm to your username.
  2. Open that file in MARS.
  3. Open and connect the Keypad and LED Display Simulator tool.
  4. Assemble and run. It should just sit there in an infinite loop displaying some stuff. Hit the stop button to stop it.

How to approach this

Don’t be intimidated! You are only going to be writing around 500 lines of code (possibly more, if it’s heavily commented) to finish what I’ve given you in abc123_proj1.asm.

Please follow the instructions in order. Later steps assume you have done earlier steps. Ten broken things are not as good as five fully-working things. You learn more from getting things working.

These instructions start verbose, but become more abstract as you go on. It’s because I have to introduce a bunch of concepts all at the beginning, and then as you get more comfortable with them, I can give you more general instructions and you can go “oh, I did something like this before.”

Also, this is a pretty sizeable project, and you’ve been given almost 3 weeks to do it. I would recommend you pace yourself and try to reach certain milestones each week. For example:


What you’ve been given

You will only submit your abc123_proj1.asm file. Do not put any code in any other file. You will not need to change any other file. (But you may change levels.asm for testing as explained below).


What are enter and leave?

All the functions I’ve given you look something like this:

update_all:
enter
    jal obj_update_all
    jal update_timers
    jal update_camera
leave

What are enter and leave? They’re macros I provided in macros.asm:

So they act as the “braces” around functions. They replace the push ra and pop ra, jr ra that you had to write by hand in the labs.

As an extra feature, if you want to use any s registers, you list them in the same order after enter and leave:

my_function:
enter s0, s1 # same as pushing ra, s0, and s1
    # in this function, I'm allowed to use s0 and s1.

# IMPORTANT: always list the same registers in the SAME ORDER as the 'enter'!
leave s0, s1 # same as popping s1, s0, and ra, then 'jr ra'

Only use enter and leave ONCE in each function. If you need to save multiple s registers, list them as shown above. Do not do multiple enters or leaves in a row. This will imbalance the stack and cause crashes!

These macros greatly reduce the amount of code you have to write for function prologues/epilogues. So now you really have no excuse for not using s registers ;)


Seeing what the program does now

When you first run the program, this is what you’ll see.

Along the top is the HUD (“heads-up display”), which shows some important information about the game:

If you’re curious, look in map.asm, at the contents of the level_data array at the top. You can see that each character corresponds to one tile.

What’s all the stuff below the HUD? That’s the tilemap. It forms the background of our game, and is what the player will walk around on and interact with. Right now we can see some brick walls, some dirt (the brown speckled squares), and some vines. We are only looking at a tiny piece of the tilemap right now. Once the player can move around, we will see more of it.

Wait, where is the player? Well, let’s take a detour first…


Objects

The tilemap is the “stage” on which the objects appear and move around. Have a look at around line 30 of your proj1.asm file. You’ll see these arrays:

# Object arrays. These are parallel arrays. The player object is in slot 0,
# so the "player_x" and "player_y" labels are pointing to the same place as
# slot 0 of those arrays. Same thing for the other arrays.
object_type:   .word OBJ_EMPTY:NUM_OBJECTS
player_x:
object_x:      .word 0:NUM_OBJECTS # fixed 24.8 - X position
player_y:
object_y:      .word 0:NUM_OBJECTS # fixed 24.8 - Y position
player_vx:
object_vx:     .word 0:NUM_OBJECTS # fixed 24.8 - X velocity
player_vy:
object_vy:     .word 0:NUM_OBJECTS # fixed 24.8 - Y velocity
player_moving:
object_moving: .word 0:NUM_OBJECTS # 0 = still, nonzero = moving for this many frames
player_dir:
object_dir:    .word 0:NUM_OBJECTS # direction object is facing

Just like the particles in lab 4, these are parallel arrays. For example, the object in “slot 4” has a type in object_type[4]; a position in object_x[4] and object_y[4]; a velocity vector in object_vx[4] and object_vy[4]; and so on. Each of these arrays is NUM_OBJECTS long.

The types of objects are the OBJ_ constants in game_constants.asm. OBJ_EMPTY is a special value to mean “no object in this slot.” The object_type array is initially filled with OBJ_EMPTY but objects are created when the map is loaded by load_map (called from main).

The object in slot 0 is special: it is always the player object. That’s why the player_x, player_y, player_vx, player_vy etc. labels are pointing at the beginnings of those arrays. (If you have multiple labels on one .word, all the labels point to the same place.)


How objects are shaped

Each object is a square centered on its position in the object_x and object_y arrays. It is OBJ_SIZE pixels wide and tall, and the distance from the center to any side is OBJ_HALF_SIZE. (See diagram to the right; the dot in the middle is the position.)

If you look at the definitions of these constants in game_constants.asm, you’ll see:

.eqv OBJ_SIZE 0x500
.eqv OBJ_HALF_SIZE 0x280

0x500? 0x280? That’s because these are fixed-point integers. We are using 24.8 fixed-point integers in this project: 24 bits of “whole number” followed by 8 bits of fraction.

If you write these numbers in hex, it works out that the last two hex digits are the fraction, and the digits before them are the whole part. So 0x500 means “5.0”, and 0x280 means “2.5” (because the fraction is 0x80 / 0x100 = 128 / 256 = 0.5). You’ll get used to it :)


Object methods

MIPS has no built-in classes, but that doesn’t stop us from doing object-oriented programming. Each type of object in the game has two methods:

For example, the player object has obj_update_player and obj_draw_player, both in your proj1.asm. Both methods take the object index as the a0 argument, so within these methods, you can use a0 as the indexes into the various object_ arrays. (Essentially a0 is the this argument.)


1. Drawing the objects

It’s hard to get any work done if you can’t even see the things you are trying to implement! So let’s draw all the objects by implementing the obj_draw_ methods.


1.1 Drawing the player

All the objects in this level actually already exist! They are not visible because all the obj_draw methods in proj1.asm are empty. So let’s implement obj_draw_player.

  1. When obj_draw_player runs, the object index is already in a0. So we can immediately call obj_get_topleft_pixel_coords.
    • It takes an object index in a0, and places the pixel coordinates of the top-left corner of the passed-in object into the return value registers.
  2. Move those coordinates into the first two argument registers, because we’re about to call another function.
  3. The third argument will be player_textures[player_dir * 4].
    • player_dir is what kind of variable? so how do you get its value?
    • then load player_textures[player_dir * 4] into a2.
      • this is a .word array.
  4. Finally, call blit_5x5_sprite_trans.
    • This is a function at the bottom of proj1.asm that adjusts the X and Y coordinates based on the position of the “camera” (which we’ll get to soon).

You should now see the little yellow blob guy on screen when you run the program, like in the screenshot above!


1.2. Drawing the bugs

There are some bugs, too. Drawing them is pretty similar to drawing the player, so let’s get that out of the way.

First, change obj_draw_bug like so:

obj_draw_bug:
enter s0
    move s0, a0

leave s0

Doing enter s0 and leave s0 gives us permission to use s0 in this function, and the move puts the object index into s0. This is good practice to use for the object methods moving forward. (We were able to get away with not doing this in obj_draw_player because we have access to the player_ variables.)

Now, the rest of obj_draw_bug is almost the same as obj_draw_player:

  1. call obj_get_topleft_pixel_coords and move the return values into the argument regs
  2. index an array of textures, but this time it’s bug_textures[object_dir[s0]]
    • yes, that’s an array index inside an array index… what happens first?
    • you do NOT need to multiply s0 by anything, it is already pre-multiplied
    • loading out of object_dir[s0] can be done in a single line!
    • ..but you DO need to multiply the value that you load out of object_dir[s0], because bug_textures is a .word array!
  3. call blit_5x5_sprite_trans

Boom, there are the bugs!


1.3 Drawing the diamonds and boulders

These are even easier than the player and bugs.

For each of obj_draw_diamond and obj_draw_boulder:

  1. call obj_get_topleft_pixel_coords and move the return values into the argument regs
  2. use la to put a texture address into a2
    • tex_diamond for diamonds, tex_boulder for boulders
  3. call blit_5x5_sprite_trans

I guess you don’t really need to use s0 for these… But there you go.

You now have a 20%!


2. Moving the player around

Now we need to move the player. This will have some similarities to lab 4, but we’re also going to create a good amount of “code scaffolding” to support it as well as a bunch of future steps of the project.

obj_update_player

Here is my sketch for the code that goes in obj_update_player. Translate this code, and stub out each of the 5 player_ functions that it calls.

{
    // if player is not moving...
    if(player_moving == 0) {
        // see if the player is on a goal tile
        player_check_goal();

        // see if the player is on a vine tile, and bail if they are
        if(player_check_vines() == 0) {
            // see if they want to place dirt
            player_check_place_input();

            // and see if they want to move
            player_check_move_input();
        }
    } else {
        // they're moving, so move them!
        obj_move(0);
    }

    // always check for dig inputs
    player_check_dig_input();
}

player_check_move_input

This function checks to see if the player wants to move with the arrow keys. This is kind of similar to what you did on lab 4, but simpler:

  1. call input_get_keys_held
  2. put its return value in s0 (what do you have to do to be allowed to use it?)
  3. check for them pressing the four directions, just like you did on lab 4, except:
    • the keys being held are in s0 instead of v0
    • in each case, you will be calling player_try_move, another new function, with an argument
    • KEY_U should call player_try_move(DIR_N)
    • KEY_D should call player_try_move(DIR_S)
    • KEY_L should call player_try_move(DIR_W)
    • KEY_R should call player_try_move(DIR_E)
      • remember to use e.g. li a0, DIR_N, don’t copy and paste the constant value! use the name!

player_try_move

Make this function, and at first, just paste this code into it (“into” means between the enter and leave so don’t forget those)

	print_str "trying to move "
	syscall_print_int
	newline

Now assemble and run, click the display, and hit the arrow keys. You should see some messages being printed:

If you don’t see anything printed, here are some common mistakes:

If it continuously prints messages even when you don’t hold any arrow keys, your ifs in player_check_move_input are probably using the opposite branch type than they should. You want to skip each if’s code when the and gives you 0.

If you still can’t get it printing properly, you know what you should do by now. (The answer is not “sit there for two days wondering what you did wrong and feeling bad about it”)

Alright, now delete those 3 lines of printing code. player_try_move’s responsibility is to, well, try to move the player. The player can’t always move, for example if they’re trying to move into a wall. So we have to do some checks first to verify that they can move, and only then start them moving. Here’s the sketch for this function:

// are they changing directions?
if(player_dir != a0) {
    player_dir = a0;
    // prevent them from moving immediately so that
    // they can turn without moving
    player_move_timer = PLAYER_MOVE_DELAY;
}

// only allow them to move every PLAYER_MOVE_DELAY frames
if(player_move_timer == 0) {
    player_move_timer = PLAYER_MOVE_DELAY;

    switch(obj_collision_check(0, player_dir)) {
        case COLLISION_TILE: return;
        case COLLISION_OBJ:
            // TODO: object pushing, but later.

        // fall through to move case (do NOT break out of the case here)
        default: // "move" case
            obj_start_moving_forward(0, PLAYER_MOVE_VELOCITY, PLAYER_MOVE_DURATION);
    }
}

To explain broadly:

Now, if you assemble, run, click on the display, and use the arrow keys, you should be able to change what direction the player is facing. But they can’t move yet. Why?

It’s because player_move_timer is never being updated. We set it to PLAYER_MOVE_DELAY but never decrement it so it’s never 0 so the second if never runs.

To fix that, find the update_timers function. In it, decrement player_move_timer, but only if it’s greater than 0. (It should never go negative, is what I mean.)

Now you should be able to move the player around! They should stop at the brick walls and dirt, and pass through other things like vines, boulders, diamonds, and bugs.

If you press an arrow key once and the player quickly moves offscreen without you holding it down: you messed up the if-else in obj_update_player and you forgot to jump over the else part. (am I psychic?)

And if you go off the right side of the screen… lol bye. The player disappears offscreen and we can’t get them back. We have to make the camera follow the player now!


Tangent: what’s obj_collision_check doing?

Uh, quite a bit actually. The source code is in obj.asm, which also calls a function from collide.asm. It basically checks to see if there is a solid tile or solid object one “tile” away from the center of the object in the given direction.

If there is nothing solid there, it returns COLLISION_NONE which means we can move freely. If there is a solid tile, it returns COLLISION_TILE which causes the switch in player_try_move to return, disallowing the player from moving. If there is a solid object, it returns COLLISION_OBJ which we will handle later.


3. Moving the camera (this is a short one)

Remember I said we were only looking at a portion of the tilemap? The “camera” is really the position of that portion that we are currently looking at. “Moving the camera” amounts to moving the tilemap around.

Find update_camera. In there, you need to:

  1. Get the top-left pixel coordinates of the player (object 0)
    • you called the function for this in all your obj_draw methods, but…
    • this function takes an argument. in the obj_draw methods, that argument was already in a0, but now you have to put it there yourself. The player is object zero, so put 0 in a0 before calling this function.
  2. Add CAMERA_OFFSET_X and CAMERA_OFFSET_Y to the return values of that function, and put the results of the additions into the argument registers
  3. Call tilemap_set_scroll

That’s it. Now when you move the player down or to the right, the camera follows!

You now have a 36%!


4. Digging and placing dirt

The player can now move around, but that alone is a little boring. Let’s let them dig up dirt and then place it elsewhere.

For that, it might be useful to use one of the test levels I’ve made to test specific features. Look up at the start of main:

main:
    # load the map and objects
    la  a0, level_1
    #la  a0, test_level_dirt
    #la  a0, test_level_diamonds
    #la  a0, test_level_vines
    #la  a0, test_level_boulders
    #la  a0, test_level_goal
    #la  a0, test_level_bug_movement
    #la  a0, test_level_bug_vines
    #la  a0, test_level_bug_goal
    #la  a0, test_level_blank
    jal load_map

See all those commented-out la lines? Each one of those is a test level for testing those specific features. Right now, we’re telling load_map to load level_1, but if we comment out that line and uncomment the test_level_dirt line like this:

    #la  a0, level_1
    la  a0, test_level_dirt
    #la  a0, test_level_diamonds
    #la  a0, test_level_vines
    #la  a0, test_level_boulders
    #la  a0, test_level_goal
    #la  a0, test_level_bug_movement
    #la  a0, test_level_bug_vines
    #la  a0, test_level_bug_goal
    #la  a0, test_level_blank
    jal load_map

When we run the game now, we see a totally different layout of tiles designed for testing the dirt-digging and placing abilities we are about to implement. Be sure to switch between test levels as you implement things. For grading, we will grade using level_1, and possibly use the other test levels to narrow down what works and what doesn’t.

4.1 Digging dirt

The player should be able to dig up dirt tiles on the tilemap by facing a dirt tile and pressing X.

In player_check_dig_input:

  1. Call input_get_keys_pressed
    • this is a new function different from input_get_keys_held, and returns a bitflag value of all the keys that went from “not pressed” to “pressed” on this frame.
    • it’s useful for actions that you don’t want to repeat when the key is held down.
  2. Check the return value for KEY_X just like you would with input_get_keys_held. If they pressed X…
    • Get the tile coordinates in front of the player object.
    • Get the tile at those coordinates.
    • If it’s dirt (TILE_DIRT),
      • Set the tile at those coordinates to TILE_EMPTY
      • Increment player_dirt, but only if it’s less than PLAYER_MAX_DIRT

This isn’t very complicated code, but I’m hoping you can be a little independent about looking up the functions you need in the reference page! This is a realistic simulation of what a lot of “real” programming is like: you have a library of functions and documentation for them, and it’s up to you to “glue them together” to solve a problem.

it's too late for me to worry about re-recording this gif, yes there will be some objects on the left

Done correctly, you should now be able to walk up to the dirt tiles, press X, and the tile should disappear and the count of dirt at the top of the screen should increase.

Be sure to test:

Also you can test the “maximum dirt” by temporarily changing player_dirt to 95, then digging some dirt; your dirt counter should never go above 99.

4.2 Placing dirt

Placing dirt is similar to digging, but a little more complicated because we have to do some more checks to see if it’s okay. The player should be able to place dirt tiles on the tilemap by facing an empty tile and pressing Z when they have at least 1 dirt tile in their inventory.

There is an additional condition here. At the very top of your proj1.asm, see this?

.eqv GRADER_MODE 0

If this is 1, it will allow you to place dirt even if you haven’t dug any up yet.

Anyway. Here’s how player_check_place_input will work:

Wow that’s a lot of “and”s. Here’s a tip on how to write a long nested conditional like this: put a _return: label before the leave, and just branch to _return whenever you need to give up (whenever any condition is NOT satisfied). So you’ll branch to it if they AREN’T pressing Z, and when player_dirt is 0, and when the tile in front of them is NOT empty etc.

“There is no object at the pixel coordinates in front of them” - you’ll need obj_get_pixel_coords_in_front for the first part of this, and you should use TILE_SIZE as the distance argument. From there, which function lets you see if an object exists at those coordinates?

You should now be able to dig up dirt with X, then place it elsewhere with Z.

Be sure to test:

You now have a 48%! You’re about halfway done!


5. Diamond objects

Let’s do something easy: diamond objects. (Use the test_level_diamonds level for this.)

Diamond objects behave very simply:

So here’s what obj_update_diamond needs to do:

  1. put the object index into s0!
  2. call obj_move_or_check_for_falling
    • this does the work of seeing if the diamond should fall, and if it should, it moves it.
  3. see if the diamond object is colliding with the player. there’s an obj_ function for this.
    • don’t forget you have to pass the object index as the argument!
  4. if it is,
    • increment player_diamonds
    • free the diamond object (pass that argument!)

As shown in the animation to the right, be sure to test that:


6. Vine tiles

Use test_level_vines for this. If you try it right now, you can walk all over the vine tiles like they’re not even there. That’s wrong.

The vines are supposed to be an obstacle. They’re uh, all thorny and stuff. So you can step on them, but it hurts, and so you step back immediately. Yeah.

A while ago I had you write some code in obj_update_player. It called player_check_vines and if it returned 1, it skipped getting input from the player. Now we’ll implement player_check_vines.

Remove the code in player_check_vines that makes it always return 0. Instead it should:

That’s it. Test that:

You now have a 60%!


7. Boulders

Use test_level_boulders for this.

Boulders are mostly kind of dumb objects that don’t do much, but the code to allow the player to push them from the left or right can be tricky.

First let’s get obj_update_boulder out of the way: just call obj_move_or_check_for_falling. That’s it. Seriously. One line. If you test it now, you should be able to dig dirt out from under the boulders and watch them fall, just like the diamonds did. See, common behavior implemented in one reusable function!

Now let’s revisit player_try_move. Remember that case for COLLISION_OBJ that was marked as “TODO” before? Let’s fill that out:

Why v1? Becuase if obj_collision_check returned COLLISION_OBJ in v0, then v1 contains the index of the object that is in the way! So we are going to try to push that object with player_try_push_object.

player_try_push_object takes the object to try to push in a0 and returns a boolean: it returns 1 if the player should be able to move, and 0 if the player should not be able to move (if they are pushing a boulder from the wrong direction).

Tangent: bending the rules of control flow structures

There are basically three outcomes of this function:

Rather than trying to massage this logic into a complicated set of nesting ifs, it’s honestly easier to treat it like a weird kind of switch-case, where most of the logic is in the switch, and the three cases are tiny. So you can structure this function like:

player_try_push_object:
enter
	# a bunch of code, where some branches branch to _push,
	# _return_yes, or _return_no

_push:
	# push the object!

# fall into the next case
_return_yes:
	li v0, 1
	j _return
_return_no:
	li v0, 0
_return:
leave

Alright. Here are the rules for pushing:

Things to test:

Wow, that was a big chunk of points! you now have a 76%.


8. The goal tile

Use test_level_goal for this.

The goal tile is how you win the game, and its behavior is pretty simple. Here’s how player_check_goal works: if the player is standing on a goal tile, AND bugs_saved == bugs_to_save, then set game_over = 1.

That’s it. That’s all you do. Do not jal show_game_over_message or do syscall 10 or anything like that. All that control flow is handled by main and check_game_over. All you have to do is set the game_over variable to 1.

When you step on the checkered goal tile, you should now see a congratulation message along with a display of how many diamonds you collected!

You’re at an 82%.


9. Bugs, the final boss of the project

The bugs are your friends. They navigate through the level on their own and eat vines that they step on. Your goal is to get them to the goal; once all the bugs make it to the goal, you can go into the goal too.

The overall shape of obj_update_bug is like:

// assuming you moved the object index argument into s0!
if(object_moving[s0] != 0) {
    obj_move(s0);
} else {
    if(bug is not on a goal tile) { // part 9.3
        eat vines;                  // part 9.2
        move;                       // part 9.1
    }
}

It’s up to you to split this code up into as many functions as you like. You could do everything in object_update_bug but it would be HUGE and confusing.


9.1 Bug movement

Use test_level_bug_movement for this.

This is definitely the hardest part of the project. After this, the other parts are easy. Bug movement works like so:

  1. If there is something solid in front of the bug…
    • note that down but don’t do anything yet.
  2. If there is something solid to the left of the bug…
    • if there was something solid in front of the bug in step 1,
      • turn the bug to the right.
    • else,
      • move the bug forward with BUG_MOVE_VELOCITY and BUG_MOVE_DURATION
  3. Else (there was nothing solid to the left of the bug)…
    • turn the bug to the left,
    • then move the bug forward with BUG_MOVE_VELOCITY and BUG_MOVE_DURATION

Yeah, it’s complicated. But it’s doable. “Is there something solid” and “move the bug forward” are things you know how to do.

“To the left of” and “turn the bug” are actually really easy to implement if you’re clever. Notice how the directions are defined:

# Cardinal directions.
.eqv DIR_N 0
.eqv DIR_E 1
.eqv DIR_S 2
.eqv DIR_W 3

The directions increase clockwise (turning to the right). They are in the range [0, 3], which is conveniently also the range of integers you get if you perform modulo by 4… And you know two ways to do that!

Good luck. Implemented correctly, the bug should walk around the entire perimeter of the test room as shown in the animation to the right. Also if you move down you can see three more bugs used to test some cases of movement. Yours should behave the same. (Also, poor bug in the bottom-left…)

If the bug walks upwards and then the game crashes with an alignment error as soon as it reaches the top wall, it’s very likely a bug (lol) in your obj_draw_bug function. Cmon, you know what alignment errors on array loads mean by now. I hope. 😬

You’re at a 92%.


9.2 Bugs eating vines

You’re in the home stretch now. Use test_level_bug_vines.

Bugs eating vines should now be a piece of cake for you. If a bug is sitting on a vine tile, set it to an empty tile. That’s all!


9.3 Bugs reaching the goal

Last piece. Use test_level_bug_goal.

Again this should be trivial. If a bug is sitting on a goal tile, free it, increment bugs_saved, and make sure the rest of obj_update_bug DOES NOT RUN because the bug is gone now.

Importantly, if you touch the goal before the bug gets to it, the game should not end! If it does, go back to player_check_goal because something is wrong there. (The animation shows this.)


You’re done!

Try the level_1 level and see if you can beat it. You have to help the bugs and they will help you. Placing dirt can help corral the bugs into the directions you want them to go. See if you can get all 18 diamonds too.


Submission

Be sure to review the grading rubric before submitting.

You will submit only your abc123_proj1.asm file (but renamed with your username).

Please put any important notes to the grader at the top of your asm file in comments. For example, if you wrote some code that is never called, they will not see the behavior; tell them that you attempted it and you may get some partial credit.

To submit:

  1. On Canvas, go to “Assignments” and click this project.
  2. Click “Start Assignment.”
  3. Under “File Upload,” click the “Browse” button and choose your abc123_proj1.asm file.
    • Do not submit any other files.
  4. Click “Submit Assignment.”

If you need to resubmit, that’s fine, just click “New Attempt” on the assignment page and upload it again. The last thing you submit is what we grade. But remember, if the last thing you submit is the wrong thing, you will receive a 20 point penalty.

It is okay to submit on/before the normal due date, and then resubmit on the late due date. You will get the late penalty, but if you turn in a 60 on time and a 100 late, that 100 becomes a 90, and it’s a net win anyway!