Title: Design Patterns for Games
1Design Patterns for Games
- Stephen Wong
- Dung Nguyen
- Rice University
2Lets Play a Game
- Suns Tic-Tac-Toe
- What can we learn from this?
- Arrays
- For-loops
- Nested Conditionals
- What arent we learning?
- Delineation of concepts from implementation
- Abstraction
- Design
3Something Different
- Its not really about TicTacToe
- Its a vehicle to teach something BIGGER.
- Abstraction
- Design Process
- Fundamental Principles
4Whats in a Game?
X X
O
O
Model
View
Rules
Buttons, etc.
Strategies
Display outputs
Players
Controller
5Game Model
Abstract these components!
- Rules of the Game
- Board configurations
- Legal moves
- Next Move Strategies
- Random
- Min-Max
- Etc.
- Player Management
- Turn taking
- Win/lose/draw notification
- Fault handling
Decouple these components!
6The Rules of the Game
IBoardModel
makeMove(player, row, col)
, chkMoveCmd)
, bdStatusVstr)
IUndoMove
Object execute(bdStatusVstr, param)
// other methods
commands
host/visitor
ICheckMoveCmd
validMoveCase()
IBoardStatusVisitor
invalidMoveCase()
player0WonCase()
player1WonCase()
drawCase()
noWinnerCase()
7State Diagram
Invalid Move State
Valid Move State
Non-Terminal State (no winner yet)
Terminal States
Player 0 Wins
Player 1 Wins
Draw Game
8State Design Pattern
state.execute(v, )
v.noWinnerCase()
v.player0WonCase()
v.player1WonCase()
v.drawCase()
9Playing the Game
ComputerPlayer
takeTurn()
The next move process is decoupled from the rules
of the game!
10Facade
IRequestor
TurnControl
IView
11What the player sees
public interface IRequestor public
abstract void setTokenAt( int row, int col,
int player, IRejectCommand
rejectCommand) public interface
IRejectCommand public abstract void
execute()
12Player Factories
Only displays the toString() of the factories.
The factories are treated as Objects!
IView
GameModel
private interface IMakePlayer public APlayer
create(int playerNo)
13The Set of APlayer Factories
Anonymous APlayer factory
public Vector getPlayers() Vector v new
Vector() v.addElement(new IMakePlayer()
public APlayer create(int playerNo)
return new HumanPlayer(requestor,
playerNo, turnAdmin)
public String toString() return "Human
player" ) return v
Factory method
IView only cares about this!
14Min-Max Principle
- V(s)
- For terminal state
- 1, if s is a winning state for that player
- 0, if s is a draw state
- -1, if s is a losing state for that player
- For non-terminal state
- maxV(c) c is a child valid move state of s ,
if that player moves next - minV(c) c is a child valid move state of s,
if the other player moves next. - The best next move for a given player, m, is
determined from maxV(c) c ? S where S is the
set of available moves for that player. How max
is computed is a variant.
Application of a process over a set!
15Mapping and Lambda
- Math/FP Map(?, S) ?(x) x ? S
- Express our algorithm in terms of mapping, not
iteration - min() ? map(?, min-accum)
- max() ? map(?, max-accum)
Both accumulators are abstractly equivalent!
- Backtracking is automatically handled by mapping.
16Mapping Abstraction
IBoardModel
makeMove(player, row, col)
, chkMoveCmd)
, bdStatusVstr)
IUndoMove
Object execute(bdStatusVstr, param)
void map(player, lambda, param)
Controls continuation of mapping
command
Called on all valid moves.
Called when there are no valid moves.
IBoardLambda
boolean apply(board, param, row, col, cell-val)
void noApply(board, param)
17Min-Max Abstraction
INextMoveStrategy
MinMax
accFacIAccFactory
AAccum makeAcc(player)
Point getNextMove(model, player)
minMaxEvalIBoardLambda
boolean apply()
void noApply()
AAccum acc accFac.makeAcc(player) model.getBoar
dModel().map(minMaxEval, acc) return
acc.getMove()
18private IBoardLambda minMaxEval new
IBoardLambda() public boolean apply(board,
acc, row, col, cell-value) IUndoMove undo
host.makeMove(row, col, acc.getPlayer(),
validMvVstr, new IBoardStatusVisitor()
player0WonCase(...) player1WonCase()
drawCase() noWinnerCase()
undo.apply(validUndo) return
acc.isNotDone()
Try a test move.
? to be mapped over the available states.
Update accumulator
Called by map on each valid (row, col)
Update accumulator
What to do in each situation
Update accumulator
Declarative style programming!
AAccumulator nextAcc acc.makeOpposite()
host.map(nextAcc.getPlayer(), minMaxEval,
nextAcc) acc.updateBest(row, col,
nextAcc.getVal()) return null )
Undo the move.
Stop mapping?
19Alpha-Beta Pruning
Just a new accumulator!
Override the creation of the next levels
accumulator
public class AlphaAcc extends MaxAcc
public AAccumulator makeOpposite()
return new BetaAcc()
this.modelPlayer AlphaAcc.this.modelPlayer
public boolean isNotDone()
return AlphaAcc.this.getVal() lt
this.getVal()
Accumulator for the opposite player as an
anonymous inner class.
Stop mapping if pruning condition is met.
Inner class gives the scoping we need!
Abstraction isolates the essence and provides
extensibility
20Player Management
Abstract players
Event-loop for turn-taking
Call-back techniques for asynchronous processing
21Design Patterns In Action
- MVC separates model from view
- Commands and Visitors isolate rules from
behaviors - State pattern models game behavior
- Calculating the next move is a Strategy
Design patterns express abstractions
22Concepts in Action
- Abstract functions lambdas
- Higher order functions Mapping
- Declarative programming
- Invariant Min-Max Principle
- Variant
- Full depth-first search
- Alpha-beta pruning
Its more than just a game!
23More Information
- Nguyen and Wong, Design Pattern for Games
(OOPSLA 2002) - Nguyen and Wong, Patterns for Decoupling Data
Structures and Algorithms (SIGCSE 1999) -