Design Patterns for Games - clear.rice.edu

Design Patterns for Games
Stephen Wong
Dung Nguyen
Rice University
Let’s Play a Game


Sun’s Tic-Tac-Toe
What can we learn from this?
– Arrays
– For-loops
– Nested Conditionals

What aren’t we learning?
– Delineation of concepts from implementation
– Abstraction
– Design
Something Different…

It’s not really about TicTacToe…
– It’s a vehicle to teach something BIGGER.
Abstraction
 Design Process
 Fundamental Principles

What’s in a Game?
Model
X
Rules
Strategies
Players
adapter
adapter
O
Controller
O
Install adapters
View
X
Buttons, etc.
Display outputs
Game Model

Rules of the Game
– Board configurations
– Legal moves

Next Move Strategies
Abstract
these components!
– Random
– Min-Max
– Etc.

Player Management
Decouple
these components!
– Turn taking
– Win/lose/draw notification
– Fault handling
Game Model Classes
GameModel
{Game Management}
•Game initialization
• Win/lose/draw notification
IBoardModel
{Rules of the Game}
•Board configurations
•Legal moves
Abstracted &
Decoupled!
TurnControl
{Player Management}
INextMoveStrategy
APlayer
{Turn Taker}
{Next Move Computation}
•Random
•Min-max/Alpha-beta
•Limited depth
•Custom
State Diagram
Invalid Move State
Valid Move State
Non-Terminal State
(no winner yet)
Terminal States
Player #0 Wins
Player #1 Wins
Draw Game
State Design Pattern
IBoardModel
execute(IBoardStatusVisitor v, …)
TicTacToeBoard
state.execute(v, …)
state
ABoardState
v.noWinnerCase(…)
ATerminalState
Player0Won
v.player0WonCase(…)
Player1Won
NonTerminalState
DrawGame
v.player1WonCase(…)
v.drawCase(…)
The Rules of the Game
IBoardModel
IUndoMove makeMove(player, row, col), chkMoveCmd), bdStatusVstr)
Object execute(bdStatusVstr, param)
// other methods
commands
host/visitor
ICheckMoveCmd
validMoveCase()
invalidMoveCase()
IUndoMove
apply(…)
IBoardStatusVisitor
player0WonCase(…)
player1WonCase(…)
drawCase(…)
noWinnerCase(…)
Playing the Game
ComputerPlayer
INextMoveStrategy
takeTurn(…)
Point getNextMove(…)
Random
MinMax
AlphaBeta
The next move process is decoupled
from the rules of the game!
Facade
APlayer
takeTurn(…)
IRequestor
IBoardModel
makeMove(…)
TurnControl
IView
What 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();
}
Player Factories
IView
Only displays the toString()
of the factories.
The factories are treated
as Objects!
Selected
APlayer
factories
Set of
APlayer
factories
GameModel
private interface IMakePlayer {
public APlayer create(int playerNo);
}
The Set of APlayer Factories
public Vector getPlayers() {
Vector v = new Vector();
Anonymous APlayer
factory
Factory method
v.addElement(new IMakePlayer() {
public APlayer create(int playerNo) {
return new HumanPlayer(requestor, playerNo, turnAdmin);
}
public String toString() { return "Human player"; }
});
return v;
}
IView only cares
about this!
Min-Max Principle
V(s) =
For terminal state
 +1, if s is a winning state for that player
 0, if s is a draw state
Application of
 -1, if s is a losing state for that player
a process
For non-terminal state
over a set!
 max{V(c) | c is a child valid move state of s} ,
if that player moves next
 min{V(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 max{V(c) | c  S} where S is the set of available
moves for that player. How max is computed is a variant.
Mapping and Lambda

Math/FP: Map(, S) = {(x) | x  S}
Both
accumulators
are abstractly
equivalent!

Express our algorithm in terms
of mapping, not iteration:
– min(…)  map(, min-accum)
– max(…)  map(, max-accum)

Backtracking is automatically handled by
mapping.
Abstract Accumulators
Intelligent
component
AAccum
void updateBest(row, col, value)
Point getMove()
AAccum makeOpposite()
IAccFactory
AAccum makeAcc(player)
Opposite is
MinAcc
MinAcc
MaxAcc
Mapping Abstraction
IBoardModel
IUndoMove makeMove(player, row, col), chkMoveCmd), bdStatusVstr)
Object execute(bdStatusVstr, param)
void map(player, lambda, param)
Controls
Called on all valid
continuation
of
Called when
there
moves.
mapping
are no valid
moves.
command
IBoardLambda
boolean apply(board, param, row, col, cell-val)
void noApply(board, param)
INextMoveStrategy
Min-Max Abstraction
MinMax
accFac:IAccFactory
AAccum makeAcc(player)
Point getNextMove(model, player)
minMaxEval:IBoardLambda
boolean apply(…)
void noApply(…)
AAccum acc = accFac.makeAcc(player);
model.getBoardModel().map(minMaxEval, acc);
return acc.getMove();
private IBoardLambda evalLambda = new IBoardLambda() {
Try a test move.
public boolean apply(board, acc, row, col, cell-value) {
 to be mapped over
IUndoMove undo
= board.makeMove(
the available
states.row, col, acc.getPlayer(), validMvVstr,
Called by map on{
new IBoardStatusVisitor()
each valid (row, col)
Update
player0WonCase(...) {…}
accumulator
What to do in
each
situation {…}
player1WonCase(…)
drawCase(…) {…}
Declarative
programming!
noWinnerCase(…) {
AAccumulator nextAcc = acc.makeOpposite();
Undoboard.map(nextAcc.getPlayer(),
the move.
evalLambda, nextAcc);
acc.updateBest(row, col, nextAcc.getVal()); return null; } });
undo.apply(validUndo);
return acc.isNotDone();
}
Stop mapping?
Just a new accumulator!
Alpha-Beta Pruning
Override the creation of the
next level’s
accumulator
Accumulator
for
the
public class Alpha extends MaxAcc {
opposite player as an
inner {class.
public AAccumulatoranonymous
makeOpposite()
Stop mapping if pruning
Closure
gives
the
condition
is
met.
return new Beta(modelPlayer) {
scoping we need!
public boolean isNotDone() {
return Alpha.this.getVal() < Beta.this.getVal();
}
};
}
}
Abstraction isolates the essence
and provides extensibility
Player Management
Abstract players
Event-loop for turn-taking
Call-back techniques for
asynchronous processing
Design 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
Concepts in Action
Abstract functions – lambda’s
 Higher order functions – Mapping
 Declarative programming
 Invariant: Min-Max Principle
 Variant:

– Full depth-first search
– Alpha-beta pruning
More Information…

Design Pattern for Games:
http://www.exciton.cs.rice.edu/research/SIGCSE02

Patterns for Decoupling Data Structures and
Algorithms:
http://www.exciton.cs.rice.edu/research/SIGCSE02