46

Learn bdd-playing-dice-book

Embed Size (px)

DESCRIPTION

A ebook to learn BDD, Ruby and Shoes having fun

Citation preview

Page 1: Learn bdd-playing-dice-book

1

www.princexml.com
Prince - Personal Edition
This document was created with Prince, a great way of getting web content onto paper.
Page 2: Learn bdd-playing-dice-book
Page 3: Learn bdd-playing-dice-book

Learn BDD Playing Dice!

YOUR BEHAVIOR IS HAVE FUN

First Edition

Page 4: Learn bdd-playing-dice-book
Page 5: Learn bdd-playing-dice-book

Learn BDD Playing Dice!

YOUR BEHAVIOR IS HAVE FUN

First Edition

by Valério Farias de Carvalho

Page 6: Learn bdd-playing-dice-book

by Valério Farias de Carvalho. This document is licensed under Creative Commons 3.0 Attribution License.

First edition: July 2010

Valério Fariashttp://www.valeriofarias.comTwitter: @valeriofarias

Cover image from http://www.flickr.com/photos/missturner/

Page 7: Learn bdd-playing-dice-book

INTRODUCTION

Hello Ruby enthusiast! Hello BDD enthusiast! Welcome to this journey line by line, test by test, using theBehaviour Driven Development technique in a interesting project: War Dice Simulation!. I wrote this tiny bookto learn BDD and RSpec for myself. I would want to start with a little example. And if was possible with afunny example too. Then I thought: Why not playing dice! Why not playing war dice! Then there you are:Learning BDD playing dice!

The app that we'll create together uses two classes: Dice and WarDice. The first chapter I start to constructthe RSpec file of Dice class and the Dice class simultaneously and step by step until all of the tests becomegreen.

The second chapter I go on with the WarDice class, that is a simulation of each dice of war game!

Finally, the third chapter I use the classes in a funny shoes application.

The philosophy of this book is to learn making funny things. In one word: Experimentation. Then I hope youenjoy this simple but also instructive book.

The tests used in this book aren't a silver bullet. They are only a way between some others. How I told, Imade to learn RSpec. You can send suggests, clone the project, modify it and codify in other ways. Whoknows we'll playing together in the 2.0 version of this book :).

The complete source code of application you can download in https://github.com/valeriofarias/shoes-war-dice/tree

Learn BDD Playing Dice!

7

Page 8: Learn bdd-playing-dice-book

Now, just read, codify, test, share and have fun!

Learn BDD Playing Dice! - Your behavior is have fun

8

Page 9: Learn bdd-playing-dice-book

Chapter 1

Making a Dice class using BDD

GETTING STARTED

Before start to programming let's install the essential packages. First install the rspec:

gem install rspec

If you want more usability, install the package zentest:

gem install ZenTest

Now install the package autotest-notification. This gem set the autotest (ZenTest) to send messages tosoftware as Growl, LibNotify, and Snarl, displaying a window with the results. Read the readme file of theproject to know how to install: http://github.com/carlosbrando/autotest-notification/. With this three gems, ourjourney become funny!

Chapter 1: Making a Dice class using BDD

9

Page 10: Learn bdd-playing-dice-book

To complete the bag, go to the shoes application page and read how to install in your operating system. Yes,shoes is very fun! We'll end our play with it. Access http://github.com/shoes/shoes/downloads orhttp://github.com/shoes/shoes. To learn how to use and execute shoes read the ebook Nobody knows Shoesavailable in http://github.com/shoes/shoes/downloads.

Now let's start our travel test by test in the BDD world!

CREATING THE RSPEC FILE

First create the dice folder and the lib and spec folders inside it. Then you have to create the file dice_spec.rbinside the spec folder. Well! What you waiting! Let's write the first requirement in that file. Now we start toplay!

requirerequire "rubygems"requirerequire "spec"

requirerequire "lib/dice"

describe Dice dodoit "should have numbers 1 to 6"

endend

To execute this test open the terminal end enter in the dice folder and use the test command:

cd dicespec spec//dice_spec

In our example with autotest, just digit the next command:

Learn BDD Playing Dice! - Your behavior is have fun

10

Page 11: Learn bdd-playing-dice-book

autospec

Now the test execute automatically every time that the files was saved. Come back to our example. Theoutput of this first test broke because we need create the Dice class in the lib/dice.rb file.

spec ----autospec spec//dice_spec.rbrb.//spec//dice_spec.rbrb:6: uninitialized constant Dice (NameError)

WRITE THE DICE CLASS

To fix the initial error, just write the Dice class:

classclass Diceendend

Output: show the pending requirement.

Pending:

Dice should have only numbers 1 to 6 (Not Yet Implemented).//spec//dice_spec.rbrb:7

Finished inin 0.04099 seconds

1 example, 0 failures, 1 pending

THE NUMBERS IN THE DICE

The first requirement is to delimit the numbers of dice: 1 to 6:

Chapter 1: Making a Dice class using BDD

11

Page 12: Learn bdd-playing-dice-book

describe Dice dodoit "should have only numbers 1 to 6" dodo

dice == DiceDice.newnew(1..6).shouldshould includeinclude( dice.playplay )

endendendend

Output:

F

1)NoMethodError inin 'Dice should have only numbers 1 to 6'undefined method `play' for #<Dice:0xb7b6986c>./spec/dice_spec.rb:9:

Finished in 0.073369 seconds

1 example, 1 failure

CREATE THE PLAY METHOD IN DICE.RB

To fix the previous failure let's write the play method:

classclass Dicedefdef playplayendend

endend

Output: still failure because the play method returns nil.

Learn BDD Playing Dice! - Your behavior is have fun

12

Page 13: Learn bdd-playing-dice-book

F

1)'Dice should have only numbers 1 to 6' FAILEDexpected 1..6 to includeinclude nilnil.//spec//dice_spec.rbrb:9:

Finished inin 0.031104 seconds

1 example, 1 failure

NUMBER OUTSIDE THE RANGE 1..6

Let's experiment to put a number outside of the range 1..6 to see what happens:

classclass Dicedefdef playplay

10endend

endend

Output: the test still failure because the number is outside the range.

F

1)'Dice should have numbers 1 to 6' FAILEDexpected 1..6 to includeinclude 10.//spec//dice_spec.rbrb:9:

Finished inin 0.03021 seconds

Chapter 1: Making a Dice class using BDD

13

Page 14: Learn bdd-playing-dice-book

1 example, 1 failure

NUMBER INSIDE THE RANGE 1..6

Now, let's put a number between 1-6, and finally the Dice class passes the test.

classclass Dicedefdef playplay

6endend

endend

Output:

.

Finished inin 0.027648 seconds

1 example, 0 failures

RANDOM NUMBERS

For while it's ok. Now, let's work with the random numbers requirement. I'll put also some requirements that Iremember, but not too much. I'll put x in the beginning of 'it' statement to rspec ignore then. this is just a trick;).

requirerequire "rubygems"requirerequire "spec"

Learn BDD Playing Dice! - Your behavior is have fun

14

Page 15: Learn bdd-playing-dice-book

requirerequire "lib/dice"

describe Dice dodoit "should have only numbers 1 to 6" dodo

dice == DiceDice.newnew(1..6).shouldshould includeinclude( dice.playplay )

endend

# Three rand groups of 1000 numbers must be different each otherit "should show the numbers randomly" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

xit "should store the last number after the dice was played."xit "should play the dice when dice object was initialized"

endend

Output:

.F

1)'Dice should show the numbers randomly' FAILEDexpected falsefalse, got truetrue.//spec//dice_spec.rbrb:18:

Finished inin 0.093321 seconds

Chapter 1: Making a Dice class using BDD

15

Page 16: Learn bdd-playing-dice-book

2 examples, 1 failure

GENERATE RANDOM NUMBER AND REFACTORY

Now I have to generate random numbers and also have to refactory the first requirement that limit thenumbers 1 to 6. This requirement must be tested with a variety of numbers instead of only one how it's rightnow. To play a little bit with ruby, I'll modify also the require code to simplify the number of lines.

classclass Dicedefdef playplay

randrand(6)endend

endend

%w{ rubygems spec lib/dice }.eacheach {|lib| requirerequire lib}

describe Dice dodoit "should show only numbers 1 to 6" dodo

dice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.should_notshould_not be_nilgroup.should_notshould_not be_emptygroup.should_notshould_not includeinclude('-') # Negative numbers aren't permittedgroup.should_notshould_not includeinclude('0')group.should_notshould_not includeinclude('7')group.should_notshould_not includeinclude('8')group.should_notshould_not includeinclude('9')

endend

Learn BDD Playing Dice! - Your behavior is have fun

16

Page 17: Learn bdd-playing-dice-book

# Three rand groups of 1000 numbers must be different each otherit "should show the numbers randomly" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

xit "should store the last number after the dice was played."xit "should play the dice when dice object was initialized"

endend

Output: rand(6) generate also zeros, the the test failure.

F.

1)'Dice should show only numbers 1 to 6' FAILEDexpected "40251322225105400021423125552351044325205220224304451434252545153314510153043001251012005523244142243512333204035042444130240534042050050324205500223120330524430331015422350203015044053545205524012055023101003331405204353205410102441530220034031430225503404511243223354504315335402445045511" notnot to includeinclude "0".//spec//dice_spec.rbrb:10:

Finished inin 0.046589 seconds

2 examples, 1 failure

Chapter 1: Making a Dice class using BDD

17

Page 18: Learn bdd-playing-dice-book

FIXING THE RANDOM NUMBER FAILURE

Finally let's put the command rand(6) + 1 to limit the range 1 to 6 and let's refactore the requirement of therange to use regexp. Now the test pass :).

classclass Dicedefdef playplay

randrand(6) ++ 1endend

endend

%w{ rubygems spec lib/dice }.eacheach {|lib| requirerequire lib}

describe Dice dodoit "should have only numbers 1 to 6" dodo

dice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Three rand groups of 1000 numbers must be different each otherit "should show the numbers randomly" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

xit "should store the last number after the dice was played."

Learn BDD Playing Dice! - Your behavior is have fun

18

Page 19: Learn bdd-playing-dice-book

xit "should play the dice when dice object was initialized"endend

STORE THE DICE NUMBER

Let's work in the next requirement: "should store the last number after the dice was played". I'll create themethod show_number and I'll put a constant to test pass.

%w{rubygems spec lib/dice}.eacheach {|lib| requirerequire lib}

describe Dice dodo

it "should have only numbers 1 to 6" dododice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Three groups of 1000 random numbers must be different each otherit "should show the numbers randomly" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

it "should store the last number after the dice was played." dododice == DiceDice.newnewdice.playplay

Chapter 1: Making a Dice class using BDD

19

Page 20: Learn bdd-playing-dice-book

dice.show_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)endend

xit "should play the dice when dice object was initialized."endend

classclass Dicedefdef playplay

randrand(6) ++ 1endend

defdef show_numbershow_number3

endendendend

CHANGING CONSTANTS BY VARIABLES

This is an important rule in BDD. Now you can change the constant by a instance variable in the dice class.The test'll pass.

classclass Dicedefdef playplay

@number == randrand(6) ++ 1endend

defdef show_numbershow_number@number

endendendend

Learn BDD Playing Dice! - Your behavior is have fun

20

Page 21: Learn bdd-playing-dice-book

PLAY THE DICE WHEN THE CLASS WAS INITIALIZED

Now I want that the dice play when it's initializing. The next test'll break.

%w{rubygems spec lib/dice}.eacheach {|lib| requirerequire lib}

describe Dice dodo

it "should have only numbers 1 to 6" dododice == DiceDice.newnewgroup == (1..1_000).collectcollect{ dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Three groups of 1000 random numbers must be different each otherit "should show the numbers randomly" dodo

dice == DiceDice.newnewgroup1 == (1..1_000).collectcollect{ dice.playplay }group2 == (1..1_000).collectcollect{ dice.playplay }group3 == (1..1_000).collectcollect{ dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false(group2 ==== group3).shouldshould be_false

endend

it "should store the last number after the dice was played." dododice == DiceDice.newnewdice.playplaydice.show_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

it "should play the dice when dice object was initialized." dodoDiceDice.newnew.show_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)

Chapter 1: Making a Dice class using BDD

21

Page 22: Learn bdd-playing-dice-book

endend

endend

Output:

...F

1)'Dice should play the dice when dice object was initialized.' FAILEDexpected "" to match /^[1-6]*[1-6]$/.//spec//dice_spec.rbrb:29:

Finished inin 0.12371 seconds

4 examples, 1 failure

USING THE INITIALIZEINITIALIZE METHOD

Now I finalize the dice class putting the initialize method with a message for the play method. The testpass.

classclass Dicedefdef initializeinitialize

playendend

defdef playplay@number == randrand(6) ++ 1

endend

Learn BDD Playing Dice! - Your behavior is have fun

22

Page 23: Learn bdd-playing-dice-book

defdef show_numbershow_number@number

endendendend

LET'S REFACTORY

Now I also can make a little refactory in the dice_spec.rb. I'll put a block before(:each) to simplify the code. I'llmake another refactory in the third requirement: "should store the last number after the dice was played".Now he works with a lot of numbers. The logic is if the last number is stored then two groups of 100 numbersstored are different each other.

%w{rubygems spec lib/dice}.eacheach {|lib| requirerequire lib}

describe Dice dodobeforebefore(::eacheach) dodo

@dice == DiceDice.newnewendend

it "should have only numbers 1 to 6" dodogroup == (1..1_000).collectcollect{ @dice.playplay }.joinjoingroup.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

# Three groups of 1000 random numbers must be different each otherit "should show the numbers randomly" dodo

group1 == (1..1_000).collectcollect{ @dice.playplay }group2 == (1..1_000).collectcollect{ @dice.playplay }group3 == (1..1_000).collectcollect{ @dice.playplay }(group1 ==== group2).shouldshould be_false(group1 ==== group3).shouldshould be_false

Chapter 1: Making a Dice class using BDD

23

Page 24: Learn bdd-playing-dice-book

(group2 ==== group3).shouldshould be_falseendend

it "should store the last number after the dice was played." dodogroup1 == (1..100).collectcollect dodo

@[email protected]_numbershow_number

endendgroup2 == (1..100).collectcollect dodo

@[email protected]_numbershow_number

endend(group1 ==== group2).shouldshould be_false

endend

it "should play the dice when dice object was initialized." [email protected]_numbershow_number.to_sto_s.shouldshould matchmatch(/^[1-6]*[1-6]$/)

endend

endend

PLAY WITH THE DICE CLASS

Now that the class is finished. Let's play with it in the irb!

>>>> requirerequire 'dice'=> truetrue>>>> dice == DiceDice.newnew=> #<Dice:0xb7a64188 @number=5>>>>> dice.show_numbershow_number=> 5

Learn BDD Playing Dice! - Your behavior is have fun

24

Page 25: Learn bdd-playing-dice-book

>>>> dice.classclass=> Dice

Using the dice only one time and abandon it

>>>> DiceDice.newnew.show_numbershow_number=> 2

Play the dice a lot of times:

>>>> 20.timestimes{ print dice.playplay }21636456135236136236=> 20

Three dice:

>>>> yellowdice == [DiceDice.newnew, DiceDice.newnew, DiceDice.newnew]=> [#<Dice:0xb7a3e3d4 @number=3>, #<Dice:0xb7a3e3ac @number=5>, #<Dice:0xb7a3e384 @number=5>]>>>> yellowdice.eacheach{ |dice| puts dice.show_numbershow_number }355=> [#<Dice:0xb7a3e3d4 @number=3>, #<Dice:0xb7a3e3ac @number=5>, #<Dice:0xb7a3e384 @number=5>]

Play again the same dice

>>>> yellowdice.eacheach{ |dice| puts dice.playplay }525=> [#<Dice:0xb7a3e3d4 @number=5>, #<Dice:0xb7a3e3ac @number=2>, #<Dice:0xb7a3e384 @number=5>]

What's the bigger value of the three dice of last play?

Chapter 1: Making a Dice class using BDD

25

Page 26: Learn bdd-playing-dice-book

>>>> puts yellowdice.collectcollect{ |item| item.show_numbershow_number }.maxmax5=> nilnil

And about the less value?

>>>> puts yellowdice.collectcollect{ |item| item.show_numbershow_number }.minmin2=> nilnil

Wait. The last example I used 3 dice?!? Ahaaa! This looks like the dice of war game. Let's create the wargame dice class in the next chapter.

Learn BDD Playing Dice! - Your behavior is have fun

26

Page 27: Learn bdd-playing-dice-book

Chapter 2

Reproducing the War Game Dice

DESCRIBING THE WAR GAME

Now I finally finished the Dice class! But I want something more excitant! I want reproduce in a ruby class thedice of war game. That's right! That 6 dice. 3 red and 3 yellow that represent attack and defence respectively.

I'll use two arrays: reddice and yellowdice to store the dice values. I remember now that the use of the dicedepend on the army number of attacker and defender. But in this experiment I'll think only in the handling ofdice in the game. Then the dice can be handle using 1, 2 or 3 red dice versus 1, 2 or 3 yellow dice. I'll have tocompare the bigger red value with the bigger yellow value and consequently the next values using this logic(from bigger to less values). In draw case the yellow win. The red only win when the number was bigger thenthe yellow.

Well, I think that's it. Let's work now!

Chapter 2: Reproducing the War Game Dice

27

Page 28: Learn bdd-playing-dice-book

TESTING ARRAY COMPARISON

Well, I remembered that we need to use array comparison in the dice of war game, but In my first test witharray comparison I had problems. You can see this problem in the next test:

describe Array dodoit "should be comparable" dodo

([8,9,10] >> [1,2,3]).shouldshould be_trueendend

endend

Output:

....F

1)NoMethodError inin 'Array should be comparable'undefined method `>' for [8, 9, 10]:Array./spec/dice_spec.rb:43:

Finished in 0.039464 seconds

5 examples, 1 failure

INCLUDE COMPARABLECOMPARABLE MODULE

To solve the previous issue, just include the Comparable module in the dice.rb file. I'll put in a simplify formpurposely to play with ruby possibilities :).

classclass Array; includeinclude Comparable; endend

Learn BDD Playing Dice! - Your behavior is have fun

28

Page 29: Learn bdd-playing-dice-book

Now the test pass.

NUMBER OF DICE

Now I'll work on the first requirement: The attack and defence should use 1, 2 or 3 dice. I'll put intentionallya number different of 1, 2, 3 to cause an error.

describe Wardice dodoit "The attack and defence should use 1, 2 or 3 dice" dodo

wardice == WardiceWardice.newnew(0, 3)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

endend

it "Should compare from bigger to less values and save in array result"endend

classclass Wardiceattr_readerattr_reader ::redred, ::yellowyellow

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow

endendendend

Output:

'Wardice The attack and defence should use 1, 2 or 3 dice' FAILEDexpected "0" to match /^[1-3]$/.//spec//dice_spec.rbrb:50:

Chapter 2: Reproducing the War Game Dice

29

Page 30: Learn bdd-playing-dice-book

Finished inin 0.032203 seconds

8 examples, 1 failure, 2 pending

SOLVING THE NUMBER ISSUE

If the class was initialized with numbers differents of the range (1..3) then the variables will be filled with arand number between the range. I'll make also a little refactory in the spec file to put a situation that a numberof dice is correct. You can see below the solution.

classclass WarDiceattr_readerattr_reader ::redred, ::yellowyellow

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

endendendend

describe WarDice dodoit "The attack and defence should use 1, 2 or 3 dice" dodo

wardice == WardiceWardice.newnew(0, 7)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

wardice2 == WardiceWardice.newnew(2, 3)wardice2.redred.shouldshould ==== 2wardice2.yellowyellow.shouldshould ==== 3

endend

Learn BDD Playing Dice! - Your behavior is have fun

30

Page 31: Learn bdd-playing-dice-book

it "Should compare from bigger to less values and save in array result"endend

DECREASE SORT ARRAY

This is a important requirement, but I remembered only now: wardice Should provide yellow and red diceresults with an array in decreasing sort. You can see the solution below:

describe WarDice dodoit "The attack and defence should use 1, 2 or 3 dice" dodo

wardice == WarDiceWarDice.newnew(0, 7)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

wardice2 == WarDiceWarDice.newnew(2, 3)wardice2.redred.shouldshould ==== 2wardice2.yellowyellow.shouldshould ==== 3

endend

it "Should provide yellow and red dice results with an array in decreasing sort" dodowardice == WardiceWardice.newnew(3, 3)wardice.reddicereddice.is_a?is_a?(Array).shouldshould be_truewardice.yellowdiceyellowdice.is_a?is_a?(Array).shouldshould be_true

wardice.reddicereddice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.reddicereddicewardice.yellowdiceyellowdice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.yellowdiceyellowdice

endend

it "Should compare from bigger to less values and save in array result"

endend

Chapter 2: Reproducing the War Game Dice

31

Page 32: Learn bdd-playing-dice-book

classclass wardiceattr_readerattr_reader ::redred, ::yellowyellow, ::reddicereddice, ::yellowdiceyellowdice

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

@reddice == []@yellowdice == []

@dice == [email protected]{|row| @reddice[row] == [@dice.playplay] }@yellow.timestimes{ |row| @yellowdice[row] == [@dice.playplay] }

endendendend

Output:

'wardice Should provide yellow and red dice results with an array in decreasing sort' FAILEDexpected: [[5], [2], [4]],

got: [[5], [4], [2]] (using ====).//spec//dice_spec.rbrb:63:

Finished inin 0.035218 seconds

9 examples, 1 failure, 2 pending

Learn BDD Playing Dice! - Your behavior is have fun

32

Page 33: Learn bdd-playing-dice-book

SOLVING SORTING ARRAY ISSUEclassclass WarDice

attr_readerattr_reader ::redred, ::yellowyellow, ::reddicereddice, ::yellowdiceyellowdice

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

@reddice == []@yellowdice == []

@dice == [email protected]{|row| @reddice[row] == [@dice.playplay] }@yellow.timestimes{ |row| @yellowdice[row] == [@dice.playplay] }

@reddice.sort!sort!{|x,y| y <=><=> x }@yellowdice.sort!sort!{|x,y| y <=><=> x }

endendendend

Now the test pass.

COMPARING VALUES

Let's work now in the last requirement: Should compare from bigger to less values and save in arrayresult

describe Wardice dodoit "The attack and defence should use 1, 2 or 3 dice" dodo

Chapter 2: Reproducing the War Game Dice

33

Page 34: Learn bdd-playing-dice-book

wardice == WardiceWardice.newnew(0, 7)wardice.redred.to_sto_s.shouldshould matchmatch(/^[1-3]$/)wardice.yellowyellow.to_sto_s.shouldshould matchmatch(/^[1-3]$/)

wardice2 == WardiceWardice.newnew(2, 3)wardice2.redred.shouldshould ==== 2wardice2.yellowyellow.shouldshould ==== 3

endend

it "Should provide yellow and red dice results with an array in decreasing sort" dodowardice == WardiceWardice.newnew(3, 3)wardice.reddicereddice.is_a?is_a?(Array).shouldshould be_truewardice.yellowdiceyellowdice.is_a?is_a?(Array).shouldshould be_true

wardice.reddicereddice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.reddicereddicewardice.yellowdiceyellowdice.sortsort{|x, y| y <=><=> x }.shouldshould ==== wardice.yellowdiceyellowdice

endend

it "Should compare from bigger to less values and save in array result" dodowardice == WardiceWardice.newnew(3, 2)wardice.reddicereddice.firstfirst.shouldshould >> wardice.reddicereddice.lastlastwardice.attackattack

wardice.resultresult[0].shouldshould ==== "Red Win" ifif wardice.reddicereddice[0] >> wardice.yellowdiceyellowdice[0]wardice.resultresult[0].shouldshould ==== "Yellow Win" ifif wardice.reddicereddice[0] <=<= wardice.yellowdiceyellowdice[0]

endendendend

The final WarDice class:

classclass Wardiceattr_readerattr_reader ::redred, ::yellowyellow, ::reddicereddice, ::yellowdiceyellowdice, ::resultresult

Learn BDD Playing Dice! - Your behavior is have fun

34

Page 35: Learn bdd-playing-dice-book

defdef initializeinitialize(red, yellow)@red, @yellow == red, yellow@red == randrand(3) ++ 1 ifif @red.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?@yellow == randrand(3) ++ 1 ifif @yellow.to_sto_s.grepgrep(/^[1-3]$/).empty?empty?

@reddice == []@yellowdice == []@result == []

@dice == [email protected]{|row| @reddice[row] == [@dice.playplay] }@yellow.timestimes{ |row| @yellowdice[row] == [@dice.playplay] }

@reddice.sort!sort!{|x, y| y <=><=> x }@yellowdice.sort!sort!{|x, y| y <=><=> x }

endend

defdef [email protected]_with_indexeach_with_index dodo |item, index|

nextnext ifif @yellowdice[index].nil?nil?reddice == itemyellowdice == @yellowdice[index]

ifif reddice >> yellowdice@result <<<< "Red Win"

elseelse@result <<<< "Yellow Win"

endendendend

endendendend

Chapter 2: Reproducing the War Game Dice

35

Page 36: Learn bdd-playing-dice-book

PLAY WITH THE WARDICE CLASS

Open irb and write the next sequence:

>>>> requirerequire 'dice'=> truetrue

Initialize the WarDice class. The first parameter is the number of red dice and the second is the number ofyellow dice:

>>>> wardice == WarDiceWarDice.newnew(2, 3)=> #<WarDice:0xb7b0b410 @yellowdice=[[6], [1], [1]], @reddice=[[3], [1]], @dice=#<Dice:0xb7b0ae0c@number==6>>, yellow3, result["Yellow Win", "Yellow Win"], red2

Show numbers in yellow and red dice:

>>>> puts wardice.reddicereddice31=> nilnil>>>> puts wardice.yellowdiceyellowdice611=> nilnil

Show result:

>>>> puts wardice.resultresultYellow WinYellow Win=> nilnil

Learn BDD Playing Dice! - Your behavior is have fun

36

Page 37: Learn bdd-playing-dice-book

All output in one command:

>>>> war.reddicereddice.each_with_indexeach_with_index{ |item, index| puts "Red:#{item} Yellow:#{war.yellowdiceyellowdice[index]}- #{war.resultresult[index]}"}Red:3 Yellow:6 -- Yellow WinRed:1 Yellow:1 -- Yellow Win=> [[3], [1]]

Now we have the tools to play. Then the next chapter we'll use this classes to make a nice graphicapplication using Shoes.

Chapter 2: Reproducing the War Game Dice

37

Page 38: Learn bdd-playing-dice-book

Chapter 3

Playing dice with shoes

A LITTLE TEST WITH DICE CLASS AND SHOES

Now let's create a graphic interface using Shoes that will use the class WarDice. But first let's' make a simpletest with the class Dice. You have to create a new file diceshoes.rb, then paste the code below inside it andsave the file at the same directory of dice.rb file. Finally, execute it with shoes to see the result.

Observe that I just included the dice.rb file with a require in the beginning of the code below:

requirerequire 'dice'ShoesShoes.appapp ::titletitle => "Test with dice class", ::widthwidth => 500, ::heightheight => 500 dodo

background aliceblue

para "Welcome! This is an example using Dice class.", ::weightweight => "bold"

dice == DiceDice.newnew

Learn BDD Playing Dice! - Your behavior is have fun

38

Page 39: Learn bdd-playing-dice-book

# Print dice numbers with random colors1_000.timestimes dodo

r, g, b == rand, rand, randpara dice.playplay, ::strokestroke => rgbrgb(r****3, g****3, b****3), ::sizesize => "large"

endendendend

Output:

THE WAR DICE SHOES CODE

Finally the last application. Copy the following code in a new file and save with the name wardiceshoes.rb inthe lib folder. I put some remarks across the code to facilitate the understanding. Have a nice experiment.

Chapter 3: Playing dice with shoes

39

Page 40: Learn bdd-playing-dice-book

requirerequire 'dice'

ShoesShoes.appapp ::titletitle => "Dice of War game", ::widthwidth => 500, ::heightheight => 500 dodobackground gradientgradient( black, teal )

# List with number of red dice: 1, 2 or 3para "Red", ::strokestroke => tomato@numberreddice == list_box ::itemsitems => ["1", "2", "3"],

::widthwidth => 70, ::choosechoose => "3" dodo |list|endend

para " X ", ::strokestroke => snow

# List with number of yellow dice: 1, 2 or 3@numberyellowdice == list_box ::itemsitems => ["1", "2", "3"],

::widthwidth => 70, ::choosechoose => "3" dodo |list|endendpara "Yellow", ::strokestroke => yellow

# Define an aleatory position@a == @b == @c == []

(40..200).stepstep(10){ |x| @a <<<< x }(230..450).stepstep(10){ |y| @b <<<< y }(80..450).stepstep(10){ ||z|| @c <<<< z }

# Variables that will store values of wardice object@reddice == @yellowdice == @resulttext == []

button "Attack", ::widthwidth => 80 dodo

# Clear the [email protected]{ |d| d.removeremove }@resulttext.eacheach{ |a| a.removeremove }

Learn BDD Playing Dice! - Your behavior is have fun

40

Page 41: Learn bdd-playing-dice-book

# The wardice object is initializingwardice == WarDiceWarDice.newnew( @numberreddice.texttext.to_ito_i, @numberyellowdice.texttext.to_ito_i )@reddice == wardice.reddicereddice@yellowdice == wardice.yellowdiceyellowdice@result == wardice.resultresult

# Every dice is drawn in random [email protected]{ |item| drawdraw( @a[randrand(@a.lengthlength)], @c[randrand(@c.lengthlength)], item.to_sto_s.to_ito_i, 1, truetrue ) }@yellowdice.eacheach{ |item| drawdraw( @b[randrand(@b.lengthlength)], @c[randrand(@c.lengthlength)], item.to_sto_s.to_ito_i, 2, truetrue )}

endend

button "Verify", ::widthwidth => 80 dodo

# Clear the [email protected]{ |d| d.removeremove }@resulttext.eacheach{ |a| a.removeremove }

# Initial position of diceleftyellow == 250leftred == 150topred == topyellow == 100

# Every dice are drawn in a defined [email protected] dodo |item|

drawdraw( leftred, topred, item.to_sto_s.to_ito_i, 1, falsefalse )topred +=+= 100

endend

@yellowdice.eacheach dodo |item|drawdraw( leftyellow, topyellow, item.to_sto_s.to_ito_i, 2, falsefalse )topyellow +=+= 100

endend

Chapter 3: Playing dice with shoes

41

Page 42: Learn bdd-playing-dice-book

# Initial position of resultleftresult == 300topresult == 80

# The results are drawn in defined [email protected]_with_indexeach_with_index dodo |item, index|

@resulttext[index] == para item.to_sto_s, ::strokestroke => snow, ::toptop => topresult, ::leftleft => leftresulttopresult +=+= 100

endend

endend

# Method draw was based in the Pretty Dice project written by Ed Heil@dice == []

defdef drawdraw( left, top, number, color, rotate )

imagewidth == 60imageheight == 60

i == imageimage( imagewidth, imageheight,::toptop => top -- imagewidth // 2,::leftleft => left -- imagewidth // 2,::shadowshadow => 10, ::centercenter => truetrue ) dodo

ifif color ==== 1strokecolor == redfillrectanglecolor == tomatofilldotscolor == darkred

elseelsestrokecolor == yellowfillrectanglecolor == lightgoldenrodyellow

Learn BDD Playing Dice! - Your behavior is have fun

42

Page 43: Learn bdd-playing-dice-book

filldotscolor == chocolateendend

sw == 1strokewidth swstroke strokecolor

fill fillrectanglecolor

inset == 2inset2 == 8

rectrect( inset, inset, imagewidth--inset--sw, imageheight--inset--sw, 10 )

fill filldotscolor

ovalradius == 10low == inset2high == imagewidth -- inset2 -- ovalradiusmid == ( imagewidth -- ovalradius ) // 2

ovaloval( mid, mid, ovalradius ) ifif number %% 2 ==== 1

ifif number >> 1ovaloval( low, low, ovalradius )ovaloval( high, high, ovalradius )

endend

ifif number >> 3ovaloval( low, high, ovalradius )ovaloval( high, low, ovalradius )

endend

ifif number >> 5

Chapter 3: Playing dice with shoes

43

Page 44: Learn bdd-playing-dice-book

ovaloval( mid, low, ovalradius )ovaloval( mid, high, ovalradius )

endend

endend # end of image block

i.rotaterotate( randrand( 359 ) ) ifif rotate

@dice <<<< i

endend

endend

PLAYING WITH WAR DICE SHOES APP

Below you can see the War Dice app in action. You must choose the number of red and yellow dice inrespective list box. Then click in Attack button to play dice. Now just have fun!

Learn BDD Playing Dice! - Your behavior is have fun

44

Page 45: Learn bdd-playing-dice-book

Now click in verify button to know who wins!

Chapter 3: Playing dice with shoes

45

Page 46: Learn bdd-playing-dice-book

Learn BDD Playing Dice! - Your behavior is have fun

46