Download presentation
Presentation is loading. Please wait.
1
Design Patterns for Games
Stephen Wong Dung Nguyen Rice University
2
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
3
Something Different… It’s not really about TicTacToe… Abstraction
It’s a vehicle to teach something BIGGER. Abstraction Design Process Fundamental Principles Forces thinking about scalability x3 is too small Design process vs. design solutions
4
X O What’s in a Game? Model View Controller Rules Buttons, etc.
adapter Rules Buttons, etc. Strategies adapter Display outputs Players Install adapters Controller
5
Game Model Abstract these components! Decouple 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!
6
The Rules of the Game IBoardModel ICheckMoveCmd IBoardStatusVisitor
IUndoMove makeMove(player, row, col) , chkMoveCmd) , bdStatusVstr) Object execute(bdStatusVstr, param) // other methods commands host/visitor ICheckMoveCmd Need manipulative for callback Board knows the rules but not how the game is played. validMoveCase() IBoardStatusVisitor invalidMoveCase() player0WonCase(…) player1WonCase(…) IUndoMove apply(…) drawCase(…) noWinnerCase(…)
7
State Diagram Invalid Move State Valid Move State Non-Terminal State
(no winner yet) Terminal States Player #0 Wins Player #1 Wins Draw Game
8
State Design Pattern IBoardModel TicTacToeBoard ABoardState
execute(IBoardStatusVisitor v, …) state.execute(v, …) TicTacToeBoard ABoardState state v.noWinnerCase(…) ATerminalState NonTerminalState Player0Won Player1Won DrawGame v.player0WonCase(…) v.player1WonCase(…) v.drawCase(…)
9
The next move process is decoupled from the rules of the game!
Playing the Game ComputerPlayer INextMoveStrategy Point getNextMove(…) takeTurn(…) Random MinMax AlphaBeta Next move process is decoupled from rules of the game! The next move process is decoupled from the rules of the game!
10
Facade APlayer IRequestor IBoardModel TurnControl IView takeTurn(…)
makeMove(…) TurnControl IView
11
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();
12
Player Factories IView GameModel Only displays the toString()
of the factories. The factories are treated as Objects! IView Set of APlayer factories Selected APlayer factories GameModel private interface IMakePlayer { public APlayer create(int playerNo); }
13
The 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!
14
Application of a process over a set!
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 -1, if s is a losing state for that player For non-terminal state 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. Application of a process over a set! Point out that the principle is invariant, but how we compute the max and min is a variant: full depth-first search or alpha-beta pruning. By decoupling the variant from the invariant, we can re-use the invariant (the code for the principle - the framework) and add new variants (for example, alpha-beta pruning) without changing any of the existing code. That's what extensibility, re-usability, etc. is all about! Start dropping hints about alpha-beta pruning now!
15
Mapping 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! By varying the behaviors of the accumulators, we have different ways of computing the max and min. Alpha-Beta pruning is simply one of the concrete variants. Backtracking is automatically handled by mapping.
16
Mapping Abstraction IBoardModel IBoardLambda
IUndoMove makeMove(player, row, col) , chkMoveCmd) , bdStatusVstr) 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. Need manipulative for callback IBoardLambda boolean apply(board, param, row, col, cell-val) void noApply(board, param)
17
minMaxEval:IBoardLambda
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();
18
to be mapped over the available states.
private 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?
19
Alpha-Beta Pruning Just a new accumulator!
Override the creation of the next level’s accumulator public class AlphaAcc extends MaxAcc { public AAccumulator makeOpposite() { return new BetaAcc() { { this.modelPlayer = AlphaAcc.this.modelPlayer; } public boolean isNotDone() { return AlphaAcc.this.getVal() < 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
20
Player Management Abstract players Event-loop for turn-taking
Call-back techniques for asynchronous processing Game doesn’t treat computer vs. human player differently abstract player Just say “too much to talk about” but do not put it on the slide.
21
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 This is not a beginning project! Design patterns express abstractions
22
It’s more than just a game!
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 It’s more than just a game!
23
More Information… Nguyen and Wong, “Design Pattern for Games” (OOPSLA 2002) Nguyen and Wong, “Patterns for Decoupling Data Structures and Algorithms” (SIGCSE 1999)
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.