Project 9

roguelike

The Project

This project is going to be a little different than projects we have done in the past. You'll get some starter code for a terminal-based "roguelike" game, some ideas about features you could add to it, and student code from last year. Your job will be to:

  • decide what features you want to add
  • incorporate the code for at least one feature from someone else's roguelike into your own code AND CREDIT THEM (at least one feature)
  • write your own original code for at least one feature and be able to explain it
  • include at least 3-5 features

This assignment doesn’t have quite as many rules as we’ve had in previous projects - the goal is for you have fun adding features to your game!

Do this project on repl.it by looking in our Unit 7 - Dictionaries repl.it classroom in the Projects section (right under the Assignments section). You should see a project called "Roguelike for real". The first time I uploaded it, there were errors (oops! fixed now).

What’s A Roguelike?

In the old days, there was a game called Rogue. It looked like this:

The player’s character is the little yellow smiley face. Wikipedia says: In Rogue, players control a character as they explore several levels of a dungeon as they seek the Amulet of Yendor located in the dungeon’s lowest level. The player-character must fend off an array of monsters that roam the dungeons. Along the way, they can collect treasures that can help them offensively or defensively, such as weapons, armor, potions, scrolls, and other magical items. Rogue is turn-based taking place on a square grid represented in ASCII.

A lot of the ideas originally found in Rogue have since spread to tons of other video games, and we call those games roguelikes. Here’s a very complex one called Brogue:

In this project, you’ll be building a roguelike of your own.

The starter code

The starter code comes with three files: level.txt, roguelib.py, and main.py.

1: level.txt

A simple text file that is a map of the level!

If you want the game’s level to look different, you can edit level.txt. To have more levels/maps, you might rename it level1.txt and add more files named level2.txt, start.txt, boss_level.txt, etc.

For the starter code, a '#' is a wall, a ' ' is an empty space, and a '@' is the player’s starting position. You’re the @ sign, you can move around, you can’t go through walls, and you can quit the game with 'q' when you get bored.

It looks like this:

#######################
#                     #
#                     #
#                     #
#                     #
#                     ###############
#                                   #
#                                   #
#                                   #
#                                   #
#                                   #
#                                   #
#############################  ######
                      #    @        #
                      #             #
                      #             #
                      #             #
                      #             #
                      ###############                                                                                                                                               

Please enter a command and then press Enter.                                                                                                                                        Available commands:                                                                       [u]p - move up                                                                            [r]ight - move right                                                                      [d]own - move down                                                                        [l]eft - move left                                                                        [q]uit - quit the game                                                                    
> 

NOTE:

Make sure your map is a rectangle, even if you have to put spaces at the end of a line.


2: roguelib.py

A Python file that contains some useful functions that can be combined together in order to make a simple roguelike game.

Go ahead and read through this file - you don’t have fully read and understand every single line of code, but it would be a good idea to look through the file and see what functions it defines and what their docstrings say the functions do.

You’ll have to make changes to some of these functions when you add new features to the game!


3: main.py

A small Python file that combines the functions from roguelib.py together into a simple game loop.

Read the game loop and convince yourself that you understand how it works. Refer back to the code in roguelib.py whenever main.py uses a function that don’t fully understand.

Feel free to add more code to this file. You can also create more files to hold parts of your code--it’s a useful thing to do as your project gets bigger.

As a general rule, it’s hard to read and understand a big complicated file with 1000 lines of code in it and a vague name like program.py. It’s much easier to read and understand several smaller files, each of which has 150-200 lines of code and a good clear name like ai.py or physics.py or quest.py.


Getting Started


Seriously, Read Those Files

This is what programming in real life is like. It’s almost never the case that you’re starting a new program completely from scratch - you’ll almost always be working on a (probably really big!) program that someone else wrote years ago, or that you wrote a few weeks ago but it’s been long enough that now you don’t really remember what all the code does.

This is what you do: you read through the preexisting code, convince yourself that you understand what it does and how it fits together, and then, once you’re familiar with the codebase, you can start making changes and adding features.


Your Goal

The point of this project is for you to add several features to make your roguelike interesting and fun to play. I'd like you to:

  1. come up with an original idea (one or more) and write its code, AND
  2. find a cool feature (one or more) in another student's roguelike and incorporate that code into your own roguelike, making sure to CREDIT the student(s) in a comment on the first line of the main.py file, something like this:
# Thanks to Tamara O'Malley for the coin pickup code.


Here are some ideas for features.

  • A goal space (if the player makes it to this space, they’ve beaten the game!)
  • Monsters that just sit there and don’t do anything
  • A combat system (when you bump into a monster, you fight it!)
  • XP / leveling up / stats
  • Monster AI (monsters follow you around!)
  • A combat log that says stuff like “you strike the skeleton for 3 damage!”
  • A dog that you can pet
  • Items / power-ups
  • Doors / traps
  • Spells / ranged combat
  • Puzzles
  • Quests
  • Randomly generated levels

If you come up with an idea you like better, do that instead!


Below is a walkthrough of adding 2 features. If you're not sure where or how to start, keep reading...


Adding A Goal Space

For starters, let’s add a simple feature to the game: a goal space. If the player reaches it then we’ll tell them good job and end the game.

It’ll look like this:

#######################                                                                   
#                     #                                                                   
#                     #                                                                   
#                     #                                                                   
#                     #                                                                   
#                     ###############                                                     
#                                   #                                                     
#                                   #                                                     
#                                   #                                                     
#                                   #                                                     
#                                   #                                                     
#                                   #                                                     #############################  ######                                                                           #             #                                                                           
#      @      #                                                                           
#             #                                                                           
#      !      #                                                                           
#             #                                                                           ###############                                                                           

Please enter a command and then press Enter.
Available commands:
[u]p - move up
[r]ight - move right
[d]own - move down
[l]eft - move left
[q]uit - quit the game
>                                                                                                                                                                                                                                                                                                                                                                       


Here are the things I did in order to add that feature to the game:

1) Add a goal space to the map

2) Find the position of the goal space in the map

3) Print the goal space

4) Do something when the player reaches the goal space


The code for a goal space

Once I had done all of those things, I had a working goal space feature! Notice that this involved messing around with a lot of the code in roguelib.py - you’ll have to do this too when you add your own features. It’s going to involve reading code and figuring out what it does and figuring out what you want to do and where you should make your changes. This is what programming is like! :)


Click here for the code I wrote to make that happen. Then read the rest of this section!


This file is a “diff”, which stands for “the difference between an old version of a program and a new version of that program”.

NOTE: red lines are old lines, green lines are new lines

  • if there is ONLY a green line, I ADDED that line
  • if there is ONLY a red line, I REMOVED that line
  • if there is a PAIR of lines (a red line followed by a green line), I CHANGED the line--carefully EDIT the line, don't add the green line as an additional line

NOTE: look for the function name between the blue/purple @@ symbols

NOTE: look for the file name at the end of a line of blue text, as seen on line 1: diff --git a/roguelike/game.py b/roguelike/game.py

NOTE: game.py is the same as main.py -- repl.it requires the "runner" file to be named main so we had to change it


As you scroll through, you might notice that I included a couple of assert statements (see lines 61 and 69 in the diff). You don’t have to do that when you add a feature, I just did it for this particular feature in load_level() because I wanted the program to loudly and specifically complain if someone either a) forgot to add a goal space or b) tried to add two goal spaces even though the way I wrote this code only supports tracking the (x, y) position of a single goal space.


As you add the goal space to your roguelike, it might be helpful to refer to the following list of where the changes are made. LOOK CAREFULLY at the couple lines of code shown in the diff before and after each change to make sure you are adding the code in the right place. The line number of the diff is NOT the same as the line number of your code.

Add a goal space to the map.

In level.txt, add the goal space. I used the character !.

(lines 28-29)

Notice that I added the goal really close to the player so that when I test it I don't have to travel across the whole map. I can always move it later once I know it works.

Find the position of the goal space in the map.

In roguelib.py, update load_level():

* look for the goal space character, !, in level.txt

(lines 51, 60-62, 69)

* save its (x, y) coordinate position in the game dictionary.

(line 73)

Print the goal space.

In roguelib.py, update draw_game() so that it prints out a ! where the goal space is.

(lines 40-42)

Do something when the player reaches the goal space.

In main.py, update run_game() to print a message and end the game (break) when the player reaches the goal space.

(lines 9-15)

You might be thinking: but what if I want to add a feature that can happen on more than one space at a time? Keep reading!


Implementing Dumb Monsters

Here’s a feature that adds dumb monsters to the level -- let's call them goblins. They just sit there and don’t do anything, but the player can’t move into a space if it has a goblin in it. Here’s what that looks like:

#######################                                                                   
#                     #                                                                   
#                     #                                                                   
#                     #                                                                   
#                     #                                                                   
#                     ###############                                                     
#                                   #                                                     
#           @        g              #                                                     
#           g                       #                                                     
#                                   #                                                     
#                                   #                                                     
#                                   #                                                     #############################  ######                                                                           #             #                                                                           
#             #                                                                           
#             #                                                                           
#             #                                                                           
#             #                                                                           ###############                                                                           

Please enter a command and then press Enter.
Available commands:
[u]p - move up
[r]ight - move right
[d]own - move down
[l]eft - move left
[q]uit - quit the game
>                                                                                                                                                                                                                                                                                                                                                                       


Here are the things I did in order to add that feature to the game:

1) Add some goblins to the map

2) Find the position of each goblin in the map

3) Print the goblins

4) Do something when the player reaches a goblin


The code for a dumb monster

Notice that this feature is a bit more complicated than the goal space feature was. First, a goblin is a player so I created it as a dictionary; it holds information like its type (goblin), its x and y coordinates, and a couple other things. And because there can be multiple goblins in the level, I had to keep track of them in a list. This list could potentially hold other types of monsters as well.


Click here for the code I wrote to make that happen. Then keep reading...


As you add goblins to your roguelike, it might be helpful to refer to the following list of where the changes are made.

Add goblins to the map.

In level.txt, add some monsters. I used g for goblin.

(lines 11-12)

Notice that again, I added them really close to the player so that when I test it I don't have to travel across the whole map. I can always move them later once I know it works.

Find the positions of the goblins in the map.

In roguelib.py, update load_level():

* look for each g in level.txt, create a goblin monster by storing its information (type, position, etc) in a dictionary, and add each goblin to a list of monsters

(line 75, 84-92)

* add the list of monsters to the game dictionary.

(line 101)

Print the goblins.

In roguelib.py, update draw_game() so that it prints out a g where the goblin is.

(lines 51-55, 61-66)

Do something when the player reaches a goblin.

In roguelib.py, update move() and process_user_input() so that the player can't move on top of a goblin.

(move(): lines 25, 31, 39-42)

(process_user_input(): line 110)

Notice That There’s A Pattern Here

We followed these steps when implementing both of those features:

  1. Choose a character that hasn’t been used for some other feature already, like , and add it to level.txt one or more times.
  2. Update load_level() to look for that character AND save its position in the game dictionary. If it needs to hold information, make it a dictionary that holds the position (along with whatever other info you need). If you're adding more than one of the character, store them in a list.
  3. Update draw_game() to print that character out in the right place so that the player can see this new thing you added to the game. Or not...maybe there's an invisible monster? Yikes!
  4. Here's the tricky part: add code that makes this feature actually do something. For the goal space we added code that ends the game if the player reaches the goal space; for monsters, we added code that prevents the player from moving into a space that has a monster in it.

Steps 1 through 3 were similar both times, but step 4 was different for each feature.


Now it's your turn! Add:

  • at least one more feature that is your own original design, and
  • at least one more feature that you got from another student. Don't forget to credit them!

Also feel free to modify the goal space / dumb monster features if you like.

Submitting your project

Before submitting your project, make sure you:

  • test your program a bunch of different ways to make sure it works!
  • update the name of the project to be the name of your game + your name, like dragonquest by Tamara
  • update the description of the project to (briefly) describe your game features
  • credit the student whose code you borrowed on the first line of the main.py file, something like this: # Thanks to Tamara O'Malley for the coin pickup code.


Your CODE should always follow the style guidelines. The part about descriptive names is important! For instance:

  • n is a bad name, username is a good one.
  • ns is a bad name, number_of_symbols is a good one.


Since we are doing this in the repl.it classroom as a Project, I have access to your code and your name should be in the project name. No need to turn it in on Google Classroom.


Most of the projects in this class were created by JR Heard, a TEALS volunteer at Madison, 2017-2019. His version of this project lives at https://blog.jrheard.com/python/roguelike.