[Tutorial] Basic Game Programming: Minesweeper, Part 3
Author |
Message |
DemonWasp
|
Posted: Wed Aug 05, 2009 8:56 pm Post subject: [Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
Prerequisites
This is part 3 of the Basic Game Programming tutorial. Find the previous parts here: part 1, part 2. Complete both of these parts first.
User Input
The user input for this particular program comes almost exclusively from the mouse. The only real wrinkle is that we need Turing to treat our mouse as having two buttons - a left and a right - while its default is to treat all mouse buttons as a single left-click. We can do this with a simple command:
We'll put this right at the top of the file, next to our View.Set line from part 2 - this ensures that our setup is complete before we need to detect multi-button interaction.
Input: Blocking or Polling?
There are two main kinds of input, one of which will be better for our purposes. These types are as follows:
Blocking Input: this is the kind of input that suspends your program while it receives input. This is what happens when you use "get <variable-name>". However, suspending our program while we wait for input isn't suitable in this case.
Polling Input: this is the kind of input that detects the current status of input devices, rather than waiting for input. This is what happens when you use "Input.KeyDown()". This is the kind of input we'll be using for our program, because this allows us to continue executing while we get input.
Our basic idea is that we'll have a single main game loop where we do something like this:
code: |
loop
get-user-input
handle-user-input
draw-map
exit when check-for-gameover
end loop
|
get-user-input
Our get-user-input portion is fairly simple and uses a built-in function to poll the mouse for state: Mouse.Where().
Turing: |
% Variables to store input state - this goes before the loop
var mouse_x, mouse_y, mouse_button : int := 0
var mouse_grid_x, mouse_grid_y : int
% Gathering user input
Mouse.Where ( mouse_x, mouse_y, mouse_button )
mouse_grid_x := floor ( mouse_x / scale_x ) + 1
mouse_grid_y := floor ( mouse_y / scale_y ) + 1
|
The part after the Mouse.Where line is where we convert from pixel-coordinates we get from Mouse.Where() to grid coordinates, which we can use as indexes into our map and user_state arrays (see part 1).
handle-user-input
The first thing we need to do is reject invalid input. We'll do this by clearing the mouse_button variable, which makes it look like no buttons are being pressed. The only invalid input we need to ignore, for the moment, is clicks outside the play area (this happens whenever we click on another application).
Turing: |
% Ignore (clear) all clicks outside the play area.
if mouse_grid_x < 1 or mouse_grid_x > game_size_x or mouse_grid_y < 1 or mouse_grid_y > game_size_y then
mouse_button := 0
end if
|
Let's get left-clicking working first. The rules for left-clicking are as follows:
1. If the user left-clicks on a mine, they immediately lose. We reveal the map, output a message to this effect, and end the program.
2. If the user left-clicks anywhere else, that square is revealed - number or blank as appropriate. The only wrinkle to this rule is that whenever an empty square is revealed, all the squares around that square will be revealed as well; this will be dealt with in the next part.
Turing: |
% Handle valid clicks (within the play area)
if mouse_button = 1 and and user_state ( mouse_grid_x, mouse_grid_y ) = USER_NONE then % left-click = reveal that square
user_state ( mouse_grid_x, mouse_grid_y ) := USER_REVEALED
case map ( mouse_grid_x, mouse_grid_y ) of
label MAP_MINE :
reveal_map ()
draw_map ()
Text.Locate ( 1, 1 )
put "Boom, you're done!"
exit
label MAP_EMPTY :
% TODO in part 4: reveal_area ( mouse_grid_x, mouse_grid_y )
draw_map ()
label :
draw_map ()
end case
end if
|
This implies that we will need a new method to reveal the entire map. You can probably come up with this yourself, but I'll show it here anyway. This will go with the rest of our procedures, above the main loop, so that the procedures will be declared before they're accessed.
Turing: |
procedure reveal_map()
for x : 1..game_size_x
for y : 1..game_size_y
if user_state ( x, y ) = USER_NONE and map ( x, y ) not= MAP_MINE then
user_state ( x, y ) := USER_REVEALED
end if
end for
end for
end reveal_map
|
Next, we'll work on making right-clicking work. The rules for right-clicking work like this:
1. If the square has been revealed, don't do anything.
2. If the square hasn't been marked yet, change it to a flag.
3. If the square has been flagged, change it to a question mark.
4. If the square has been given a question mark, change it to unmarked.
Turing: |
elsif last_mouse_button = 100 and mouse_button = 0 then % right-click = cycle between none / flag / question
case user_state ( mouse_grid_x, mouse_grid_y ) of
label USER_NONE :
user_state ( mouse_grid_x, mouse_grid_y ) := USER_FLAGGED
label USER_FLAGGED :
user_state ( mouse_grid_x, mouse_grid_y ) := USER_QUESTION
label USER_QUESTION :
user_state ( mouse_grid_x, mouse_grid_y ) := USER_NONE
label USER_REVEALED :
% Do nothing otherwise
end case
draw_map()
|
Pre-Game Setup
We need to make sure that all variables have been properly initialized before we try to access them. This is similar to the temporary test code from part 2.
Turing: |
% Pre-game Setup
for x : 1 .. game_size_x
for y : 1 .. game_size_y
map ( x, y ) := MAP_EMPTY
user_state ( x, y ) := USER_NONE
end for
end for
place_mines ()
draw_map ()
|
Putting it Together
This is the combination of the above code, put into the main game loop.
Turing: |
% MAIN GAME LOOP
var mouse_x, mouse_y, mouse_button : int := 0
var mouse_grid_x, mouse_grid_y : int
% Pre-game Setup
for x : 1 .. game_size_x
for y : 1 .. game_size_y
map ( x, y ) := MAP_EMPTY
user_state ( x, y ) := USER_NONE
end for
end for
place_mines ()
draw_map ()
% Play the game
loop
Mouse.Where (mouse_x, mouse_y, mouse_button )
mouse_grid_x := floor (mouse_x / scale_x ) + 1
mouse_grid_y := floor (mouse_y / scale_y ) + 1
% Ignore (clear) all clicks outside the play area.
if mouse_grid_x < 1 or mouse_grid_x > game_size_x or mouse_grid_y < 1 or mouse_grid_y > game_size_y then
mouse_button := 0
end if
% Handle valid clicks (within the play area)
if mouse_button = 1 and user_state (mouse_grid_x, mouse_grid_y ) = USER_NONE then % left-click = reveal that square
user_state (mouse_grid_x, mouse_grid_y ) := USER_REVEALED
case map (mouse_grid_x, mouse_grid_y ) of
label MAP_MINE :
reveal_map ()
draw_map ()
Text.Locate (1, 1)
put "Boom, you're done!"
exit
label MAP_EMPTY :
% TODO (part 4): reveal_area ( mouse_grid_x, mouse_grid_y )
draw_map ()
label :
draw_map ()
end case
elsif mouse_button = 100 then % right-click = cycle between none / flag / question
case user_state (mouse_grid_x, mouse_grid_y ) of
label USER_NONE :
user_state (mouse_grid_x, mouse_grid_y ) := USER_FLAGGED
label USER_FLAGGED :
user_state (mouse_grid_x, mouse_grid_y ) := USER_QUESTION
label USER_QUESTION :
user_state (mouse_grid_x, mouse_grid_y ) := USER_NONE
label USER_REVEALED :
% Do nothing otherwise
end case
draw_map ()
end if
end loop
|
This code will execute nicely, and it even works quite a bit like real minesweeper - you can play a game if you want, though there's still a substantial amount missing.
Next Lesson
In the next lesson, we'll detect when a player wins the game, make contiguous empty areas appear whenever we reveal any of their squares, and make some other minor improvements. We're nearly done!
As always, comments, questions and concerns are welcomed. |
|
|
|
|
|
Sponsor Sponsor
|
|
|
saltpro15
|
Posted: Thu Aug 06, 2009 9:45 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
well done as always DemonWasp. I'm going to recommend these to our CS teacher for the grade 10 class this year |
|
|
|
|
|
DemonWasp
|
Posted: Fri Aug 07, 2009 11:43 am Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
Cool, thanks! The only real catch for teaching this at a grade 10 level is that part 4 is going to require recursion (for contiguous empty fields), which can be a difficult concept for some people. |
|
|
|
|
|
andrew.
|
Posted: Fri Aug 07, 2009 4:13 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
It becomes easier to understand in grade 11 though because you learn recursion in U level math. |
|
|
|
|
|
saltpro15
|
Posted: Fri Aug 07, 2009 4:28 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
I would recommend them to our gr11 class if it weren't for the fact that our gr11 class this year is basically just hardcore people, which is a blessing I suppose...
I'll gladly teach people recursion |
|
|
|
|
|
DemonWasp
|
Posted: Fri Aug 07, 2009 8:38 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
The big problem with Gr11 CS in Ontario is that anyone who wants to take it can, without the Gr10 prerequisite. This makes it impossible for teachers to assume any level of computer knowledge, which effectively puts the class back at square one, forcing the teacher to either duplicate the Gr10 course or go a lot faster to get to new material.
Recursion is a new concept, but this case is fairly obvious - each empty square will reveal the 8 around it; when those squares are empty, they're revealed too and we continue checking. If it's not in the curriculum, he can probably just gloss over it or offer it as a subject for the keeners.
If you can get him to show you the cirriculum for Gr10/11/12 CS, I'd like to see it - that way, I can structure (some of) my tutorials more towards those requirements. |
|
|
|
|
|
saltpro15
|
Posted: Fri Aug 07, 2009 9:03 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
I'll check with him and get back to you. I happen to know for a fact that our gr11 CS class only has about 7 people in it, and all of us took the gr10 prerequisite and excelled in it.
have you ever played or heard of the game Cube Field? I think that or Tetris would be a good choice for gr11 CS, since both would require OOP and gr11 is basically the intro to OOP
edit : or something like Audiosurf, without the music syncing (maybe have just random boxes appear while music plays?). |
|
|
|
|
|
DemonWasp
|
Posted: Fri Aug 07, 2009 9:28 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
I was thinking I might do a SimCity clone (like, original SimCity here). Alternately, some simplistic RPG game would possibly qualify for the "intermediate" level. |
|
|
|
|
|
Sponsor Sponsor
|
|
|
Velocity
|
Posted: Wed Nov 09, 2011 9:37 pm Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
Wonderful tutorial. Helped alot:) |
|
|
|
|
|
Yves
|
Posted: Wed Nov 09, 2011 10:29 pm Post subject: Re: [Tutorial] Basic Game Programming: Minesweeper, Part 3 |
|
|
You necro'd a thread that was dead for 2 years... look at the post date next time? |
|
|
|
|
|
DemonWasp
|
|
|
|
|
|
|