Combine everything you've learned so far to write a very simple maze game. (The maze is simple, not the program! Compare to Rogue and NetHack.)
Concepts: nested loops and conditionals; constants
Textbook: chapter 5 (review); 2.2 (constants)
Write a turn-based maze game. The maze is a simple 12x8 rectangle with a single exit. (So it's really just a big room, not a maze.) The player is represented by a single character on the screen. The player starts at a random place somewhere in the maze-room, and the exit is at some random location on the right/east side of the maze-room.
Each turn, print the current map of the maze-room and ask the user which direction they want to move.
The game ends when the player reaches the exit. Print some congratulatory message and end the program.
Drawing the map. The map must be the requested size, but which characters you use for the player, exit, and floor are up to you (as long as each is different). Here is what the maze might look like on the screen using '*' for the player, '=' for the exit, and '.' for the room floor.
............ ............ ............ ......*..... ............ ............ ...........= ............ Enter next move (N/E/S/W):
On the other hand, you might use 'P' for the player, 'E' for the exit, and ' ' for the floor. (' ' works for the floor only if you do the borders extra credit, below.)
-------------- | | | | | | | P | | | | | | E | | -------------- Which way do you want to move [(u)p, (d)own, (l)eft, or (r)ight]?
Input characters. You may choose from one of 3 direction input systems:
Your program is required to accept one of these systems, using lowercase letters. Clearly indicate in your program's output how the user should enter a move.
You may support additional input options, if you want: more than one system, uppercase characters, typed words, numbers, etc. (Note that it's easy to accept diverse input if you just uppercase or lowercase the input and then take the first character.)
Constants. In your program, use final
variables to hold the following details:
You should be able to change only the values assigned to these variables in order to change the size, shape, or look of your maze. (Of course, your program should still work correctly after making such changes.)
If you want to use methods, you can declare your constants inside your class but outside of any method, like this:
//constants used in drawing the maze public static final int MAZE_WIDTH = 12; public static final int MAZE_HEIGHT = 8; //...etc...
If you do this, these variables will be accessible from any method.
Instructions to the User. Be sure to explain any necessary rules, important characters, or input requirements in your user interface.
Player Placement and Movement. Place the player and exit randomly at the start of the game. (The exit should be somewhere on the right/east wall of the maze though.) Be careful not to initially place the player on the exit square. Also, do not let the player move beyond the bounds of the maze.
Error-handling. If the player enters something other than one of the valid movement choices, print an appropriate error message and ask the user to try again.
Coding Standards. As with all assignments since A11, you must follow Java Coding Standards while writing the code for this program.
Here is some sample output to give you a better idea of how a sample game might look from start to finish:
Move your character (*) to the exit (#). You may move North, South, East, or West. ............ ...........# ............ ........*... ............ ............ ............ ............ Enter next move (N/E/S/W): n ............ ...........# ........*... ............ ............ ............ ............ ............ Enter next move (N/E/S/W): E ............ ...........# .........*.. ............ ............ ............ ............ ............ Enter next move (N/E/S/W): down Sorry, but "down" is not a valid direction choice. Try again. Enter next move (N/E/S/W): quit Sorry, but "quit" is not a valid direction choice. Try again. Enter next move (N/E/S/W): e ............ ...........# ..........*. ............ ............ ............ ............ ............ Enter next move (N/E/S/W): e ............ ...........# ...........* ............ ............ ............ ............ ............ Enter next move (N/E/S/W): e You can't leave the maze except through the exit! ............ ...........# ...........* ............ ............ ............ ............ ............ Enter next move (N/E/S/W): n You've made it out of the maze!
As long as you satisfy the above requirements for the program (also see Grading, below, to double-check), you can go about solving this problem however you want. However, if you're stuck, here are the steps I'd suggest.
However you choose to go about it, remember to program one step at a time! You should be compiling, running, and testing your program after every few lines you write to make sure everything you've written so far is correct. Often, when writing loops, it helps to write code from the inside out, rather than top to bottom.
mazeExitY = (int) (Math.random() * MAZE_HEIGHT);If
MAZE_HEIGHT
is currently 10, this will set mazeExitY
to some random number between 0 and 9.
How you determine the coordinates of your maze-room is up to you. It's generally traditional in graphics to treat the upper-left corner as (0,0). This would make the lower-right corner of the room (MAZE_WIDTH - 1, MAZE_HEIGHT - 1). (The rest of these suggestions assume this system.) However, you could make the lower-left corner your origin, and/or you might want to start indexing at (1,1).
Remember to try printing out your variables a few times before continuing to make sure you're getting random numbers in the complete range.
x
for this loop.)
y
for this loop.) At this point, you should be able to print a 2D board/maze-room.
playerY--;
would update their position. However, before moving the character, make sure this doesn't move them off the board/out of the maze.
It is recommended you use methods to break up the program into more manageable chunks, but it is not required.
Once you meet the basic requirements for this maze game, you may add some additional features. Here are a few suggestions:
You may come up with additional ideas of your own. If you include any extra features, be sure to mention them in your initial class documentation.
Only a total +3 points of EC will be awarded, and only if the required features of the game work.
Upload your UsernameA13.java
file to Tamarin.
Out of 10 points:
Section 001: Practice for students (individually or in small groups) in lab. Use one or more loops to do the following:
Given a user-specified width:
*** *** ***
* ** *** ****
* ** *** ****
**** *** ** *
**** *** ** *
0 1 2 3 4 .. 11 12 1 1 2 3 4 11 12 2 2 4 6 8 22 24 3 3 6 9 12 33 36 . . . . . . 11 11 22 33 44 .. 121 132 12 12 24 36 48 .. 132 144
Sample solutions: DivisibleByThree.java (#1 and 1B) and PrintingBoxes.java (#2 to 6).
main
method. While this is simple in terms of methods, you'll probably find that main then gets very complex with so many nested structures--ifs, nested loops, try/catch blocks, etc.
It can be clearer if you try to break the problem into methods. You can use static methods if you want (though we have not yet covered these in lab); or you can use an object-oriented approach. Here is one such design you might want to try that uses a separate Maze
class:
Instance Variables and Constants: You'll want a playerX
and playerY
to track the player's current location, and at least a mazeExitY
. You should also define the necessary final
constants required above. (These constants can be static
class variables if you want.)
Maze()
- Initialize your instance variables with appropriate random values in the constructor.
print()
- Print the maze to the screen (or else return a String containing the maze for the UsernameA11
to print).
movePlayer(?)
- The type and number of the parameters are up to you. I'd recommend just one parameter (a char or int, perhaps?) that indicates the direction the player should be moved. This method should then update the playerX
and playerY
values to move the character to a new location. This method could then return a boolean
--true if the player actually moved or false if not (such as when such movement would take the player off the board or into a wall).
isPlayerAtExit()
- Returns true or false if the player is on the same square as the exit.
The rest of your code would go in the main method of UsernameA11
. There, you could do something like this:
Maze maze = new Maze(); //code: print game instructions while (!maze.isPlayerAtExit()) { maze.print(); //code: prompt user to enter a move. Based on that input: boolean moved = maze.movePlayer(???); if (!moved) { //player hit a wall, so print an error message } }
Note that error checking of the user input is not shown here. Still, this approach will make your main method much simpler. However, the trade-off is that you have to deal with the complexity of breaking your code into methods.
for
loop go?
for (int y = 0; y < MAZE_HEIGHT; y++) { for (int x = 0; x < MAZE_WIDTH; x++) { System.out.print(FLOOR); } System.out.println(); }This just prints the FLOOR for every character. So you will need to add some conditionals (
if/else-if/else
statements) to determine which character to print out at each square. The loop counters x and y tell you what location you are currently printing. You will need to check to see if that (x, y)
location is the same as the player's location (playerX, playerY)
. If so, print PLAYER. If it's the location of the exit, print EXIT instead. Otherwise, just print FLOOR.
for (int y = 0; y < MAZE_HEIGHT; y++) { for (int x = 0; x < MAZE_WIDTH; x++) { if (/* x and y == playerX and playerY */) { /* print the player */ }else if (/* x and y == exitX and exitY */) { /* print the exit */ }else { System.out.print(FLOOR); } } System.out.println(); }Note how only one character will be printed each time through the loop(s)--either the player, the exit, or the floor.
while (player has not reached the exit) { /* * Print the map (using two nested for loops, as shown above */ /* * Ask user to move. * (Make sure they enter a valid move. * Don't move the player off the map.) * Update playerX/playerY to new player location. */ } /* * (Loop ended because player finally reached the exit.) * Print congratulatory message. */You might move some of this code out of the while loop and into a couple methods, but it's not required. You will still need this basic loop structure somewhere.
One option is to use a loop: keep generating the player's initial x and y position until it is not
the same as the exit. This is the safest way, and will work for any sized maze larger than 1x1. (Note that just an if
here is not sufficient--though very unlikely, you could still randomly generate the same player starting position a second time in a row.)
Another option is to just move either the player or the exit by one square to prevent the collision. Since the exit has to stay on the right well (unless you are doing the EC) and could currently be at the top or bottom row, it's easier to just move the player left by one square if she starts on the exit. This works well enough and is always safe on any maze with a width >= 2.
Finally, you could just never place the player in the same column as the exit. This is not as good as one of the two above techniques in terms of greatest randomness, but it's better than nothing and is the simplest option.
playerX
and playerY
variables. Therefore, before you do that, just test whether that would in fact take the player outside of the maze. For example, if the player is currently on the far left side of the maze (playerX
== 0) and tries to move left/west, that would mean playerX
would go to -1. That would be outside the maze, so, instead of decrementing playerX
, print an error message instead.
An alternative technique is just to move the player and then test if the player is now outside the maze. If so, print the error message and move the player back. However, this requires you to test which way the player just moved so you can correctly move her back again, so this technique is not any shorter than testing before you move.