controlling your game
Delphi programming techniques(3)


This articles gives some suggestions how to control your (board) game.
Illustration below shows the layout of such a game, connect4 or whatever.
There is a board, where pieces are placed by the players.
A game is started by pressing the "start" button.
A game is ended by pressing the "new game" button.
Properties as level or strategy are selectable by the "property" buttons.

First of all, procedures have to be written to paint or erase the board.
These are low level paint procedures.

Next, data structures must be designed, such as
    - a two dimensional array representing the board state
    - a one dimensional array holding the list of moves
    - additional arrays of constants that control game behaviour

note: the low level paint routines must not use the arrays, which are higher in level.
Reason is, that a change of the data structures during the design phase does not need a redesign
of the low level paint routines.


Next, the high-level procedures must be designed.
They take care of the moves by altering the arrays and calling the low level paint routines.

The high level procedures are called by events from mouse or keyboard.
Problem here is, that actions originating from such events depend on the situation of the game.
So, together with the data structures, the game states must be defined

type TGameState =
(gsInitializing, gsMenu, gsPlayerMove, gsComputermove, gsPlayerWin, gsComputerwin, gsEnd);

var GameState : TGameState;

gsInitializingduring startup of game. Initialize tables such as clearing the moves list.
gsMenuallow for property selections between games.
gsPlayerMoveallow mouse or keyboard events to define player move.
gsComputerMoveblock events, while computer calculates/displays it's move.
gsPlayerWingame end by winning of player.
gsComputerWingame end by winning of computer.
gsEndgame end, board full, no winner.

Besides the mouse and keyboard events, it is convenient to consider the control buttons as well as events.

type TGameEvent =
(geInitDone, geStart, geNewGame, gePropertyChange, gePlayermoveDone, geComputermovedone);

geInitDoneinitialization of tables is completed.
geStartthe start button was pressed.
geNewGamethe new game button was pressed.
gePropertychangea property button was pressed.
gePlayerMoveDonethe player has issued a move.
geComputerMoveDonethe computer has finished it's move.


Above events are best handled by nested case statements.
This is how the structure looks:
procedure GameControl(gev : TGameEvent);
begin
  case GameState of
    gsInitializing  : case gev of
	                 geInitDone : setmenumode;
		         end;   
    gsMenu : case gev of
               geStart : startgame;
               geNewGame : setmenumode;
               gePropertyChange : setproperties;
             end;	 
    gsPlayerMove : case gev of
                     geNewGame : setmenumode;
                     gePlayermoveDone : docomputermove;
                   end;	
    gsComputermove : case gev of
                      geComputermovedone : doplayermove;
                     end;   
    gsPlayerWin : case gev of
                    geNewGame : setmenumode;
                  end;   
    gsComputerwin : case gev of
                      geNewGame : setmenumode;
                    end;
    gsEnd: case gev of
             geStart: startgame;
             geNewGame : setmenumode;
           end;	
  end;	
So, the procedures setmenumode, startgame, docomputermove, etc are triggered by
events that are redirected according to the game state.
They call the hi-level procedures to do the job, such as enabling/disabling property buttons,
triggering timers to accomplish a move.
They direct keyboard and mouse events allowing the the player to move.
Once the player has moved, a call to gamecontrol with gev = gePlayerMoveDone
informs gamecontrol, which then must call a procedure to test for win or end situation
or switch the gamestate to gsComputerMove.

Below is a coarse structure of a typical board game where the player plays against the computer.
The proc1..n procedures are the high level helpers of the gameControl routine to change gamestates,
alter the game data structures as moves are done, enable/disable property selection buttons,
or display informative messages to the player.