snakes_and_ladders 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +156 -0
- data/Rakefile +10 -0
- data/lib/snakes_and_ladders/board.rb +26 -0
- data/lib/snakes_and_ladders/cell.rb +19 -0
- data/lib/snakes_and_ladders/game.rb +58 -0
- data/lib/snakes_and_ladders/grid.rb +55 -0
- data/lib/snakes_and_ladders/player.rb +34 -0
- data/lib/snakes_and_ladders/portal.rb +21 -0
- data/lib/snakes_and_ladders/version.rb +3 -0
- data/lib/snakes_and_ladders.rb +22 -0
- data/snakes_and_ladders.gemspec +25 -0
- data/spec/snakes_and_ladders/board_spec.rb +74 -0
- data/spec/snakes_and_ladders/cell_spec.rb +57 -0
- data/spec/snakes_and_ladders/game_spec.rb +152 -0
- data/spec/snakes_and_ladders/grid_spec.rb +108 -0
- data/spec/snakes_and_ladders/player_spec.rb +123 -0
- data/spec/snakes_and_ladders/portal_spec.rb +62 -0
- data/spec/spec_helper.rb +82 -0
- data/spec/support/matchers.rb +5 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0f2c16f3b1a14cc698be0bba9d33de259cdff5c
|
4
|
+
data.tar.gz: 48566fe26a8518f5e54f393cc1bdcf6053010384
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7786ee623a842528548f0f3768dc8eaafdd4415b9e2fc2803648f162b6c27c5078cf0fcd201697c389ef126d7438be4728765fddb9a633ea7edc0c80ecbf507f
|
7
|
+
data.tar.gz: ce2ea4b033ae9e0766723033057332354be8816d40434c18ddbccd8c70fd89b2e2cf0684d1a8fb97c041cfbb9050410ad36f470b964fe83566b0c1b4eb5b27a0
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Mohamad El-Husseini
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
# SnakesAndLadders
|
2
|
+
|
3
|
+
This gem is a CLI clone of the classic board game [Snakes and Ladders][1] (or Chutes and Ladders). It's written in Ruby, and uses the Object-oriented programming paradigm.
|
4
|
+
|
5
|
+
This gem uses features only available in **Ruby 2.1** and above, such as *keyword arguments*, and *required keyword arguments*. You will need Ruby 2.1 to run this gem.
|
6
|
+
|
7
|
+
## Table of Contents
|
8
|
+
- [About the Game][game]
|
9
|
+
- [Installation][installation]
|
10
|
+
- [Playing the Game][playing]
|
11
|
+
- [Simulating Games][simulating]
|
12
|
+
- [Extensibility and Building Custom Boards][extensibility]
|
13
|
+
- [Rendering][rendering]
|
14
|
+
- [Contributing][contributing]
|
15
|
+
|
16
|
+
## About the Game
|
17
|
+
|
18
|
+
From the Snakes and Ladders [Wikipedia entry][1]:
|
19
|
+
|
20
|
+
>Snakes and Ladders is an ancient Indian board game regarded today as a worldwide classic. It is played between two or more players on a gameboard having numbered, gridded squares. A number of "ladders" and "snakes" are pictured on the board, each connecting two specific board squares. The object of the game is to navigate one's game piece, according to die rolls, from the start (bottom square) to the finish (top square), helped or hindered by ladders and snakes respectively. The historic version had root in morality lessons, where a player's progression up the board represented a life journey complicated by virtues (ladders) and vices (snakes).
|
21
|
+
|
22
|
+
This implementation is feature complete except for one rule: Rolling three sixes consecutively will not send you back to square one. Like the original, landing on or rolling in excess of the last square will cause you to win.
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
gem 'snakes_and_ladders'
|
29
|
+
|
30
|
+
And then execute:
|
31
|
+
|
32
|
+
$ bundle
|
33
|
+
|
34
|
+
Or install it yourself as:
|
35
|
+
|
36
|
+
$ gem install snakes_and_ladders
|
37
|
+
|
38
|
+
## Playing
|
39
|
+
|
40
|
+
First, create some players. Unlike its physical counterpart, this implementation has no player limit; you can play with as many players as you want.
|
41
|
+
|
42
|
+
````ruby
|
43
|
+
peach = SnakesAndLadders.new_player(name: "Peach", color: "Pink")
|
44
|
+
luigi = SnakesAndLadders.new_player(name: "Luigi", color: "Green")
|
45
|
+
````
|
46
|
+
|
47
|
+
Now we need a game instance. The game ships with the classic board (based on Milton Bradley edition, 1952). You can create your own boards using the Grid class. A random board generator will soon become available.
|
48
|
+
|
49
|
+
````ruby
|
50
|
+
game = SnakesAndLadders.classic([peach, luigi])
|
51
|
+
````
|
52
|
+
|
53
|
+
Now let's roll the die to play.
|
54
|
+
|
55
|
+
````ruby
|
56
|
+
game.play_turn
|
57
|
+
# => Peach rolls 1.
|
58
|
+
# => Peach moves to square 1 and takes a ladder!
|
59
|
+
# => Peach is on square 38.
|
60
|
+
game.play_turn
|
61
|
+
# => Luigi rolls 5.
|
62
|
+
# ...
|
63
|
+
````
|
64
|
+
|
65
|
+
## Simulating Games
|
66
|
+
|
67
|
+
You can simulate entire games from start to finish without having to call `play_turn`. At any time you can call `game.simulate`, and the game will play itself to the end. You can simulate games before and after they have started.
|
68
|
+
|
69
|
+
````ruby
|
70
|
+
game.simulate
|
71
|
+
# => Peach rolls a 5!
|
72
|
+
# => Peach is on square 43.
|
73
|
+
# => Luigi rolls 2!
|
74
|
+
# => Luigi moves to square 80 and takes a ladder!
|
75
|
+
# => Luigi is on square 100.
|
76
|
+
# => Game over! Luigi wins in 14 turns. Congratulations!
|
77
|
+
````
|
78
|
+
|
79
|
+
## Extensibility
|
80
|
+
|
81
|
+
The game is fairly extensible and additional tiles to cell, snake, and ladder are pluggable. You can create additional tiles by inheriting from `SnakesAndLadders::Cell` and overriding the `enter` and `exit` methods where necessary.
|
82
|
+
|
83
|
+
````ruby
|
84
|
+
module SnakesAndLadders
|
85
|
+
class Bowser < Cell
|
86
|
+
def initialize(location: location)
|
87
|
+
super
|
88
|
+
end
|
89
|
+
|
90
|
+
def enter(player, board)
|
91
|
+
player.kidnap!
|
92
|
+
puts "#{player} was kidnapped by evil Bower. Bwahahahaha!"
|
93
|
+
super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
````
|
98
|
+
|
99
|
+
In this example we created a new tile called Bowser. It kidnaps any player that lands on it. The implementation of kidnap is not important for the purpose of this example.
|
100
|
+
|
101
|
+
### Creating Custom Boards
|
102
|
+
|
103
|
+
Our Bowser tile is not going to use itself. We have to build a board and tell it where to place Bowser tiles. We can do this using the packaged builder. The `Grid` class is responsible for mounting new boards. It accepts an array of tile mappings.
|
104
|
+
|
105
|
+
````ruby
|
106
|
+
mappings = [
|
107
|
+
{ class: :Portal, location: 1, destination: 38 },
|
108
|
+
{ class: :Bowser, location: 5 },
|
109
|
+
{ class: :Portal, location: 16, destination: 6 },
|
110
|
+
# ...
|
111
|
+
]
|
112
|
+
````
|
113
|
+
|
114
|
+
A mapping is just a hash of attributes that represent a tile. A mapping *must contain* `class` and `location` keys. You only have to create mappings for custom tiles. The grid will fill any missing locations with a normal `Cell` objects (this behaviour is also customisable by passing in a custom `default_tile` argument).
|
115
|
+
|
116
|
+
Our example mappings will produce the following board. The numbers denote tile numbers.
|
117
|
+
|
118
|
+
````
|
119
|
+
| 1. Portal | 2. Cell | 3. Cell | 4. Cell | 5. Bowser | 6. Cell ... | 16. Portal |
|
120
|
+
````
|
121
|
+
|
122
|
+
If you are wondering what `Portal` is, it's a class that represents snakes and ladders. Snakes and ladders have identical behaviour. Only their data differs. In the case of a snake, its destination is always smaller than its location. The opposite is true for ladders. So we can use the same class to represent both. You can think of them as portals because they transport players to other locations; *where to* is secondary.
|
123
|
+
|
124
|
+
Once we have our mappings we tell the grid to mount them.
|
125
|
+
|
126
|
+
````ruby
|
127
|
+
tiles = SnakesAndLadders::Grid.new(size: 100, tile_mappings: mappings).build
|
128
|
+
board = SnakesAndLadders::Board.new(grid: tiles)
|
129
|
+
game = SnakesAndLadders::Game.new(board: board, players: players)
|
130
|
+
````
|
131
|
+
|
132
|
+
Now our game will use our shiny new Bowser tile. Needless to say you should only create mappings for classes that you have built.
|
133
|
+
|
134
|
+
## Rendering
|
135
|
+
|
136
|
+
The game has no built in renderer. But any instance of Game contains all the information needed to render the game, so it's relatively easy to create your own render.
|
137
|
+
|
138
|
+
## Contributing
|
139
|
+
|
140
|
+
If you fancy a crack at creating a terminal renderer class, please go ahead. I'm also always looking for code reviews.
|
141
|
+
|
142
|
+
1. Fork it
|
143
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
144
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
145
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
146
|
+
5. Create new Pull Request
|
147
|
+
|
148
|
+
|
149
|
+
[1]: http://en.wikipedia.org/wiki/Snakes_and_Ladders
|
150
|
+
[game]: #about-the-game
|
151
|
+
[installation]: #installation
|
152
|
+
[playing]: #playing
|
153
|
+
[simulating]: #simulating-games
|
154
|
+
[extensibility]: #extensibility
|
155
|
+
[rendering]: #rendering
|
156
|
+
[contributing]: #contributing
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module SnakesAndLadders
|
2
|
+
class Board
|
3
|
+
attr_reader :grid
|
4
|
+
|
5
|
+
def initialize(grid: Grid.classic)
|
6
|
+
@grid = grid
|
7
|
+
end
|
8
|
+
|
9
|
+
def move(player, from, to)
|
10
|
+
if destination = get_cell(to)
|
11
|
+
if location = get_cell(from)
|
12
|
+
location.exit(player)
|
13
|
+
end
|
14
|
+
destination.enter(player, self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_cell(index)
|
19
|
+
grid[index]
|
20
|
+
end
|
21
|
+
|
22
|
+
def size
|
23
|
+
grid.size
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SnakesAndLadders
|
2
|
+
class Cell
|
3
|
+
attr_reader :location, :players
|
4
|
+
|
5
|
+
def initialize(location:, players: [])
|
6
|
+
@location = location
|
7
|
+
@players = players
|
8
|
+
end
|
9
|
+
|
10
|
+
def exit(player)
|
11
|
+
players.delete(player)
|
12
|
+
end
|
13
|
+
|
14
|
+
def enter(player, board)
|
15
|
+
players.push(player) && player.position = location
|
16
|
+
puts "#{player} is on square #{location}."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SnakesAndLadders
|
2
|
+
class Game
|
3
|
+
attr_reader :board, :players, :winner, :turn, :player
|
4
|
+
|
5
|
+
def initialize(board:, players: [], turn: 0)
|
6
|
+
@board = board
|
7
|
+
@players = players
|
8
|
+
@turn = turn
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_player(player)
|
12
|
+
players.push(player)
|
13
|
+
end
|
14
|
+
|
15
|
+
def play_turn
|
16
|
+
return if over?
|
17
|
+
|
18
|
+
init_turn
|
19
|
+
|
20
|
+
puts "#{player} rolls #{player.last_roll}!"
|
21
|
+
|
22
|
+
if board.move(player, player.position, player.destination_after_last_roll)
|
23
|
+
self.winner = player if won?
|
24
|
+
else
|
25
|
+
self.winner = player if will_win?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def over?
|
30
|
+
!!winner
|
31
|
+
end
|
32
|
+
|
33
|
+
def simulate
|
34
|
+
play_turn until over?
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def init_turn
|
40
|
+
@player = players.at(turn % players.size)
|
41
|
+
@player.roll_die
|
42
|
+
@turn += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def won?
|
46
|
+
player.position.equal?(board.size)
|
47
|
+
end
|
48
|
+
|
49
|
+
def will_win?
|
50
|
+
player.destination_after_last_roll >= board.size
|
51
|
+
end
|
52
|
+
|
53
|
+
def winner=(player)
|
54
|
+
@winner = player
|
55
|
+
puts "Game over! #{winner} wins in #{winner.turns} turns. Congratulations!"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SnakesAndLadders
|
2
|
+
class Grid
|
3
|
+
attr_reader :size, :tile_mappings, :default_tile
|
4
|
+
|
5
|
+
CLASSIC_BOARD_MAPPINGS = [
|
6
|
+
{ class: :Portal, location: 1, destination: 38 },
|
7
|
+
{ class: :Portal, location: 4, destination: 14 },
|
8
|
+
{ class: :Portal, location: 9, destination: 31 },
|
9
|
+
{ class: :Portal, location: 16, destination: 6 },
|
10
|
+
{ class: :Portal, location: 28, destination: 84 },
|
11
|
+
{ class: :Portal, location: 36, destination: 44 },
|
12
|
+
{ class: :Portal, location: 40, destination: 42 },
|
13
|
+
{ class: :Portal, location: 47, destination: 26 },
|
14
|
+
{ class: :Portal, location: 49, destination: 11 },
|
15
|
+
{ class: :Portal, location: 51, destination: 67 },
|
16
|
+
{ class: :Portal, location: 56, destination: 53 },
|
17
|
+
{ class: :Portal, location: 62, destination: 19 },
|
18
|
+
{ class: :Portal, location: 64, destination: 60 },
|
19
|
+
{ class: :Portal, location: 71, destination: 81 },
|
20
|
+
{ class: :Portal, location: 80, destination: 100 },
|
21
|
+
{ class: :Portal, location: 87, destination: 24 },
|
22
|
+
{ class: :Portal, location: 93, destination: 73 },
|
23
|
+
{ class: :Portal, location: 95, destination: 75 },
|
24
|
+
{ class: :Portal, location: 98, destination: 78 },
|
25
|
+
]
|
26
|
+
|
27
|
+
def self.classic
|
28
|
+
mappings = Marshal.load(Marshal.dump(CLASSIC_BOARD_MAPPINGS))
|
29
|
+
new(size: 100, tile_mappings: mappings).build
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(size:, tile_mappings: [], default_tile: :Cell)
|
33
|
+
@size = size
|
34
|
+
@tile_mappings = tile_mappings
|
35
|
+
@default_tile = default_tile
|
36
|
+
|
37
|
+
raise ArgumentError, "Board size must be equal to or greater than its tiles." if size < tile_mappings.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def build
|
41
|
+
(1..size).each_with_object({}) { |index, grid| grid[index] = tile_at(index) }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def tile_at(index)
|
47
|
+
if mapping = tile_mappings.detect { |tile_map| tile_map[:location].equal?(index) }
|
48
|
+
klass = mapping.delete(:class)
|
49
|
+
SnakesAndLadders.const_get(klass).new(mapping)
|
50
|
+
else
|
51
|
+
SnakesAndLadders.const_get(default_tile).new(location: index)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SnakesAndLadders
|
2
|
+
class Player
|
3
|
+
attr_reader :name, :color, :die_rolls
|
4
|
+
|
5
|
+
attr_accessor :position
|
6
|
+
|
7
|
+
def initialize(name:, color:, position: 0, die_rolls: [])
|
8
|
+
@name = name
|
9
|
+
@color = color
|
10
|
+
@position = position
|
11
|
+
@die_rolls = die_rolls
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
name
|
16
|
+
end
|
17
|
+
|
18
|
+
def roll_die
|
19
|
+
die_rolls.push(rand 1..6).last
|
20
|
+
end
|
21
|
+
|
22
|
+
def turns
|
23
|
+
die_rolls.size
|
24
|
+
end
|
25
|
+
|
26
|
+
def last_roll
|
27
|
+
die_rolls.last
|
28
|
+
end
|
29
|
+
|
30
|
+
def destination_after_last_roll
|
31
|
+
position + last_roll
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SnakesAndLadders
|
2
|
+
class Portal < Cell
|
3
|
+
attr_reader :destination
|
4
|
+
|
5
|
+
def initialize(location:, destination:)
|
6
|
+
@destination = destination
|
7
|
+
super(location: location)
|
8
|
+
|
9
|
+
raise ArgumentError, "Location and destination can not be equal" if location.equal?(destination)
|
10
|
+
end
|
11
|
+
|
12
|
+
def enter(player, board)
|
13
|
+
puts "#{player} moves to square #{location} and takes a #{type}!"
|
14
|
+
board.move(player, location, destination)
|
15
|
+
end
|
16
|
+
|
17
|
+
def type
|
18
|
+
location > destination ? :snake : :ladder
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "snakes_and_ladders/game"
|
2
|
+
require "snakes_and_ladders/board"
|
3
|
+
require "snakes_and_ladders/grid"
|
4
|
+
require "snakes_and_ladders/cell"
|
5
|
+
require "snakes_and_ladders/portal"
|
6
|
+
require "snakes_and_ladders/player"
|
7
|
+
require "snakes_and_ladders/version"
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "pry"
|
11
|
+
rescue LoadError
|
12
|
+
end
|
13
|
+
|
14
|
+
module SnakesAndLadders
|
15
|
+
def self.classic(players)
|
16
|
+
SnakesAndLadders::Game.new(board: SnakesAndLadders::Board.new, players: players)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.new_player(name:, color:)
|
20
|
+
SnakesAndLadders::Player.new(name: name, color: color)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'snakes_and_ladders/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "snakes_and_ladders"
|
8
|
+
spec.version = SnakesAndLadders::VERSION
|
9
|
+
spec.authors = ["Mohamad El-Husseini"]
|
10
|
+
spec.email = ["husseini.mel@gmail.com"]
|
11
|
+
spec.description = %q{A Ruby implementation of Snakes and Ladders (Chutes and Ladders) board game.}
|
12
|
+
spec.summary = %q{This is an object oriented Ruby implementation of the classic Snakes and Ladders (Chutes and Ladders) board game.}
|
13
|
+
spec.homepage = "https://github.com/abitdodgy/snakes_and_ladders"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module SnakesAndLadders
|
4
|
+
describe SnakesAndLadders::Board do
|
5
|
+
let(:luigi) { double(:player) }
|
6
|
+
|
7
|
+
let(:first_cell) { double(:cell) }
|
8
|
+
let(:second_cell) { double(:cell) }
|
9
|
+
|
10
|
+
let(:grid) { { 3 => first_cell, 6 => second_cell } }
|
11
|
+
|
12
|
+
let(:board) { Board.new(grid: grid) }
|
13
|
+
|
14
|
+
describe "#initialize" do
|
15
|
+
it "defaults to classic grid" do
|
16
|
+
expect { Board.new }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sets grid" do
|
20
|
+
board = Board.new(grid: "any")
|
21
|
+
expect(board.grid).to eq "any"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#grid" do
|
26
|
+
it "returns the grid" do
|
27
|
+
expect(board.grid).to eq grid
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#size" do
|
32
|
+
it "returns grid size" do
|
33
|
+
expect(board.size).to eq 2
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#get_cell" do
|
38
|
+
it "returns object at index" do
|
39
|
+
expect(board.get_cell(3)).to eq first_cell
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns nil if there is no object at index" do
|
43
|
+
expect(board.get_cell(4)).to be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#move" do
|
48
|
+
context "when player is off the board" do
|
49
|
+
it "moves player to destination" do
|
50
|
+
expect(first_cell).to_not receive(:exit)
|
51
|
+
expect(second_cell).to receive(:enter).with luigi, board
|
52
|
+
board.move(luigi, 0, 6)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when player is on a cell" do
|
57
|
+
it "moves player between cells" do
|
58
|
+
expect(first_cell).to receive(:exit).with luigi
|
59
|
+
expect(second_cell).to receive(:enter).with luigi, board
|
60
|
+
board.move(luigi, 3, 6)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when movement exceeds board size" do
|
65
|
+
it "does not move player and returns false" do
|
66
|
+
expect(second_cell).not_to receive :exit
|
67
|
+
expect(second_cell).not_to receive :enter
|
68
|
+
result = board.move(luigi, 3, 7)
|
69
|
+
expect(result).to be_nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module SnakesAndLadders
|
4
|
+
describe Cell do
|
5
|
+
let(:cell) { Cell.new(location: 1) }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
it "raises an error without location" do
|
9
|
+
expect { Cell.new }.to raise_error(ArgumentError).with_message("missing keyword: location")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "sets location" do
|
13
|
+
expect(cell.location).to eq 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it "defaults players to empty array" do
|
17
|
+
expect(cell.players).to eq []
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets players" do
|
21
|
+
cell = Cell.new(location: 1, players: ["Mario"])
|
22
|
+
expect(cell.players).to eq ["Mario"]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#location" do
|
27
|
+
it "returns location" do
|
28
|
+
expect(cell.location).to eq 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#players" do
|
33
|
+
it "returns an array" do
|
34
|
+
expect(cell.players).to be_an Array
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#enter" do
|
39
|
+
let(:mario) { double(:mario) }
|
40
|
+
|
41
|
+
it "updates player position and adds player to players array" do
|
42
|
+
expect(mario).to receive(:position=).with cell.location
|
43
|
+
cell.enter(mario, "Board")
|
44
|
+
expect(cell.players).to include mario
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#exit" do
|
49
|
+
let(:cell) { Cell.new(location: 1, players: ["mario"]) }
|
50
|
+
|
51
|
+
it "delete player from players array" do
|
52
|
+
cell.exit("mario")
|
53
|
+
expect(cell.players).not_to include "mario"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module SnakesAndLadders
|
4
|
+
describe Game do
|
5
|
+
let(:game) { Game.new(board: "Board") }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
it "raises an error without board" do
|
9
|
+
expect { Game.new }.to raise_error(ArgumentError).with_message("missing keyword: board")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "sets a board" do
|
13
|
+
expect(game.board).to eq "Board"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "defaults players to empty array" do
|
17
|
+
expect(game.players).to eq []
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets players array" do
|
21
|
+
game = Game.new(board: "Board", players: %w(Mario Luigi))
|
22
|
+
expect(game.players).to eq %w(Mario Luigi)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "defaults turn to 0" do
|
26
|
+
expect(game.turn).to eq 0
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets turn" do
|
30
|
+
game = Game.new(board: "Board", turn: 5)
|
31
|
+
expect(game.turn).to eq 5
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#board" do
|
36
|
+
it "returns game board" do
|
37
|
+
expect(game.board).to eq "Board"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#players" do
|
42
|
+
it "returns an array" do
|
43
|
+
expect(game.players).to be_an Array
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#turn" do
|
48
|
+
it "return an integer" do
|
49
|
+
expect(game.turn).to be_an Integer
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#play_turn" do
|
54
|
+
let(:mario) { double(:player, roll_die: 3, position: 1, destination_after_last_roll: 4, turns: 2, last_roll: 3) }
|
55
|
+
let(:luigi) { double(:player, roll_die: 0, position: 0, destination_after_last_roll: 0, turns: 0, last_roll: 0) }
|
56
|
+
let(:peach) { double(:player) }
|
57
|
+
|
58
|
+
let(:players) { [mario, luigi, peach] }
|
59
|
+
|
60
|
+
let(:board) { double(:board, size: 10, move: true) }
|
61
|
+
let(:game) { Game.new(board: board, players: players) }
|
62
|
+
|
63
|
+
it "defaults winner to nil" do
|
64
|
+
expect(game.winner).to be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
it "is not over" do
|
68
|
+
expect(game.over?).to eq false
|
69
|
+
end
|
70
|
+
|
71
|
+
it "cycles player turns" do
|
72
|
+
game.play_turn
|
73
|
+
expect(game.player).to eq mario
|
74
|
+
game.play_turn
|
75
|
+
expect(game.player).to eq luigi
|
76
|
+
end
|
77
|
+
|
78
|
+
it "calls player.roll_die" do
|
79
|
+
expect(mario).to receive(:roll_die)
|
80
|
+
game.play_turn
|
81
|
+
end
|
82
|
+
|
83
|
+
it "increments turn" do
|
84
|
+
game.play_turn
|
85
|
+
expect(game.turn).to eq 1
|
86
|
+
end
|
87
|
+
|
88
|
+
it "calls board.move" do
|
89
|
+
expect(board).to receive(:move).with(mario, 1, 4)
|
90
|
+
game.play_turn
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#simulate" do
|
95
|
+
let(:mario) { Player.new(name: "Mario", color: "Red") }
|
96
|
+
let(:luigi) { Player.new(name: "Luigi", color: "Green") }
|
97
|
+
|
98
|
+
let(:game) { Game.new(board: Board.new, players: [mario, luigi]) }
|
99
|
+
|
100
|
+
before { game.simulate }
|
101
|
+
|
102
|
+
it "simulates and entire game" do
|
103
|
+
expect(game.over?).to eq true
|
104
|
+
expect(game.players).to include game.winner
|
105
|
+
expect(game.play_turn).to be_nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#over?" do
|
110
|
+
# This depends on the position attribute to decide winner.
|
111
|
+
# This tests the "board.move returns true" scenario, so we must assume mario already moved, and his position was updated.
|
112
|
+
# This explains why position and destination_after_last_roll are equal, which happens when the player moves.
|
113
|
+
context "when player lands on last square" do
|
114
|
+
let(:board) { double(:board, size: 10, move: true) }
|
115
|
+
let(:mario) { double(:mario, roll_die: 1, position: 10, destination_after_last_roll: 10, turns: 1, last_roll: 1) }
|
116
|
+
let(:game) { Game.new(board: board, players: [mario]) }
|
117
|
+
|
118
|
+
it "returns true" do
|
119
|
+
game.play_turn
|
120
|
+
expect(game.over?).to be true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# This depends on destination_after_last_roll to decide winner.
|
125
|
+
# This tests the "board.move returns false" scenario. So mario's position does not get updated, and we must decide if his roll
|
126
|
+
# takes him over the winning line, since he can't move off the board.
|
127
|
+
context "when player roll will cause it to exceed last cell" do
|
128
|
+
let(:board) { double(:board, size: 10, move: false) }
|
129
|
+
let(:mario) { double(:mario, roll_die: 3, position: 9, destination_after_last_roll: 12, turns: 1, last_roll: 3) }
|
130
|
+
let(:game) { Game.new(board: board, players: [mario]) }
|
131
|
+
|
132
|
+
it "returns true" do
|
133
|
+
game.play_turn
|
134
|
+
expect(game.over?).to be true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "when player roll is not sufficient to win" do
|
139
|
+
let(:board) { double(:board, size: 10, move: true) }
|
140
|
+
let(:mario) { double(:mario, roll_die: 1, position: 8, destination_after_last_roll: 9, last_roll: 1) }
|
141
|
+
let(:game) { Game.new(board: board, players: [mario]) }
|
142
|
+
|
143
|
+
before { game.add_player(mario) }
|
144
|
+
|
145
|
+
it "returns false" do
|
146
|
+
game.play_turn
|
147
|
+
expect(game.over?).to be false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module SnakesAndLadders
|
4
|
+
describe Grid do
|
5
|
+
let(:grid) { Grid.new(size: 10) }
|
6
|
+
|
7
|
+
context "#initialize" do
|
8
|
+
it "raises an error without size" do
|
9
|
+
expect { Grid.new }.to raise_error(ArgumentError).with_message("missing keyword: size")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises an error if object mappings are greater than size" do
|
13
|
+
expect { Grid.new(size: 1, tile_mappings: [{ 1 => 1 }, { 2 => 2 }]) }.to raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets size" do
|
17
|
+
expect(grid.size).to eq 10
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets tile_mappings" do
|
21
|
+
tile_mappings = [{ princess: "peach" }]
|
22
|
+
grid = Grid.new(size: 10, tile_mappings: tile_mappings)
|
23
|
+
expect(grid.tile_mappings).to eq tile_mappings
|
24
|
+
end
|
25
|
+
|
26
|
+
it "defaults tile_mappings to an empty array" do
|
27
|
+
expect(grid.tile_mappings).to eq Array.new
|
28
|
+
end
|
29
|
+
|
30
|
+
it "sets default_tile" do
|
31
|
+
grid = Grid.new(size: 10, default_tile: :BowserCastle)
|
32
|
+
expect(grid.default_tile).to eq :BowserCastle
|
33
|
+
end
|
34
|
+
|
35
|
+
it "defaults default_tile to Cell" do
|
36
|
+
expect(grid.default_tile).to eq :Cell
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#size" do
|
41
|
+
it "returns an integer" do
|
42
|
+
expect(grid.size).to be_an Integer
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#tile_mappings" do
|
47
|
+
it "returns an array" do
|
48
|
+
expect(grid.tile_mappings).to be_an Array
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#default_tile" do
|
53
|
+
it "returns a symbol" do
|
54
|
+
expect(grid.default_tile).to be_an Symbol
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#build" do
|
59
|
+
let :tile_mappings do
|
60
|
+
[
|
61
|
+
{ class: :Portal, location: 1, destination: 3 },
|
62
|
+
{ class: :Portal, location: 4, destination: 2 },
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "builds a grid from mappings" do
|
67
|
+
grid = Grid.new(size: 4, tile_mappings: tile_mappings).build
|
68
|
+
expect(grid).to be_a Hash
|
69
|
+
|
70
|
+
expect(grid[1]).to be_a Portal
|
71
|
+
expect(grid[2]).to be_a Cell
|
72
|
+
expect(grid[3]).to be_a Cell
|
73
|
+
expect(grid[4]).to be_a Portal
|
74
|
+
|
75
|
+
expect(grid[1].location).to eq 1
|
76
|
+
expect(grid[2].location).to eq 2
|
77
|
+
expect(grid[3].location).to eq 3
|
78
|
+
expect(grid[4].location).to eq 4
|
79
|
+
|
80
|
+
expect(grid[1].destination).to eq 3
|
81
|
+
expect(grid[4].destination).to eq 2
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#build with custom default_tile" do
|
86
|
+
let :tile_mappings do
|
87
|
+
[ { class: :Portal, location: 1, destination: 3 } ]
|
88
|
+
end
|
89
|
+
|
90
|
+
let(:castle) { double(:Castle) }
|
91
|
+
|
92
|
+
it "builds a grid from mappings" do
|
93
|
+
skip "not sure how to test this since we have no custom class"
|
94
|
+
# grid = Grid.new(size: 3, tile_mappings: tile_mappings, default_tile: :Castle).build
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "CLASSIC_BOARD_MAPPINGS" do
|
99
|
+
it "should define classic board const" do
|
100
|
+
expect(Grid).to have_constant(:CLASSIC_BOARD_MAPPINGS)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe ".classic" do
|
105
|
+
skip "just a reminder to test this class method"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module SnakesAndLadders
|
4
|
+
describe Player do
|
5
|
+
let(:player) { Player.new(name: "Mario", color: "Red") }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
it "raises an error without name" do
|
9
|
+
expect { Player.new(color: "Red") }.to raise_error(ArgumentError).with_message("missing keyword: name")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises an error without color" do
|
13
|
+
expect { Player.new(name: "Mario") }.to raise_error(ArgumentError).with_message("missing keyword: color")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets player name" do
|
17
|
+
expect(player.name).to eq "Mario"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets player color" do
|
21
|
+
expect(player.color).to eq "Red"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "defaults die_rolls to an empty array" do
|
25
|
+
expect(player.die_rolls).to eq []
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets die_rolls" do
|
29
|
+
player = Player.new(name: "Mario", color: "Red", die_rolls: [1,2,3])
|
30
|
+
expect(player.die_rolls).to eq [1,2,3]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "defaults position to 0" do
|
34
|
+
expect(player.position).to eq 0
|
35
|
+
end
|
36
|
+
|
37
|
+
it "sets position" do
|
38
|
+
player = Player.new(name: "Mario", color: "Red", position: 5)
|
39
|
+
expect(player.position).to eq 5
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#name" do
|
44
|
+
it "returns player name" do
|
45
|
+
expect(player.name).to eq "Mario"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#color" do
|
50
|
+
it "returns player color" do
|
51
|
+
expect(player.color).to eq "Red"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#position" do
|
56
|
+
it "returns an integer" do
|
57
|
+
expect(player.position).to be_an Integer
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#position=" do
|
62
|
+
it "sets position" do
|
63
|
+
player.position = 5
|
64
|
+
expect(player.position).to eq 5
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#to_s" do
|
69
|
+
it "returns the name attribute" do
|
70
|
+
expect(player.to_s).to eq "Mario"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#roll_die" do
|
75
|
+
it "returns an integer" do
|
76
|
+
expect(player.roll_die).to be_an Integer
|
77
|
+
end
|
78
|
+
|
79
|
+
it "is greater than 0" do
|
80
|
+
expect(player.roll_die).to be > 0
|
81
|
+
end
|
82
|
+
|
83
|
+
it "is lesser than 7" do
|
84
|
+
expect(player.roll_die).to be < 7
|
85
|
+
end
|
86
|
+
|
87
|
+
it "records die roll" do
|
88
|
+
roll = player.roll_die
|
89
|
+
expect(player.die_rolls).to include roll
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#die_rolls" do
|
94
|
+
it "returns an array" do
|
95
|
+
expect(player.die_rolls).to be_an Array
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#turns" do
|
100
|
+
before do
|
101
|
+
3.times { player.roll_die }
|
102
|
+
end
|
103
|
+
|
104
|
+
it "returns the number of turns played" do
|
105
|
+
expect(player.turns).to eq 3
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#last_roll" do
|
110
|
+
it "returns the last die roll" do
|
111
|
+
roll = player.roll_die
|
112
|
+
expect(player.last_roll).to eq roll
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#destination_after_last_roll" do
|
117
|
+
it "returns last roll added to position" do
|
118
|
+
roll = player.roll_die
|
119
|
+
expect(player.destination_after_last_roll).to eq player.position + roll
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module SnakesAndLadders
|
4
|
+
describe Portal do
|
5
|
+
let(:portal) { Portal.new(location: 3, destination: 5) }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
it "raises an error without location" do
|
9
|
+
expect { Portal.new(destination: 1) }.to raise_error(ArgumentError).with_message("missing keyword: location")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises an error without destination" do
|
13
|
+
expect { Portal.new(location: 1) }.to raise_error(ArgumentError).with_message("missing keyword: destination")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets destination" do
|
17
|
+
expect(portal.destination).to eq 5
|
18
|
+
end
|
19
|
+
|
20
|
+
it "calls super and sets location" do
|
21
|
+
expect(portal.location).to eq 3
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises an error when location is equal to destination" do
|
25
|
+
expect { Portal.new(location: 1, destination: 1) }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#location" do
|
30
|
+
it "returns location" do
|
31
|
+
expect(portal.location).to eq 3
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#destination" do
|
36
|
+
it "returns destination" do
|
37
|
+
expect(portal.destination).to eq 5
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#enter" do
|
42
|
+
let(:board) { double(:board) }
|
43
|
+
|
44
|
+
it "moves player to cell corresponding to its destination" do
|
45
|
+
expect(board).to receive(:move).with("Mario", portal.location, portal.destination)
|
46
|
+
portal.enter("Mario", board)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#type" do
|
51
|
+
it "returns snake when destination is less than location" do
|
52
|
+
portal = Portal.new(location: 5, destination: 1)
|
53
|
+
expect(portal.type).to eq(:snake)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns ladder when location is less than destination" do
|
57
|
+
portal = Portal.new(location: 1, destination: 5)
|
58
|
+
expect(portal.type).to eq(:ladder)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "snakes_and_ladders"
|
2
|
+
require "support/matchers"
|
3
|
+
require "support/utils"
|
4
|
+
|
5
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
6
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
8
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
9
|
+
#
|
10
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
11
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
12
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
13
|
+
# individual file that may not need all of that loaded. Instead, make a
|
14
|
+
# separate helper file that requires this one and then use it only in the specs
|
15
|
+
# that actually need it.
|
16
|
+
#
|
17
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
18
|
+
# users commonly want.
|
19
|
+
#
|
20
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
21
|
+
RSpec.configure do |config|
|
22
|
+
# The settings below are suggested to provide a good initial experience
|
23
|
+
# with RSpec, but feel free to customize to your heart's content.
|
24
|
+
=begin
|
25
|
+
# These two settings work together to allow you to limit a spec run
|
26
|
+
# to individual examples or groups you care about by tagging them with
|
27
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
28
|
+
# get run.
|
29
|
+
config.filter_run :focus
|
30
|
+
config.run_all_when_everything_filtered = true
|
31
|
+
|
32
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
33
|
+
# file, and it's useful to allow more verbose output when running an
|
34
|
+
# individual spec file.
|
35
|
+
if config.files_to_run.one?
|
36
|
+
# Use the documentation formatter for detailed output,
|
37
|
+
# unless a formatter has already been configured
|
38
|
+
# (e.g. via a command-line flag).
|
39
|
+
config.default_formatter = 'doc'
|
40
|
+
end
|
41
|
+
|
42
|
+
# Print the 10 slowest examples and example groups at the
|
43
|
+
# end of the spec run, to help surface which specs are running
|
44
|
+
# particularly slow.
|
45
|
+
config.profile_examples = 10
|
46
|
+
|
47
|
+
# Run specs in random order to surface order dependencies. If you find an
|
48
|
+
# order dependency and want to debug it, you can fix the order by providing
|
49
|
+
# the seed, which is printed after each run.
|
50
|
+
# --seed 1234
|
51
|
+
config.order = :random
|
52
|
+
|
53
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
54
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
55
|
+
# test failures related to randomization by passing the same `--seed` value
|
56
|
+
# as the one that triggered the failure.
|
57
|
+
Kernel.srand config.seed
|
58
|
+
|
59
|
+
# rspec-expectations config goes here. You can use an alternate
|
60
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
61
|
+
# assertions if you prefer.
|
62
|
+
config.expect_with :rspec do |expectations|
|
63
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
64
|
+
# For more details, see:
|
65
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
66
|
+
expectations.syntax = :expect
|
67
|
+
end
|
68
|
+
|
69
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
70
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
71
|
+
config.mock_with :rspec do |mocks|
|
72
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
73
|
+
# For more details, see:
|
74
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
75
|
+
mocks.syntax = :expect
|
76
|
+
|
77
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
78
|
+
# a real object. This is generally recommended.
|
79
|
+
mocks.verify_partial_doubles = true
|
80
|
+
end
|
81
|
+
=end
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: snakes_and_ladders
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mohamad El-Husseini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A Ruby implementation of Snakes and Ladders (Chutes and Ladders) board
|
70
|
+
game.
|
71
|
+
email:
|
72
|
+
- husseini.mel@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- lib/snakes_and_ladders.rb
|
84
|
+
- lib/snakes_and_ladders/board.rb
|
85
|
+
- lib/snakes_and_ladders/cell.rb
|
86
|
+
- lib/snakes_and_ladders/game.rb
|
87
|
+
- lib/snakes_and_ladders/grid.rb
|
88
|
+
- lib/snakes_and_ladders/player.rb
|
89
|
+
- lib/snakes_and_ladders/portal.rb
|
90
|
+
- lib/snakes_and_ladders/version.rb
|
91
|
+
- snakes_and_ladders.gemspec
|
92
|
+
- spec/snakes_and_ladders/board_spec.rb
|
93
|
+
- spec/snakes_and_ladders/cell_spec.rb
|
94
|
+
- spec/snakes_and_ladders/game_spec.rb
|
95
|
+
- spec/snakes_and_ladders/grid_spec.rb
|
96
|
+
- spec/snakes_and_ladders/player_spec.rb
|
97
|
+
- spec/snakes_and_ladders/portal_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
- spec/support/matchers.rb
|
100
|
+
homepage: https://github.com/abitdodgy/snakes_and_ladders
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.2.2
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: This is an object oriented Ruby implementation of the classic Snakes and
|
124
|
+
Ladders (Chutes and Ladders) board game.
|
125
|
+
test_files:
|
126
|
+
- spec/snakes_and_ladders/board_spec.rb
|
127
|
+
- spec/snakes_and_ladders/cell_spec.rb
|
128
|
+
- spec/snakes_and_ladders/game_spec.rb
|
129
|
+
- spec/snakes_and_ladders/grid_spec.rb
|
130
|
+
- spec/snakes_and_ladders/player_spec.rb
|
131
|
+
- spec/snakes_and_ladders/portal_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/support/matchers.rb
|