180 likes | 590 Views
Design Patterns for Games Stephen Wong Dung Nguyen Rice University Let’s Play a Game What can we learn from this (Sun’s)? Arrays, for-loops, nested conditionals What aren’t we learning? Delineation of concepts from implementation Abstraction, design Something Different
E N D
Design Patterns for Games Stephen Wong Dung Nguyen Rice University
Let’s Play a Game • What can we learn from this (Sun’s)? • Arrays, for-loops, nested conditionals • What aren’t we learning? • Delineation of concepts from implementation • Abstraction, design Something Different It’s a vehicle to teach something BIGGER. It’s not really about TicTacToe…
adapter adapter Install adapters What’s in a Game? Model View Rules Buttons, etc. Strategies Display outputs Players Controller
GameModel • {Game Management} • Game initialization • Win/lose/draw notification TurnControl {Player Management} APlayer {Turn Taker} • IBoardModel • {Rules of the Game} • Board configurations • Legal moves Game Model Abstracted & Decoupled! • INextMoveStrategy • {Next Move Computation} • Random • Min-max/Alpha-beta • Limited depth • Custom
States of the Game Invalid Move State Valid Move State Non-Terminal State (no winner yet) Terminal States Player #0 Wins Player #1 Wins Draw Game
IBoardStatusVisitor IBoardModel player0WonCase(…) execute(IBoardStatusVisitor v, …) player1WonCase(…) drawCase(…) noWinnerCase(…) state TicTacToeBoard ABoardState ATerminalState NonTerminalState Player0Won Player1Won DrawGame State+Visitor Design Pattern state.execute(v, …) v.noWinnerCase(…) v.player0WonCase(…) v.player1WonCase(…) v.drawCase(…)
IUndoMove apply(…) The Rules of the Game IBoardModel • IBoardModel • {Rules of the Game} • Board configurations • Legal moves , chkMoveCmd) , bdStatusVstr) makeMove(player, row, col) IUndoMove Object execute(bdStatusVstr, param) // other methods commands host/visitor ICheckMoveCmd validMoveCase() IBoardStatusVisitor invalidMoveCase() player0WonCase(…) player1WonCase(…) drawCase(…) noWinnerCase(…)
ComputerPlayer INextMoveStrategy Point getNextMove(…) Random MinMax AlphaBeta APlayer {Turn Taker} Playing the Game • INextMoveStrategy • {Next Move Computation} • Random • Min-max/Alpha-beta • Limited depth • Custom takeTurn(…) The next move process is decoupled from the rules of the game!
Min-Max Principle • V(s) = • For terminal state s • +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 s • 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 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!
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! V(s) • Backtracking is automatically handled by mapping.
IAccFactory AAccum void updateBest(row, col, value) Point getMove() AAccum makeOpposite() AAccum makeAcc(player) MinAcc MaxAcc Intelligent component Abstract Accumulators Opposite is MinAcc Accumulation is decoupled from Min-Max evaluation!
Mapping Abstraction IBoardModel , chkMoveCmd) , bdStatusVstr) makeMove(player, row, col) 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)
INextMoveStrategy Min-Max Abstraction MinMax accFac:IAccFactory AAccum makeAcc(player) Point getNextMove(model, player) evalLambda:IBoardLambda boolean apply(…) void noApply(…) AAccum acc = accFac.makeAcc(player); model.getBoardModel().map(player, evalLambda, acc); return acc.getMove();
Update accumulator • private IBoardLambda evalLambda = new IBoardLambda() { • public boolean apply(board, acc, row, col, cell-value) { • IUndoMove undo =board.makeMove(row, col, acc.getPlayer(), validMvVstr, • new IBoardStatusVisitor() { • player0WonCase(...) {…} • player1WonCase(…) {…} • drawCase(…) {…} • noWinnerCase(…) { • undo.apply(validUndo); • return acc.isNotDone(); • } Closure Try a test move. to be mapped over the available states. Called by map on each valid (row, col) What to do in each situation Declarative programming! AAccumulator nextAcc = acc.makeOpposite(); board.map(nextAcc.getPlayer(), evalLambda, nextAcc); acc.updateBest(row, col, nextAcc.getVal()); return null; } }); Undo the move. Stop mapping?
Just a new accumulator! Alpha-Beta Pruning Override the creation of the next level’s accumulator public class Alpha extends MaxAcc { public AAccumulator makeOpposite() { return new Beta(modelPlayer) { public booleanisNotDone(){return Alpha.this.getVal() < Beta.this.getVal();} }; }} Accumulator for the opposite player as an anonymous inner class. Stop mapping if pruning condition is met. Closure gives the scoping we need! Abstraction isolates the essence and provides extensibility
Design Patterns In Action • MVC separates model from view • Command and Visitor patterns decouple intrinsic and extrinsic behaviors. • State pattern models game behavior • Strategy to calculate the next move • Decorator, Façade, Adapter, Singleton, Factory – no time here! Design patterns express abstractions
Concepts in Action • Abstract functions – lambda, closure • Higher order functions – Mapping • Declarative programming • Invariant vs. Variant Decomposition • Abstract Board vs. Tic-Tac-Toe, Othello • Min-Max vs. Alpha-Beta, Depth-limited, etc. It’s more than just a game!
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/SIGCSE99