Design Patterns for Games Stephen Wong Dung Nguyen

























- Slides: 25
Design Patterns for Games Stephen Wong Dung Nguyen Rice University
Let’s Play a Game v v Sun’s Tic-Tac-Toe What can we learn from this? – Arrays – For-loops – Nested Conditionals v What aren’t we learning? – Delineation of concepts from implementation – Abstraction – Design
Something Different… v It’s not really about Tic. Tac. Toe… – It’s a vehicle to teach something BIGGER. Abstraction v Design Process v Fundamental Principles v
What’s in a Game? Model X Rules Strategies Players adapter View adapter Buttons, etc. Display outputs O Controller O Install adapters X
Game Model v Rules of the Game – Board configurations – Legal moves v Next Move Strategies Abstract these components! – Random – Min-Max – Etc. v Player Management Decouple these components! – Turn taking – Win/lose/draw notification – Fault handling
Game Model Classes Game. Model {Game Management} • Game initialization • Win/lose/draw notification IBoard. Model {Rules of the Game} • Board configurations • Legal moves Abstracted & Decoupled! Turn. Control {Player Management} INext. Move. Strategy 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 IBoard. Model execute(IBoard. Status. Visitor v, …) Tic. Tac. Toe. Board state. execute(v, …) state ABoard. State v. no. Winner. Case(…) ATerminal. State Player 0 Won v. player 0 Won. Case(…) Player 1 Won Non. Terminal. State Draw. Game v. player 1 Won. Case(…) v. draw. Case(…)
The Rules of the Game IBoard. Model IUndo. Move make. Move(player, row, col), chk. Move. Cmd), bd. Status. Vstr) Object execute(bd. Status. Vstr, param) // other methods commands host/visitor ICheck. Move. Cmd valid. Move. Case() invalid. Move. Case() IUndo. Move apply(…) IBoard. Status. Visitor player 0 Won. Case(…) player 1 Won. Case(…) draw. Case(…) no. Winner. Case(…)
Playing the Game Computer. Player INext. Move. Strategy take. Turn(…) Point get. Next. Move(…) Random Min. Max Alpha. Beta The next move process is decoupled from the rules of the game!
Facade APlayer take. Turn(…) IRequestor IBoard. Model make. Move(…) Turn. Control IView
What the player sees: public interface IRequestor { public abstract void set. Token. At( int row, int col, int player, IReject. Command reject. Command); } public interface IReject. Command { public abstract void execute(); }
Player Factories IView Only displays the to. String() of the factories. The factories are treated as Objects! Selected APlayer factories Set of APlayer factories Game. Model private interface IMake. Player { public APlayer create(int player. No); }
The Set of APlayer Factories public Vector get. Players() { Vector v = new Vector(); Anonymous APlayer factory Factory method v. add. Element(new IMake. Player() { public APlayer create(int player. No) { return new Human. Player(requestor, player. No, turn. Admin); } public String to. String() { 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 v Math/FP: Map( , S) = { (x) | x S} Both accumulators are abstractly equivalent! v Express our algorithm in terms of mapping, not iteration: – min(…) map( , min-accum) – max(…) map( , max-accum) v Backtracking is automatically handled by mapping.
Abstract Accumulators Intelligent component AAccum void update. Best(row, col, value) Point get. Move() AAccum make. Opposite() Min. Acc Max. Acc IAcc. Factory AAccum make. Acc(player) Opposite is Min. Acc
Mapping Abstraction IBoard. Model IUndo. Move make. Move(player, row, col), chk. Move. Cmd), bd. Status. Vstr) Object execute(bd. Status. Vstr, param) void map(player, lambda, param) Controls Called on all valid continuation of Called when there moves. mapping are no valid moves. command IBoard. Lambda boolean apply(board, param, row, col, cell-val) void no. Apply(board, param)
INext. Move. Strategy Min-Max Abstraction Min. Max acc. Fac: IAcc. Factory AAccum make. Acc(player) Point get. Next. Move(model, player) min. Max. Eval: IBoard. Lambda boolean apply(…) void no. Apply(…) AAccum acc = acc. Fac. make. Acc(player); model. get. Board. Model(). map(min. Max. Eval, acc); return acc. get. Move();
private IBoard. Lambda eval. Lambda = new IBoard. Lambda() { sur Cl o Try a test move. public boolean apply(board, acc, row, col, cell-value) { to be mapped over IUndo. Move undo = board. make. Move( the available states. row, col, acc. get. Player(), valid. Mv. Vstr, Called by map on new IBoard. Status. Visitor() { each valid (row, col) Update player 0 Won. Case(. . . ) {…} accumulator What to do in e each situation {…} player 1 Won. Case(…) draw. Case(…) {…} Declarative programming! no. Winner. Case(…) { AAccumulator next. Acc = acc. make. Opposite(); Undoboard. map(next. Acc. get. Player(), the move. eval. Lambda, next. Acc); acc. update. Best(row, col, next. Acc. get. Val()); return null; } }); undo. apply(valid. Undo); return acc. is. Not. Done(); } 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 Max. Acc { opposite player as an inner {class. public AAccumulatoranonymous make. Opposite() Stop mapping if pruning Closure gives the condition is met. return new Beta(model. Player) { scoping we need! public boolean is. Not. Done() { return Alpha. this. get. Val() < Beta. this. get. Val(); } } 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 v Commands and Visitors isolate rules from behaviors v State pattern models game behavior v Calculating the next move is a Strategy v Design patterns express abstractions
Concepts in Action Abstract functions – lambda’s v Higher order functions – Mapping v Declarative programming v Invariant: Min-Max Principle v Variant: v – Full depth-first search – Alpha-beta pruning
More Information… v Design Pattern for Games: http: //www. exciton. cs. rice. edu/research/SIGCSE 02 v Patterns for Decoupling Data Structures and Algorithms: http: //www. exciton. cs. rice. edu/research/SIGCSE 02