BoardGame: A MiniDraw extension Henrik Brbak Christensen Status: - - PDF document

boardgame a minidraw extension
SMART_READER_LITE
LIVE PREVIEW

BoardGame: A MiniDraw extension Henrik Brbak Christensen Status: - - PDF document

BoardGame: A MiniDraw extension Henrik Brbak Christensen Status: Draft. November 29, 2010 Chapter 1 Boardgame Architecture The MiniDraw framework is described in Chapter 30 in Flexible, Reliable Software . The Boardgame extension is a set of


slide-1
SLIDE 1

BoardGame: A MiniDraw extension

Henrik Bærbak Christensen Status: Draft. November 29, 2010

slide-2
SLIDE 2

Chapter

1

Boardgame Architecture

The MiniDraw framework is described in Chapter 30 in Flexible, Reliable Software. The Boardgame extension is a set of classes defined in the minidraw.boardgame package and defines an augmented framework in itself for the more specialized domain of supporting graphical user interfaces for board games. It does so by providing im- plementations of some of MiniDraw’s hotspots (essentially turning these into frozen spots) while providing new hotspots to be defined by developers of board games. Boardgame assumes a well-defined decoupling between the game itself (called the domain) and the graphical user interface (the GUI)—which of course is a classic ar-

  • chitecture. Boardgame provides hotspots to support the two flows of information

and control:

  • From GUI to game domain: That is, when a user manipulate some graphical ob-

ject, this action is converted into an action in the domain. The archetypical example is the user dragging a token from one square of the board game board to another which must be translated into a call to a move() sor similar method in the game domain.

  • From game domain to GUI: That is, when the state changes in board game, then

the GUI must be updated to reflect this. For instance if a chess piece hits an

  • pponent piece, then the opponent piece must disappear—or move to a place
  • utside the chess board.

Boardgame provides a number of hotspots to support this flow more directly than does MiniDraw. [DISCUSSION PENDING]

1.1 Notes on the Process

Note that the focus here is on the GUI and no domain code exists beforehand. 2

slide-3
SLIDE 3

Chapter

2

Snakes and Ladders—by TDD

The snakes and ladders game is a very old childrens’ game. The game’s logic is extremely simple: roll a die and move forward accoring to the die value, if you hit a square with a ladder you will move (forward) to the square at the end of the ladder and if you hit a snake you will move (backward) to the square at the end of the snake. The first player to reach the end square wins. From the extension package boardgame’s perspective it is ideal because it requires all the behaviour supported by the framework:

  • Generic type LOCATION: The squares on which the players’ tokens rest.
  • BoardFigure: Moveable objects with a graphical appearance (representing the

tokens of the players) that are moved by the user using the mouse.

  • Props: Static objects with an appearance that can be clicked by the user (the die,

clicking telling it to roll)

  • PositioningStrategy: Once a tokens has been put on a square we would like to

adjust its position to appear nice. To start the project I devise a short test list ✽ Show the board and a die ✽ Make tokens and die into BoardFigures ✽ Move a token invokes the game’s move method

2.1 Iteration 1: Show Board and Die

A Ant build script is written and a standard folder setup is created like that in FRS chapter 6. A simple snakes and ladder board is found on wikipedia and its size is increased a bit to make room for a die. Together with die images it is copied to the resource folder. 3

slide-4
SLIDE 4

4 ❚ Snakes and Ladders—by TDD In package snakesandladders.visual I create a simple test program, just to load the board background and show a single die (and target show). This is basically just a copy of a similar “show graphics” test program from the HotGammon project from

  • FRS. Note that it only uses the standard MiniDraw API.

package snakesladders . visual ; import minidraw . standard . ∗ ; import minidraw . framework . ∗ ; import java . awt . ∗ ; import javax . swing . ∗ ; /∗ ∗ Show v i s u a l appearance

  • f

game . ∗ <# i f type == " code "> <# i n c l u d e "/ data / author . t x t "> </# i f > ∗/ public class ShowLayout { public s t a t i c void main ( String [ ] args ) { DrawingEditor editor = new MiniDrawApplication ( "Show Layout . . . " , new SnakesAndLaddersFactory ( ) ) ; editor . open ( ) ; Figure die = new ImageFigure ( " die4 " , new Point (690 , 4 0 ) ) ; editor . drawing ( ) . add ( die ) ; editor . setTool ( new SelectionTool ( editor ) ) ; } } class SnakesAndLaddersFactory implements Factory { public DrawingView createDrawingView ( DrawingEditor editor ) { DrawingView view = new StdViewWithBackground ( editor , " snakes−and−ladders−background " ) ; return view ; } public Drawing createDrawing ( DrawingEditor editor ) { return new StandardDrawing ( ) ; } public JTextField createStatusField ( DrawingEditor editor ) { JTextField statusField = new JTextField ( " Hello Snakes . . . " ) ; statusField . setEditable ( false ) ; return statusField ; } }

The resulting graphics is shown in Figure 2.1.

slide-5
SLIDE 5

Iteration 2: Making BoardFigures ❚ 5 Figure 2.1: Visual test case of iteration 1.

2.2 Iteration 2: Making BoardFigures

Next I want to make a One Step Test to start building up the boardgame extension

  • stuff. The first one is getting boardgame to understand the three BoardFigures that

must be on the GUI. This involves creating BoardFigures using the FigureFactory. I start by making a new test case program “ShowFigures” as a copy of iteration 1 program. Unfortunately, the next step is really a several step process: 1) I have to create a Fig- ureFactory which 2) require generic type LOCATION and 3) configure MiniDraw with a BoardDrawing instance instead of just a StandardDrawing (see method cre- ateDrawing in the previous listing). Furthermore, step 3’s BoardDrawing requires a PositioningStrategy which is then step 4! To get to this point I have to Fake It a lot. It appears that point 3) is actually the proper first step, so I need to fake both LOCA- TION and FigureFactory. This leads to the following code change (TDD rhythm step 1):

public Drawing createDrawing ( DrawingEditor editor ) { return new BoardDrawing<Square >(new SnakeLadderPieceFactory (game) , new SnakeLadderPositioningStrategy ( ) , null /∗ wait with the d i e prop ∗/ ) ; }

which of course does not compile at all! I hurry up to define some fake it implemen- tations as local classes in the same file.

slide-6
SLIDE 6

6 ❚ Snakes and Ladders—by TDD

class Square { } class SnakeLadderPositioningStrategy implements PositioningStrategy <Square> { public Point calculateFigureCoordinatesIndexedForLocation ( Square location , int index ) { return new Point ( 8 0 , 3 0 0 ) ; } public Point calculateFigureCoordinatesForProps ( String keyOfProp ) { return null ; } } class SnakeLadderPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { return null ; } public Map<String , BoardFigure > generatePropMap ( ) { return null ; } }

Now it at least compiles but I get an exception from boardgame extension at run- time: “ BoardGame contract violation: buildPieceMap assumes a non-null map is return from the FigureFactory.” So to get at Step 3 I add the images of two tokens, one for blue and one for red. I start by adding just one token, as I then do not have to add any implementation of Square. The graphics I found on the net and it is licensed under the Free Art License.

class SnakeLadderPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { Map<Square , List <BoardFigure >> m = new HashMap<Square , List <BoardFigure > >(); Square square1 = new Square ( ) ; BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , null ) ; L is t s qu ar e 1l i s t = new ArrayList ( ) ; s qu ar e 1l i s t . add ( redtoken ) ;

  • m. put (

square1 , s qu a r e 1l i s t ) ; return m; } public Map<String , BoardFigure > generatePropMap ( ) { return null ; } }

I get to step 4 which looks like in Figure2.2.

☞ Why is the red token positioned there?

At this stage I still lack to add the blue token and the die. The die is really a “prop” that is an unmoveable object so I postpone that into a new test on the test list.

slide-7
SLIDE 7

Iteration 2: Making BoardFigures ❚ 7 Figure 2.2: Iteration 2. ✽ Show the board and a die ✽ Make tokens into BoardFigures ✽ Make die into a Prop BoardFigure ✽ Move a token invokes the game’s move method So I need to add the blue token as well. As it is also located on square 1, this is easy.

class SnakeLadderPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { Map<Square , List <BoardFigure >> m = new HashMap<Square , List <BoardFigure > >(); Square square1 = new Square ( ) ; BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , null ) ; BoardFigure bluetoken = new BoardFigure ( "game−token−blue " , true , null ) ; L is t s qu ar e 1l i s t = new ArrayList ( ) ; s qu ar e 1l i s t . add ( redtoken ) ; s qu ar e 1l i s t . add ( bluetoken ) ;

  • m. put (

square1 , s qu a r e 1l i s t ) ; return m; } public Map<String , BoardFigure > generatePropMap ( ) { return null ; } }

slide-8
SLIDE 8

8 ❚ Snakes and Ladders—by TDD Visually, the only change I see is that the visible token is blue now as it fully covers the red token. If you solved the reflection exercise above you know why: it is because the PositioningStrategy always returns graphical position (80,300). Thus I once again extend the test list. ✽ Show the board and a die ✽ Make tokens into BoardFigures ✽ Make die into a Prop BoardFigure ✽ Move a token invokes the game’s move method ✽ Arrange tokens on the correct square ✽ Arrange non-overlapping if on the same square End of iteration.

2.3 Iteration 3: Tokens Appear On Correct Square

This again involves multiple steps: 1) implement a PositioningStrategy that works correctly given the square a token is on 2) implement some domain code so tokens can actually be moved around. The latter point is a consequence of the way I have choosen to structure my process here: normally I use TDD to develop a quality domain implementation first and then fit a GUI afterwards. Here, however, I have choosen to go the other way, but I still have to provide some stub behaviour of the domain code. I realize that I can add these modifications with the context of the previous test case program: no need to define new java files! The changes are minimal but require a tiny bit of domain design: I have to have a way to distinguish one square from the other. I decide on the obvious choice inspired by the numbers on the game board. I add a method int index() to Square. Furthermore I fake this method as well as add fake-it code to the PositioningStrategy: only square 1 is possible and recognized.

class Square { public int index ( ) { return 1; } } class SnakeLadderPositioningStrategy implements PositioningStrategy <Square> { public Point calculateFigureCoordinatesIndexedForLocation ( Square location , int index ) { i f ( location . index ( ) == 1) { return new Point ( 2 0 , 4 0 0 ) ; } return new Point ( 8 0 , 3 0 0 ) ; } public Point calculateFigureCoordinatesForProps ( String keyOfProp ) { return null ; } }

slide-9
SLIDE 9

Iteration 3: Tokens Appear On Correct Square ❚ 9 I am lucky, the graphical position (20,400) is actually quite good for square 1 on the board. The question is how to drive the proper algorithm into place for squares other than 1? I realize that I can come some of the way by putting red and blue token on different squares in their configuration in the figure factory:

class SnakeLadderPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { Map<Square , List <BoardFigure >> m = new HashMap<Square , List <BoardFigure > >(); Square square1 = new Square ( ) ; BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , null ) ; L is t s qu ar e 1l i s t = new ArrayList ( ) ; s qu ar e 1l i s t . add ( redtoken ) ;

  • m. put (

square1 , s qu a r e 1l i s t ) ; Square square20 = new Square ( ) ; BoardFigure bluetoken = new BoardFigure ( "game−token−blue " , true , null ) ; L is t square20list = new ArrayList ( ) ; square20list . add ( bluetoken ) ;

  • m. put (

square20 , square20list ) ; return m; } public Map<String , BoardFigure > generatePropMap ( ) { return null ; } }

Note that this is still fake-it code but serves to triangulate a better PositioningStrategy into existence. Step 2: It compiles but blue token is still not on square 20, as all squares return index

  • 1. I have to add the understanding of index into class Square.

class Square { private int index ; public Square ( int index ) { this . index = index ; } public int index ( ) { return index ; } }

and update the constructor calls in the SnakeLadderPieceFactory. Now I compile and run - and the two tokens finally appear in different places like seen in Figure 2.3. Finally, I triangulate a positioning strategy in a number of edit-compile-run cycles. (I was pretty confused by wrong calculations until I discovered that the board is actu- ally “jumping a square” after square 17!)

class SnakeLadderPositioningStrategy implements PositioningStrategy <Square> { public Point calculateFigureCoordinatesIndexedForLocation ( Square location , int index ) { int sqindex = location . index ( ) ; / / Note the t r i c k y board layout − what i s square 18 i s v i s u a l l y

slide-10
SLIDE 10

10 ❚ Snakes and Ladders—by TDD Figure 2.3: Iteration 3a.

/ / square 1 9 ! ! ! i f ( sqindex > 17) { sqindex ++;} / / c a l c u l a t e the row and column int row = ( sqindex −1) / 7; int column = ( sqindex −1) % 7; / / System . out . p r i n t l n ( " r , c = "+row+" ,"+ column ) ; return new Point (20+column∗92,400−row ∗ 92 ) ; } public Point calculateFigureCoordinatesForProps ( String keyOfProp ) { return null ; } }

The result appears quite ok for the moment, see Figure 2.4. ✽ Show the board and a die ✽ Make tokens into BoardFigures ✽ Make die into a Prop BoardFigure ✽ Move a token invokes the game’s move method ✽ Arrange tokens on the correct square ✽ Arrange non-overlapping if on the same square

slide-11
SLIDE 11

Iteration 4: Move a Token ❚ 11 Figure 2.4: Iteration 3 at the end.

2.4 Iteration 4: Move a Token

I would like to see things move! I want to move the tokens. This entails activating the proper tool of boardgame, namely BoardActionTool. To ensure that it will not interfere with the present test cases I create a new test case program: MoveToken. I create this as a copy of the ShowFigures class just developed. The compiler now complains highly that there are duplicate classes. Thus I have to refactor the class structure and put all the local classes I developed into separate java files. Thus I put this iteration on hold—and do iteration 3’s step 5: refactoring.

2.5 Iteration 3: . . . Refactored

This is a simple exercise in slicing up the java source file and put the local classes into separate java source files and in the proper folders. I create two new folders in the src folder: snakesladders.domain and snakeslad- ders.view and put Square in the first and put classes SnakesAndLaddersFactory, SnakesAndLaddersPieceFactory, and SnakesAndLaddersPositioningStrategy in the latter. I refactor ShowFigures to use these classes instead of defining them locally. When it runs the refactoring step is complete.

slide-12
SLIDE 12

12 ❚ Snakes and Ladders—by TDD

2.6 Iteration 4: Move a Token—Continued

Back to MoveToken. Step 1: I tell MiniDraw to use the frozen spot tool supplied by boardgame, namely BoardActionTool.

public class MoveToken { public s t a t i c void main ( String [ ] args ) { DrawingEditor editor = new MiniDrawApplication ( "Move a token . . . " , new SnakesAndLaddersFactory ( ) ) ; editor . open ( ) ; editor . setTool ( new BoardActionTool ( editor ) ) ; } }

Step 2—it runs and I can move a token, but.. . When I release it I get a null pointer

  • exception. This is because I did not define any COMMMAND pattern object to be

associated with the token figures:

BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , null ) ;

We need to define some tool. First, let us just get rid of the exception:

BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , new NullCommand ( ) ) ;

Great—I can move tokens and there is no exception. But—of course nothing hap- pens in the game domain. The command object is the one responsible for telling the domain code that some action happened: here that a token has moved. As always I fake-it and triangulate, so my first implementation of a MoveCommand simply writes something on the console.

public MoveCommand implements Command { public boolean execute ( ) { System . out . println ( "Moving from ( "+fx+" , "+fy+" ) to ( "+ tx+" , "+ty+" ) " ) ; return valid ; } private int fx , fy , tx , ty ; public void setFromCoordinates ( int fromX , int fromY ) { fx = fromX ; fy = fromY ; } public void setToCoordinates ( int toX , int toY ) { tx = toX ; ty = toY ; } }

No thing happens??? Ahh, I forgot to associate command objects with the tokens in the factory. I realize that I only need one command object for both tokens.

slide-13
SLIDE 13

Iteration 5: Move a Token—Continued ❚ 13

public class SnakesAndLaddersPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { Map<Square , List <BoardFigure >> m = new HashMap<Square , List <BoardFigure > >(); Square square1 = new Square ( 1 ) ; BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , new MoveCommand ( ) ) ; L is t s qu ar e 1l i s t = new ArrayList ( ) ; s qu ar e 1l i s t . add ( redtoken ) ;

  • m. put (

square1 , s qu a r e 1l i s t ) ; Square square20 = new Square ( 2 0 ) ; BoardFigure bluetoken = new BoardFigure ( "game−token−blue " , true , new MoveCommand ( ) ) ; L is t square20list = new ArrayList ( ) ; square20list . add ( bluetoken ) ;

  • m. put (

square20 , square20list ) ; return m; } public Map<String , BoardFigure > generatePropMap ( ) { return null ; } }

It works, every time I move a token I get output written on the console: move: [java] Moving from (605,253) to (431,427) [java] Moving from (429,429) to (337,245) [java] Moving from (338,243) to (431,342) [java] Moving from (431,341) to (535,418) [java] Moving from (534,419) to (607,331) [java] Moving from (607,336) to (623,438) [java] Moving from (623,437) to (166,150) [java] Moving from (164,150) to (59,249) So this iteration ends in success—I can move tokens—but also in the realization that it is not quite enough. I have to convert the graphical coordinates into the square indices in order to call a move(from,to) method in the game domain. ✽ Show the board and a die ✽ Make tokens into BoardFigures ✽ Make die into a Prop BoardFigure ✽ Move a token ✽ Move a token invokes the game’s move method ✽ Arrange tokens on the correct square ✽ Arrange non-overlapping if on the same square

2.7 Iteration 5: Move a Token—Continued

Several One Step Tests are possible at this point in time. As the focus is on the boardgame abstractions I will pick the item that shows the special handling of props: Make die into

slide-14
SLIDE 14

14 ❚ Snakes and Ladders—by TDD a Prop BoardFigure. I will do this in terms of the ShowFigures test case as props are also figures and thus the test case name is a cohesive name for what it does. Basically, the die is just a BoardFigure that cannot be moved, which is indicated by a boolean value in the constructor.

public class SnakesAndLaddersPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { [ . . . ] } public Map<String , BoardFigure > generatePropMap ( ) { BoardFigure die = new BoardFigure ( " die0 " , false , new NullCommand ( ) ) ; Map<String , BoardFigure > m = new HashMap<String , BoardFigure > ( ) ;

  • m. put ( " die " , die ) ;

return m; } }

When run I get figures: [java] Exception in thread "main" java.lang.RuntimeException: BoardDrawing:PositionStrategy returns null for Prop with key die ... (This requires MiniDraw version 1.4 or later, earlier versions just throw a null pointer exception) Alas, I need to define the position strategy for the die as well.

public class SnakesAndLaddersPositioningStrategy implements PositioningStrategy <Square> { public Point calculateFigureCoordinatesIndexedForLocation ( Square location , int index ) { [ . . . ] } public Point calculateFigureCoordinatesForProps ( String keyOfProp ) { i f ( keyOfProp . equals ( " die " ) ) { return new Point (690 , 4 0 ) ; } return null ; } }

And I get to Step 4: Run all tests and see them all succeed. In Step 5: Refactor to remove duplication I remove the ugly “magic constant” of string value die by defining the constant instead in a introduced class Constant.

package snakesladders . view ; public class Constant { public s t a t i c final String diePropName=" die " ; }

slide-15
SLIDE 15

Iteration 5: Move a Token—Continued ❚ 15 One final “test case” remains: Can I move the die? I run move target that runs the MoveToken test case class and verify that I can still move tokens, but the die remains impossible to move. I can strike out a test case on the test list but quite a few new test items appear on my mind because when I move the tokens they are not “neatly” arranged on the squares as you can see in Figure ??. Also we must create the coupling from the game domain state changes to the GUI. ✽ Show the board and a die ✽ Make tokens into BoardFigures ✽ Make die into a Prop BoardFigure ✽ Move a token ✽ Move a token invokes the game’s move method ✽ Arrange tokens on the correct square ✽ Arrange non-overlapping if on the same square ✽ Position tokens neatly on the squares ✽ Update die prop when domain die is rolled ✽ Update token when domain token is moved Figure 2.5: Iteration 5 at the end.

slide-16
SLIDE 16

16 ❚ Snakes and Ladders—by TDD

2.8 Iteration 6: Update Die Prop When Domain Die Rolled

In this iteration the focus is on introducing the coupling from the domain to the GUI. This is (of course) via an OBSERVER pattern. In Boardgame it is known that the do- main is a board game and therefore a special BoardGameObserver class and protocol is provided having two methods: one for tokens/pieces and one for information ob- jects/props. I need a test program that can call a method to roll a die in order to verify that the GUI shows the new die face. I first create a test program, note the use of a very specialized tool just to call the roll die method.

public class ShowDomainUpdate { public s t a t i c void main ( String [ ] args ) { Game game = new GameImpl ( ) ; DrawingEditor editor = new MiniDrawApplication ( "Move a token . . . " , new SnakesAndLaddersFactory ( ) ) ; editor . open ( ) ; editor . setTool ( new DomainUpdateTool (game) ) ; } } class DomainUpdateTool extends NullTool { private Game game ; public DomainUpdateTool (Game game) { this . game = game ; } public void mouseDown( MouseEvent e , int x , int y ) { game . rollDie ( ) ; } }

Of course, this does not work - there is no Game defined. I will just introduce one that seems feasible for a snakes and ladders game, again only focusing at the test item at hand, namely the die and getting the observer protocol running.

public interface Game { public void rollDie ( ) ; public int getdieValue ( ) ; public void addObserver ( BoardGameObserver<Square> observer ) ; }

with a pretty Obvious Implementation:

package snakesladders . domain ; import minidraw . boardgame . BoardGameObserver ; import snakesladders . view . Constant ; public class GameImpl implements Game { private int die = 1; public void rollDie ( ) { die = ( int ) (Math . random ( ) ∗ 6 + 1 ) ;

slide-17
SLIDE 17

Iteration 6: Update Die Prop When Domain Die Rolled ❚ 17

Sidebar 2.1: Polution of Domain Code

PENDING

  • bserver . propChangeEvent ( Constant . diePropName ) ;

} public int getdieValue ( ) { return die ; } private BoardGameObserver<Square> observer ; public void addObserver ( BoardGameObserver<Square> observer ) { this . observer = observer ; } }

Of course, this is also a fake-it implementation as there can only be one Observer at a

  • time. Note that the domain code actually now contains strains of GUI oriented code.

I am not too happy about that, see 2.1. The GUI appears—and pressing the mouse throws a null pointer exception: Step 2: Run all tests and see the new one fail. Ahh - I forgot to register the GUI as observer. This is one of the places where Boardgame is a bit tedious still.

public s t a t i c void main ( String [ ] args ) { Game game = new GameImpl ( ) ; DrawingEditor editor = new MiniDrawApplication ( "Move a token . . . " , new SnakesAndLaddersFactory ( ) ) ; editor . open ( ) ; editor . setTool ( new DomainUpdateTool (game) ) ; BoardDrawing<Square> drawing = ( BoardDrawing<Square >) editor . drawing ( ) ; game . addObserver ( drawing ) ; } }

Better—now a get an exception telling me what is wrong: update: [java] Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: BoardGame contract violation: The PropAppearanceStrategy must be defined if propChangeEvents are posted. The PropAppearanceStrategy is an algorithm that based upon the string valued key

  • f a prop calculates which image name to use for the graphical prop. Alas, if the die

rolled is value two, then we need to show the image named “die2”. Alas I define one:

public class SnakesAndLaddersPropAppearanceStrategy implements PropAppearanceStrategy { private Game game ; public SnakesAndLaddersPropAppearanceStrategy (Game game) { this . game = game ; }

slide-18
SLIDE 18

18 ❚ Snakes and Ladders—by TDD

public String calculateImageNameForPropWithKey ( String key ) { return " die "+game . getDieValue ( ) ; } }

And I also have to tell BoardGame to use it which require changing the Factory:

public class SnakesAndLaddersFactory implements Factory { private Game game ; public SnakesAndLaddersFactory (Game game) { this . game = game ; } public DrawingView createDrawingView ( DrawingEditor editor ) { DrawingView view = new StdViewWithBackground ( editor , " snakes−and−ladders−background " ) ; return view ; } public Drawing createDrawing ( DrawingEditor editor ) { return new BoardDrawing<Square >(new SnakesAndLaddersPieceFactory ( ) , new SnakesAndLaddersPositioningStrategy ( ) , new SnakesAndLaddersPropAppearanceStrategy (game) ) ; }

This required a few refactorings of all the test cases so I run them all, one after the

  • ther, to test that none of them fails.

All works. And also the ShowUpdate program—whenever I click the die changes face to new, random, values. ✽ Show the board and a die ✽ Make tokens into BoardFigures ✽ Make die into a Prop BoardFigure ✽ Move a token ✽ Move a token invokes the game’s move method ✽ Arrange tokens on the correct square ✽ Arrange non-overlapping if on the same square ✽ Position tokens neatly on the squares ✽ Update die prop when domain die is rolled ✽ Update token when domain token is moved

2.9 Iteration 7: Update Token Figure When Do- main Token Is Moved

I can actually easily extend the previous “update” test case program by modifying the specialized tool I developed:

class DomainUpdateTool extends NullTool { private Game game ; public DomainUpdateTool (Game game) { this . game = game ; } int clickCount = 0; public void mouseDown( MouseEvent e , int x , int y ) { switch ( clickCount ) {

slide-19
SLIDE 19

Iteration 7: Update Token Figure When Domain Token Is Moved ❚ 19

/ / move p l a y e r 0 ’ s token from square 1 to 3 case 0: game . move(0 , new Square ( 1 ) , new Square ( 3 ) ) ; break ; case 1: game . move(0 , new Square ( 3 ) , new Square ( 1 8 ) ) ; break ; default : game . rollDie ( ) ; } clickCount ++; } }

Which does not compile as there is no move() method. Again, extreme use of Fake It makes me go fast and keep focus:

public class GameImpl implements Game { [ . . . ] private Square tokenSquare = new Square ( 1 ) ; public boolean move( int player , Square fromSquare , Square toSquare ) { System . out . println ( "Moving to "+toSquare . index ( ) ) ; tokenSquare = toSquare ; return true ; } }

OK, I see output in the console from the print statement but no visual action. I have to get the Observer going.

public class GameImpl implements Game { [ . . . ] private Square tokenSquare = new Square ( 1 ) ; public boolean move( int player , Square fromSquare , Square toSquare ) { System . out . println ( "Moving to "+toSquare . index ( ) ) ; tokenSquare = toSquare ;

  • bserver . pieceMovedEvent ( fromSquare ,

toSquare ) ; return true ; } }

which generates an exception from boardgame that states the following:

update : [ java ] Moving to 3 [ java ] Exception in thread "AWT −EventQueue−0" java . lang . RuntimeException : B

  • ardDrawing invariant

not establised : The FigureFactory has not defined a l i s t

  • f

BoardFigures for location snakesladders . domain . Square@169e11

Looking over the JavaDoc for FigureFactory I see that I have missed an important aspect of the multimap returned: POSTCONDITION: The multimap MUST contain a non-null list for every LOCATION on the board game, even if the list is empty! When I inspect the SnakesAndLaddersPieceFactory I see there is quite a lot of Fake It code around—the proper design would be to read the starting position of the game. I note it one the test list as yet another item Generate tokens based upon domain contents, but for now I will keep faking to keep focus on my iteration goal.

slide-20
SLIDE 20

20 ❚ Snakes and Ladders—by TDD

public class SnakesAndLaddersPieceFactory implements FigureFactory <Square> { public Map<Square , List <BoardFigure >> generatePieceMultiMap ( ) { Map<Square , List <BoardFigure >> m = new HashMap<Square , List <BoardFigure > >(); for ( int i = 0; i < 35; i ++ ) { Square s = new Square ( i ) ; List <BoardFigure > s l = new ArrayList ( ) ;

  • m. put ( s , s l ) ;

} Square square1 = new Square ( 1 ) ; BoardFigure redtoken = new BoardFigure ( "game−token−red " , true , new MoveCommand ( ) ) ; List <BoardFigure > s qu ar e 1l i s t = new ArrayList ( ) ; s qu ar e 1l i s t . add ( redtoken ) ;

  • m. put (

square1 , s qu a r e 1l i s t ) ; Square square20 = new Square ( 2 0 ) ; BoardFigure bluetoken = new BoardFigure ( "game−token−blue " , true , new MoveCommand ( ) ) ; List <BoardFigure > square20list = new ArrayList ( ) ; square20list . add ( bluetoken ) ;

  • m. put (

square20 , square20list ) ; return m; } [ . . . ] }

However, this leads to the same boarddrawing invariant exception as before? The answer lies again in the JavaDoc for FigureFactory’s generatePieceMultiMap method: POSTCONDITON: The LOCATION type must implement methods equals() and possibly hashCode() so lookups can be made in the returned map. After implementing these two methods in class Square, I get to Step 4: Run all tests and see them all succeed: the ShowDomainUpdate class now moves the red token nicely upon the first two mouse clicks, whereafter the die rolls.