Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 [Tutorial] Basic Game Programming: Minesweeper, Part 3
Index -> Programming, Turing -> Turing Tutorials
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
DemonWasp




PostPosted: 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:

Turing:
Mouse.ButtonChoose ( "multibutton" )


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
Sponsor
sponsor
saltpro15




PostPosted: 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




PostPosted: 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.




PostPosted: 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




PostPosted: 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 Very Happy
DemonWasp




PostPosted: 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




PostPosted: 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 Very Happy

edit : or something like Audiosurf, without the music syncing (maybe have just random boxes appear while music plays?).
DemonWasp




PostPosted: 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
Sponsor
sponsor
Velocity




PostPosted: Wed Nov 09, 2011 9:37 pm   Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3

Wonderful tutorial. Helped alot:)
Yves




PostPosted: 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




PostPosted: Wed Nov 09, 2011 10:57 pm   Post subject: RE:[Tutorial] Basic Game Programming: Minesweeper, Part 3

I pointed him to this thread from another thread: http://compsci.ca/v3/viewtopic.php?p=246597#246597 .

Don't miss parts 2 through 4:

2 : http://compsci.ca/v3/viewtopic.php?t=21647
3 : http://compsci.ca/v3/viewtopic.php?t=21727
4 : http://compsci.ca/v3/viewtopic.php?t=21885
Display posts from previous:   
   Index -> Programming, Turing -> Turing Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 11 Posts ]
Jump to:   


Style:  
Search: