nerdword 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/board.rb +102 -0
- data/lib/direction.rb +12 -0
- data/lib/move.rb +21 -0
- data/lib/not_random.rb +5 -0
- data/lib/player.rb +36 -0
- data/lib/position.rb +38 -0
- data/lib/pouch.rb +23 -0
- data/spec/board_spec.rb +118 -0
- data/spec/game_spec.rb +164 -0
- data/spec/player_spec.rb +68 -0
- data/spec/pouch_spec.rb +22 -0
- metadata +79 -0
data/lib/board.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
class Board
|
2
|
+
def initialize(values, letter_multipliers = {}, word_multipliers = {})
|
3
|
+
@values = values
|
4
|
+
@letter_multipliers = letter_multipliers
|
5
|
+
@word_multipliers = word_multipliers
|
6
|
+
@history = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def play(move)
|
10
|
+
index = generate_index(@history)
|
11
|
+
moves = find_moves(move, index)
|
12
|
+
@history << move
|
13
|
+
score = moves.inject(0) { |score, move| score + score_move(move, index) }
|
14
|
+
tiles_used = calculate_tiles_used(move, index)
|
15
|
+
if tiles_used.length == 7
|
16
|
+
score += 50
|
17
|
+
end
|
18
|
+
[score, tiles_used]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def find_moves(move, index)
|
24
|
+
moves = [move]
|
25
|
+
|
26
|
+
direction = move.direction
|
27
|
+
orthogonal_direction = Direction.opposite(direction)
|
28
|
+
|
29
|
+
move.word.length.times do |i|
|
30
|
+
current = move.position.shift(i, direction)
|
31
|
+
next if index[current] # not placed by us, so don't look for adjacent letters
|
32
|
+
|
33
|
+
behind = current.previous(orthogonal_direction)
|
34
|
+
ahead = current.next(orthogonal_direction)
|
35
|
+
|
36
|
+
if index[behind] || index[ahead]
|
37
|
+
total_index = generate_index(@history + [move])
|
38
|
+
move_start = find_move_start(current, orthogonal_direction, total_index)
|
39
|
+
prefix = Move.new(total_index[move_start], move_start, orthogonal_direction)
|
40
|
+
moves << find_move(prefix, total_index)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
moves
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_move_start(position, direction, index)
|
48
|
+
prev = position.previous(direction)
|
49
|
+
|
50
|
+
if index[prev]
|
51
|
+
find_move_start(prev, direction, index)
|
52
|
+
else
|
53
|
+
position
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_move(prefix, index)
|
58
|
+
ahead = prefix.position.shift(prefix.word.length, prefix.direction)
|
59
|
+
|
60
|
+
if index[ahead]
|
61
|
+
find_move(Move.new(prefix.word + index[ahead], prefix.position, prefix.direction), index)
|
62
|
+
else
|
63
|
+
prefix
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_index(history)
|
68
|
+
index = {}
|
69
|
+
|
70
|
+
history.each do |move|
|
71
|
+
move.word.each_char.with_index do |char, i|
|
72
|
+
index[move.position.shift(i, move.direction)] = move.word[i]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
index
|
77
|
+
end
|
78
|
+
|
79
|
+
def score_move(move, index)
|
80
|
+
word_multiplier = 1
|
81
|
+
score = move.word.each_char.with_index.inject(0) do |score, (c, i)|
|
82
|
+
pos = move.position.shift(i, move.direction)
|
83
|
+
if index[pos]
|
84
|
+
score + @values[c]
|
85
|
+
else
|
86
|
+
word_multiplier *= @word_multipliers.fetch(pos, 1)
|
87
|
+
score + @values[c] * @letter_multipliers.fetch(pos, 1)
|
88
|
+
end
|
89
|
+
end * word_multiplier
|
90
|
+
score
|
91
|
+
end
|
92
|
+
|
93
|
+
def calculate_tiles_used(move, index)
|
94
|
+
used = []
|
95
|
+
move.word.each_char.with_index do |char, i|
|
96
|
+
next if index[move.position.shift(i, move.direction)]
|
97
|
+
used << char
|
98
|
+
end
|
99
|
+
|
100
|
+
used
|
101
|
+
end
|
102
|
+
end
|
data/lib/direction.rb
ADDED
data/lib/move.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class Move
|
2
|
+
attr_reader :word, :position, :direction
|
3
|
+
|
4
|
+
def initialize(word, position, direction)
|
5
|
+
@word = word
|
6
|
+
@position = position
|
7
|
+
@direction = direction
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
[word, position, direction] == [other.word, other.position, other.direction]
|
12
|
+
end
|
13
|
+
|
14
|
+
def eql?(other)
|
15
|
+
[word, position, direction].eql?[other.word, other.position, other.direction]
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash
|
19
|
+
[word, position, direction].hash
|
20
|
+
end
|
21
|
+
end
|
data/lib/not_random.rb
ADDED
data/lib/player.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
class Player
|
2
|
+
def initialize(board, pouch, rack = [])
|
3
|
+
@board = board
|
4
|
+
@pouch = pouch
|
5
|
+
@score = 0
|
6
|
+
@rack = rack
|
7
|
+
end
|
8
|
+
|
9
|
+
def play(move)
|
10
|
+
score, tiles_used = @board.play(move)
|
11
|
+
@score += score
|
12
|
+
remove_tiles(tiles_used)
|
13
|
+
end
|
14
|
+
|
15
|
+
def score
|
16
|
+
@score
|
17
|
+
end
|
18
|
+
|
19
|
+
def draw
|
20
|
+
need = 7 - @rack.length
|
21
|
+
@rack.concat(@pouch.draw(need))
|
22
|
+
end
|
23
|
+
|
24
|
+
def exchange(tiles)
|
25
|
+
remove_tiles(tiles)
|
26
|
+
@rack.concat(@pouch.exchange(tiles))
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def remove_tiles(tiles)
|
32
|
+
tiles.each do |tile|
|
33
|
+
@rack.slice!(@rack.index(tile))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/position.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "direction"
|
2
|
+
|
3
|
+
class Position
|
4
|
+
attr_reader :col, :row
|
5
|
+
|
6
|
+
def initialize(col, row)
|
7
|
+
@col = col
|
8
|
+
@row = row
|
9
|
+
end
|
10
|
+
|
11
|
+
def shift(offset, direction)
|
12
|
+
if direction == Direction::HORIZONTAL
|
13
|
+
Position.new(col + offset, row)
|
14
|
+
else
|
15
|
+
Position.new(col, row + offset)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def previous(direction)
|
20
|
+
shift(-1, direction)
|
21
|
+
end
|
22
|
+
|
23
|
+
def next(direction)
|
24
|
+
shift(1, direction)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
[col, row] == [other.col, other.row]
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other)
|
32
|
+
[col, row].eql?([other.col, other.row])
|
33
|
+
end
|
34
|
+
|
35
|
+
def hash
|
36
|
+
[col, row].hash
|
37
|
+
end
|
38
|
+
end
|
data/lib/pouch.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class Pouch
|
2
|
+
def initialize(tiles, rng = Random.new)
|
3
|
+
@tiles = tiles
|
4
|
+
@rng = rng
|
5
|
+
end
|
6
|
+
|
7
|
+
def draw(num)
|
8
|
+
shuffle
|
9
|
+
@tiles.shift(num)
|
10
|
+
end
|
11
|
+
|
12
|
+
def exchange(tiles)
|
13
|
+
new_tiles = draw(tiles.length)
|
14
|
+
@tiles.concat(tiles)
|
15
|
+
new_tiles
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def shuffle
|
21
|
+
@tiles.sort_by! { @rng.rand }
|
22
|
+
end
|
23
|
+
end
|
data/spec/board_spec.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require "board"
|
2
|
+
require "move"
|
3
|
+
require "position"
|
4
|
+
|
5
|
+
describe Board do
|
6
|
+
it "scores a move" do
|
7
|
+
values = { ?C => 2, ?A => 4, ?T => 8 }
|
8
|
+
board = Board.new(values)
|
9
|
+
|
10
|
+
score, _ = board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
11
|
+
|
12
|
+
score.should eq(14)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "scores a multi-word move" do
|
16
|
+
values = { ?C => 4, ?A => 1, ?T => 1, ?I => 1, ?N => 2 }
|
17
|
+
board = Board.new(values)
|
18
|
+
|
19
|
+
board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
20
|
+
score, _ = board.play(Move.new("TIN", Position.new(1, 1), Direction::HORIZONTAL))
|
21
|
+
|
22
|
+
score.should eq(8)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns the tiles used to make the play" do
|
26
|
+
values = { ?C => 1, ?A => 4, ?T => 8, ?S => 1 }
|
27
|
+
board = Board.new(values)
|
28
|
+
|
29
|
+
_, tiles_used1 = board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
30
|
+
_, tiles_used2 = board.play(Move.new("CATS", Position.new(0, 0), Direction::HORIZONTAL))
|
31
|
+
|
32
|
+
tiles_used1.should eq(%w{C A T})
|
33
|
+
tiles_used2.should eq(%w{S})
|
34
|
+
end
|
35
|
+
|
36
|
+
it "scores letter multipliers" do
|
37
|
+
values = { ?C => 2, ?A => 4, ?T => 8 }
|
38
|
+
letter_multipliers = { Position.new(0, 0) => 2 }
|
39
|
+
board = Board.new(values, letter_multipliers)
|
40
|
+
|
41
|
+
score, _ = board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
42
|
+
|
43
|
+
score.should eq(16)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "scores letter multipliers in every word formed" do
|
47
|
+
values = Hash.new(1)
|
48
|
+
letter_multipliers = { Position.new(1, 1) => 2 }
|
49
|
+
board = Board.new(values, letter_multipliers)
|
50
|
+
|
51
|
+
board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
52
|
+
score, _ = board.play(Move.new("TIN", Position.new(1, 1), Direction::HORIZONTAL))
|
53
|
+
|
54
|
+
score.should eq(9)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "only counts letter multipliers played this turn" do
|
58
|
+
values = { ?C => 2, ?A => 4, ?T => 8, ?S => 1 }
|
59
|
+
letter_multipliers = { Position.new(0, 0) => 2 }
|
60
|
+
board = Board.new(values, letter_multipliers)
|
61
|
+
|
62
|
+
board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
63
|
+
score, _ = board.play(Move.new("CATS", Position.new(0, 0), Direction::HORIZONTAL))
|
64
|
+
|
65
|
+
score.should eq(15)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "scores word multipliers" do
|
69
|
+
values = { ?C => 2, ?A => 4, ?T => 8 }
|
70
|
+
word_multipliers = { Position.new(0, 0) => 2 }
|
71
|
+
board = Board.new(values, {}, word_multipliers)
|
72
|
+
|
73
|
+
score, _ = board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
74
|
+
|
75
|
+
score.should eq(28)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "scores word multipliers in every word formed" do
|
79
|
+
values = Hash.new(1)
|
80
|
+
word_multipliers = { Position.new(1, 1) => 2 }
|
81
|
+
board = Board.new(values, {}, word_multipliers)
|
82
|
+
|
83
|
+
board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
84
|
+
score, _ = board.play(Move.new("TIN", Position.new(1, 1), Direction::HORIZONTAL))
|
85
|
+
|
86
|
+
score.should eq(12)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "doesn't count word multipliers twice" do
|
90
|
+
values = { ?C => 2, ?A => 4, ?T => 8, ?S => 1 }
|
91
|
+
word_multipliers = { Position.new(0, 0) => 2 }
|
92
|
+
board = Board.new(values, {}, word_multipliers)
|
93
|
+
|
94
|
+
board.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
95
|
+
score, _ = board.play(Move.new("CATS", Position.new(0, 0), Direction::HORIZONTAL))
|
96
|
+
|
97
|
+
score.should eq(15)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "scores a 50 point bingo bonus" do
|
101
|
+
values = Hash.new(1)
|
102
|
+
board = Board.new(values)
|
103
|
+
|
104
|
+
score, _ = board.play(Move.new("RAMRODS", Position.new(0, 0), Direction::HORIZONTAL))
|
105
|
+
|
106
|
+
score.should eq(57)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "only gives bingo bonus if 7 tiles are used" do
|
110
|
+
values = Hash.new(1)
|
111
|
+
board = Board.new(values)
|
112
|
+
|
113
|
+
board.play(Move.new("RAM", Position.new(0, 0), Direction::HORIZONTAL))
|
114
|
+
score, _ = board.play(Move.new("RAMRODS", Position.new(1, 0), Direction::HORIZONTAL))
|
115
|
+
|
116
|
+
score.should eq(7)
|
117
|
+
end
|
118
|
+
end
|
data/spec/game_spec.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require "player"
|
2
|
+
require "board"
|
3
|
+
require "move"
|
4
|
+
require "position"
|
5
|
+
require "pouch"
|
6
|
+
require "not_random"
|
7
|
+
|
8
|
+
describe "Game" do
|
9
|
+
def positions_for(*rows)
|
10
|
+
positions = {}
|
11
|
+
rows.each.with_index do |row, i|
|
12
|
+
row.each_char.with_index do |col, j|
|
13
|
+
unless col == " "
|
14
|
+
positions[Position.new(j, i)] = col.to_i
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
positions
|
19
|
+
end
|
20
|
+
|
21
|
+
# View Game: http://www.scrabble-assoc.com/games/nsc2000/1
|
22
|
+
# The game has fUZES scored incorrectly, but we get it right.
|
23
|
+
it "plays a whole game" do
|
24
|
+
tiles = %w{
|
25
|
+
G I N O O T T
|
26
|
+
A E O Q R S U
|
27
|
+
A I M N O T U
|
28
|
+
A E F G I T O
|
29
|
+
D E E I L P V
|
30
|
+
A A B L Y
|
31
|
+
A A D D O
|
32
|
+
E E R
|
33
|
+
A C E H U
|
34
|
+
F I P R W
|
35
|
+
M O U
|
36
|
+
E I J L N
|
37
|
+
E S
|
38
|
+
D I N O
|
39
|
+
H R Z
|
40
|
+
A C E K T T W
|
41
|
+
f I S
|
42
|
+
a N B
|
43
|
+
E G N X
|
44
|
+
L R V
|
45
|
+
R S Y
|
46
|
+
E I
|
47
|
+
}
|
48
|
+
|
49
|
+
values = {
|
50
|
+
?A => 1,
|
51
|
+
?B => 3,
|
52
|
+
?C => 3,
|
53
|
+
?D => 2,
|
54
|
+
?E => 1,
|
55
|
+
?F => 4,
|
56
|
+
?G => 2,
|
57
|
+
?H => 4,
|
58
|
+
?I => 1,
|
59
|
+
?J => 8,
|
60
|
+
?K => 5,
|
61
|
+
?L => 1,
|
62
|
+
?M => 3,
|
63
|
+
?N => 1,
|
64
|
+
?O => 1,
|
65
|
+
?P => 3,
|
66
|
+
?Q => 10,
|
67
|
+
?R => 1,
|
68
|
+
?S => 1,
|
69
|
+
?T => 1,
|
70
|
+
?U => 1,
|
71
|
+
?V => 4,
|
72
|
+
?W => 4,
|
73
|
+
?X => 8,
|
74
|
+
?Y => 4,
|
75
|
+
?Z => 10,
|
76
|
+
?a => 0,
|
77
|
+
?f => 0
|
78
|
+
}
|
79
|
+
|
80
|
+
letter_multipliers = positions_for(
|
81
|
+
" 2 2 ",
|
82
|
+
" 3 3 ",
|
83
|
+
" 2 2 ",
|
84
|
+
"2 2 2",
|
85
|
+
" ",
|
86
|
+
" 3 3 3 3 ",
|
87
|
+
" 2 2 2 2 ",
|
88
|
+
" 2 2 ",
|
89
|
+
" 2 2 2 2 ",
|
90
|
+
" 3 3 3 3 ",
|
91
|
+
" ",
|
92
|
+
"2 2 2",
|
93
|
+
" 2 2 ",
|
94
|
+
" 3 3 ",
|
95
|
+
" 2 2 "
|
96
|
+
)
|
97
|
+
|
98
|
+
word_multipliers = positions_for(
|
99
|
+
"3 3 3",
|
100
|
+
" 2 2 ",
|
101
|
+
" 2 2 ",
|
102
|
+
" 2 2 ",
|
103
|
+
" 2 2 ",
|
104
|
+
" ",
|
105
|
+
" ",
|
106
|
+
"3 2 3",
|
107
|
+
" ",
|
108
|
+
" ",
|
109
|
+
" 2 2 ",
|
110
|
+
" 2 2 ",
|
111
|
+
" 2 2 ",
|
112
|
+
" 2 2 ",
|
113
|
+
"3 3 3"
|
114
|
+
)
|
115
|
+
|
116
|
+
shuffle = NotRandom.new
|
117
|
+
board = Board.new(values, letter_multipliers, word_multipliers)
|
118
|
+
pouch = Pouch.new(tiles, shuffle)
|
119
|
+
|
120
|
+
p1_rack = []
|
121
|
+
p2_rack = []
|
122
|
+
p1 = Player.new(board, pouch, p1_rack)
|
123
|
+
p2 = Player.new(board, pouch, p2_rack)
|
124
|
+
|
125
|
+
moves = [
|
126
|
+
Move.new("TOOTING", Position.new(3, 7), Direction::HORIZONTAL),
|
127
|
+
Move.new("EQUATORS", Position.new(3, 3), Direction::VERTICAL),
|
128
|
+
Move.new("MOUNTAIN", Position.new(8, 4), Direction::VERTICAL),
|
129
|
+
Move.new("FOGIE", Position.new(7, 0), Direction::VERTICAL),
|
130
|
+
Move.new("LIVED", Position.new(7, 10), Direction::VERTICAL),
|
131
|
+
Move.new("BAA", Position.new(2, 8), Direction::VERTICAL),
|
132
|
+
Move.new("DOPED", Position.new(9, 1), Direction::VERTICAL),
|
133
|
+
Move.new("RELAY", Position.new(1, 5), Direction::VERTICAL),
|
134
|
+
Move.new("AHA", Position.new(0, 5), Direction::VERTICAL),
|
135
|
+
Move.new("PEWIT", Position.new(4, 10), Direction::VERTICAL),
|
136
|
+
Move.new("QUA", Position.new(3, 4), Direction::HORIZONTAL),
|
137
|
+
Move.new("FLINT", Position.new(0, 14), Direction::HORIZONTAL),
|
138
|
+
Move.new("MEOW", Position.new(1, 12), Direction::HORIZONTAL),
|
139
|
+
Move.new("REJOINED", Position.new(6, 13), Direction::HORIZONTAL),
|
140
|
+
Move.new("CHEZ", Position.new(12, 11), Direction::VERTICAL),
|
141
|
+
Move.new("WACKO", Position.new(5, 3), Direction::VERTICAL),
|
142
|
+
Move.new("fUZES", Position.new(10, 14), Direction::HORIZONTAL),
|
143
|
+
Move.new("BEN", Position.new(9, 9), Direction::VERTICAL),
|
144
|
+
Move.new("SEX", Position.new(10, 4), Direction::VERTICAL),
|
145
|
+
Move.new("VAR", Position.new(10, 8), Direction::VERTICAL),
|
146
|
+
Move.new("GYRO", Position.new(4, 1), Direction::HORIZONTAL),
|
147
|
+
Move.new("DEaLT", Position.new(9, 1), Direction::HORIZONTAL),
|
148
|
+
Move.new("SIR", Position.new(11, 5), Direction::VERTICAL),
|
149
|
+
Move.new("IT", Position.new(13, 0), Direction::HORIZONTAL)
|
150
|
+
]
|
151
|
+
|
152
|
+
moves.each_slice(2) do |move1, move2|
|
153
|
+
p1.draw
|
154
|
+
p2.draw
|
155
|
+
p1.play(move1)
|
156
|
+
p2.play(move2)
|
157
|
+
end
|
158
|
+
|
159
|
+
p1_unplayed = values.values_at(*p1_rack).inject(0, :+)
|
160
|
+
|
161
|
+
p1.score.should eq(422)
|
162
|
+
(p2.score + p1_unplayed * 2).should eq(409)
|
163
|
+
end
|
164
|
+
end
|
data/spec/player_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require "player"
|
2
|
+
require "move"
|
3
|
+
require "position"
|
4
|
+
|
5
|
+
describe Player do
|
6
|
+
let(:board) { mock }
|
7
|
+
let(:pouch) { mock }
|
8
|
+
|
9
|
+
it "draws tiles from the pouch" do
|
10
|
+
rack = []
|
11
|
+
player = Player.new(board, pouch, rack)
|
12
|
+
|
13
|
+
pouch.stub(:draw).with(7).and_return(%w{A B C})
|
14
|
+
player.draw
|
15
|
+
|
16
|
+
rack.should eq(%w{A B C})
|
17
|
+
end
|
18
|
+
|
19
|
+
it "only draws the tiles needed" do
|
20
|
+
rack = %w{A B C}
|
21
|
+
player = Player.new(board, pouch, rack)
|
22
|
+
|
23
|
+
pouch.stub(:draw).with(4).and_return(%w{D E F})
|
24
|
+
player.draw
|
25
|
+
|
26
|
+
rack.should eq(%w{A B C D E F})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "exchanges tiles with new ones from the pouch" do
|
30
|
+
rack = %w{A B C D E F G}
|
31
|
+
player = Player.new(board, pouch, rack)
|
32
|
+
|
33
|
+
pouch.stub(:exchange).with(%w{A B C}).and_return(%w{X Y Z})
|
34
|
+
player.exchange(%w{A B C})
|
35
|
+
|
36
|
+
rack.should eq(%w{D E F G X Y Z})
|
37
|
+
end
|
38
|
+
|
39
|
+
it "uses up tiles to make a move" do
|
40
|
+
rack = %w{A B C D E}
|
41
|
+
player = Player.new(board, pouch, rack)
|
42
|
+
|
43
|
+
board.stub(:play).and_return([1, %w{A C E}])
|
44
|
+
player.play(Move.new("", Position.new(0, 0), Direction::HORIZONTAL))
|
45
|
+
|
46
|
+
rack.should eq(%w{B D})
|
47
|
+
end
|
48
|
+
|
49
|
+
it "records her score" do
|
50
|
+
player = Player.new(board, pouch)
|
51
|
+
move = Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL)
|
52
|
+
board.stub(:play).with(move).and_return([1, []])
|
53
|
+
|
54
|
+
player.play(move)
|
55
|
+
|
56
|
+
player.score.should eq(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "add the scores of multiple plays" do
|
60
|
+
player = Player.new(board, pouch)
|
61
|
+
board.stub(:play).and_return([1, []])
|
62
|
+
|
63
|
+
player.play(Move.new("CAT", Position.new(0, 0), Direction::HORIZONTAL))
|
64
|
+
player.play(Move.new("COT", Position.new(0, 0), Direction::VERTICAL))
|
65
|
+
|
66
|
+
player.score.should eq(2)
|
67
|
+
end
|
68
|
+
end
|
data/spec/pouch_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "pouch"
|
2
|
+
require "not_random"
|
3
|
+
|
4
|
+
describe Pouch do
|
5
|
+
it "allows players to draw tiles" do
|
6
|
+
pouch = Pouch.new(%w{A B C D E F G}, NotRandom.new)
|
7
|
+
|
8
|
+
draw1 = pouch.draw(4)
|
9
|
+
draw2 = pouch.draw(4)
|
10
|
+
|
11
|
+
draw1.should eq(%w{A B C D})
|
12
|
+
draw2.should eq(%w{E F G})
|
13
|
+
end
|
14
|
+
|
15
|
+
it "allows players to exchange tiles" do
|
16
|
+
tiles = %w{A B C D E F G}
|
17
|
+
pouch = Pouch.new(tiles, NotRandom.new)
|
18
|
+
|
19
|
+
pouch.exchange(%w{X Y Z}).should eq(%w{A B C})
|
20
|
+
tiles.should eq(%w{D E F G X Y Z})
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nerdword
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tom von Schwerdtner
|
9
|
+
- Nestor Walker
|
10
|
+
- Eric Ostrich
|
11
|
+
- Sam Goldman
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
date: 2012-12-14 00:00:00.000000000 Z
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: rspec
|
19
|
+
requirement: !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ! '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
type: :development
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
description: Play crossword games with your friends.
|
34
|
+
email:
|
35
|
+
- dev@smartlogicsolutions.com
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/board.rb
|
41
|
+
- lib/direction.rb
|
42
|
+
- lib/move.rb
|
43
|
+
- lib/not_random.rb
|
44
|
+
- lib/player.rb
|
45
|
+
- lib/position.rb
|
46
|
+
- lib/pouch.rb
|
47
|
+
- spec/board_spec.rb
|
48
|
+
- spec/game_spec.rb
|
49
|
+
- spec/player_spec.rb
|
50
|
+
- spec/pouch_spec.rb
|
51
|
+
homepage: http://github.com/smartlogic/nerdword
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.8.23
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Get nerdy... with words!
|
75
|
+
test_files:
|
76
|
+
- spec/board_spec.rb
|
77
|
+
- spec/game_spec.rb
|
78
|
+
- spec/player_spec.rb
|
79
|
+
- spec/pouch_spec.rb
|