ace_of_spades 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ class AceOfSpades::Suit
2
+ include Comparable
3
+ attr_accessor :card
4
+
5
+ def initialize(suit)
6
+ @symbol = Suit.parse(suit)
7
+ raise "Invalid argument: #{suit}" unless @symbol
8
+ end
9
+
10
+ def <=>(suit)
11
+ return nil unless suit.instance_of?(Suit)
12
+ SUITS.index(@symbol) <=> SUITS.index(suit.to_sym)
13
+ end
14
+
15
+ def to_sym
16
+ @symbol
17
+ end
18
+
19
+ def to_s
20
+ @symbol.to_s
21
+ end
22
+ alias_method :to_str, :to_s
23
+
24
+ def method_missing(method, *args, &block)
25
+ match = Suit.parse(%r(.+\?\z).match(method).to_s.chop)
26
+ return super unless match
27
+ @symbol == match
28
+ end
29
+
30
+ class << self
31
+ def suitable?(suit)
32
+ !!parse(suit)
33
+ end
34
+
35
+ def wrap(suit)
36
+ return suit if suit.instance_of?(Suit)
37
+ new(suit) if suitable?(suit)
38
+ end
39
+
40
+ def parse(arg)
41
+ regex = arg.to_s.length == 1 ? %r(\A#{arg})i : %r(\A#{arg}(s*)\z)i
42
+ SUITS.grep(regex).first
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ class AceOfSpades::Value
2
+ include Comparable
3
+ attr_accessor :card
4
+
5
+ def initialize(value)
6
+ @symbol = Value.parse(value)
7
+ raise "Invalid argument: #{value}" unless @symbol
8
+ end
9
+
10
+ def <=>(value)
11
+ return nil unless value.instance_of?(Value)
12
+ to_i <=> value.to_i
13
+ end
14
+
15
+ def to_sym
16
+ @symbol
17
+ end
18
+
19
+ def to_s
20
+ @symbol.to_s
21
+ end
22
+ alias_method :to_str, :to_s
23
+
24
+ def to_i
25
+ aces_high? ? VALUES.rindex(@symbol) + 1 : VALUES.index(@symbol) + 1
26
+ end#
27
+ alias_method :to_int, :to_i
28
+
29
+ def aces_high?
30
+ return true unless card
31
+ card.aces_high?
32
+ end
33
+
34
+ def method_missing(method, *args, &block)
35
+ match = Value.parse(%r(.+\?\z).match(method).to_s.chop)
36
+ return super unless match
37
+ @symbol == match
38
+ end
39
+
40
+ class << self
41
+ def valuable?(value)
42
+ !!parse(value)
43
+ end
44
+
45
+ def wrap(value)
46
+ return value if value.instance_of?(Value)
47
+ new(value) if valuable?(value)
48
+ end
49
+
50
+ def parse(arg)
51
+ return VALUES[arg - 1] if (1..14).include?(arg)
52
+ short_arg = arg.to_s.length == 1
53
+ regex = short_arg ? %r(\A#{arg})i : %r(\A#{arg}\z)i
54
+ values = short_arg ? VALUES[-4..-1] : VALUES
55
+ values.grep(regex).first
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,294 @@
1
+ require "spec_helper"
2
+
3
+ describe Card do
4
+ let(:ace_spades) { Card.new(:ace, :spades) }
5
+ let(:joker) { Card.new(:joker) }
6
+
7
+ describe '.new' do
8
+ context "when called with valid arguments" do
9
+ let(:ten_hearts) { Card.new(10, 'h') }
10
+ it "initializes" do
11
+ expect(ace_spades).to be_an_instance_of(Card)
12
+ expect(ten_hearts).to be_an_instance_of(Card)
13
+ end
14
+ it "correctly saves the suit" do
15
+ expect(ace_spades.suit.to_sym).to be(:Spades)
16
+ expect(ten_hearts.suit.to_sym).to be(:Hearts)
17
+ end
18
+ it "correctly saves the value" do
19
+ expect(ace_spades.value.to_sym).to be(:Ace)
20
+ expect(ten_hearts.value.to_sym).to be(:Ten)
21
+ end
22
+ it "saves self as value.card and suit.card" do
23
+ expect(ace_spades.value.card).to be(ace_spades)
24
+ expect(ace_spades.suit.card).to be(ace_spades)
25
+ end
26
+ context "when a joker" do
27
+ it "initializes as a joker" do
28
+ expect(joker.suit).to be_nil
29
+ expect(joker.value).to be_nil
30
+ end
31
+ end
32
+ end
33
+ context "when initialized with invalid arguments" do
34
+ it "raises an error" do
35
+ expect{Card.new}.to raise_error
36
+ expect{Card.new(:joker, :foo)}.to raise_error
37
+ expect{Card.new(:ace, :foo)}.to raise_error
38
+ expect{Card.new('a', 5)}.to raise_error
39
+ expect{Card.new(:foo)}.to raise_error
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '.wrap' do
45
+ context "when passed a Card instance" do
46
+ it "returns the card instance" do
47
+ expect(Card.wrap(joker)).to be(joker)
48
+ end
49
+ end
50
+ context "when passed arguments that can be converted into a Card instance" do
51
+ it "returns the arguments as a Card instance" do
52
+ expect(Card.wrap(:ace, :spades)).to eq(ace_spades)
53
+ end
54
+ end
55
+ context "when passed arguments that cannot me converted into a Card instance" do
56
+ it "returns nil" do
57
+ expect(Card.wrap(:foo)).to be_nil
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#value=' do
63
+ let(:card) { ace_spades }
64
+ context "when passed a Value instance" do
65
+ it "assigns the Value" do
66
+ card.value = Value.new(:king)
67
+ expect(card.value).to be_an_instance_of(Value)
68
+ expect(card.value.to_sym).to be(:King)
69
+ end
70
+ end
71
+ context "when passed an object that can be converted to a Value" do
72
+ it "assigns the Value" do
73
+ card.value = :king
74
+ expect(card.value).to be_an_instance_of(Value)
75
+ expect(card.value.to_sym).to be(:King)
76
+ end
77
+ end
78
+ context "when passed an object that cannot be converted to a Value" do
79
+ it "assigns nil" do
80
+ card.value = :foo
81
+ expect(card.value).to be_nil
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#suit=' do
87
+ let(:card) { ace_spades }
88
+ context "when passed a Suit instance" do
89
+ it "assigns the Suit" do
90
+ card.suit = Suit.new(:hearts)
91
+ expect(card.suit).to be_an_instance_of(Suit)
92
+ expect(card.suit.to_sym).to be(:Hearts)
93
+ end
94
+ end
95
+ context "when passed an object that can be converted to a Suit" do
96
+ it "assigns the Suit" do
97
+ card.suit = :hearts
98
+ expect(card.suit).to be_an_instance_of(Suit)
99
+ expect(card.suit.to_sym).to be(:Hearts)
100
+ end
101
+ end
102
+ context "when passed an object that cannot be converted to a Suit" do
103
+ it "assigns nil" do
104
+ card.suit = :foo
105
+ expect(card.suit).to be_nil
106
+ end
107
+ end
108
+ end
109
+
110
+ describe '#deck & #deck=' do
111
+ let(:deck) { Deck.new }
112
+ let(:card) { ace_spades }
113
+ it "retrieves the deck" do
114
+ expect(card.deck).to be_nil
115
+ end
116
+ it "assigns the deck" do
117
+ card.deck = deck
118
+ expect(card.deck).to be(deck)
119
+ end
120
+ end
121
+
122
+ describe '#aces_high?' do
123
+ let(:deck) { Deck.new(aces_high: true) }
124
+ let(:card) { ace_spades }
125
+ it "returns the #aces_high? result from the deck" do
126
+ card.deck = deck
127
+ expect(card.aces_high?).to be(true)
128
+ deck.aces_high = false
129
+ expect(card.aces_high?).to be(false)
130
+ end
131
+ context "when the card has no deck" do
132
+ it "returns true" do
133
+ expect(card.aces_high?).to be(true)
134
+ end
135
+ end
136
+ end
137
+
138
+ describe'#<=>' do
139
+ let(:ace_hearts) { Card.new(14, :H) }
140
+ let(:king_spades) { Card.new(13, :S) }
141
+ let(:king_hearts) { Card.new(13, :H) }
142
+ context "when passed an incomparable object" do
143
+ it "returns nil" do
144
+ expect(ace_spades <=> 5).to be_nil
145
+ end
146
+ end
147
+ context "when passed a card with the same value and suit" do
148
+ let(:ace_of_spades) { Card.new(14, :S) }
149
+ it "returns 0" do
150
+ expect(ace_spades <=> ace_of_spades).to be(0)
151
+ end
152
+ end
153
+ context "when passed a card with a higher suit" do
154
+ it "returns -1" do
155
+ expect(ace_hearts <=> ace_spades).to be(-1)
156
+ end
157
+ end
158
+ context "when passed a card with a lower suit" do
159
+ it "returns 1" do
160
+ expect(ace_spades <=> ace_hearts).to be(1)
161
+ end
162
+ end
163
+ context "when passed a card with a higher suit and a lower value" do
164
+ it "returns -1" do
165
+ expect(ace_hearts <=> king_spades).to be(-1)
166
+ end
167
+ end
168
+ context "when passed a card with a lower suit and a higher value" do
169
+ it "returns 1" do
170
+ expect(king_spades <=> ace_hearts).to be(1)
171
+ end
172
+ end
173
+ context "when passed a joker" do
174
+ it "returns nil" do
175
+ expect(ace_spades <=> joker).to be_nil
176
+ end
177
+ end
178
+ describe Comparable do
179
+ it "compares correctly" do
180
+ expect(ace_spades == ace_spades).to be(true)
181
+ expect(ace_hearts < ace_spades).to be(true)
182
+ expect(ace_spades > ace_hearts).to be(true)
183
+ end
184
+ end
185
+ end
186
+
187
+ describe '#to_s' do
188
+ context "when a joker" do
189
+ it "returns 'Joker'" do
190
+ expect(joker.to_s).to eq('Joker')
191
+ end
192
+ end
193
+ context "when not a joker" do
194
+ it "returns 'Value of Suit'" do
195
+ expect(ace_spades.to_s).to eq('Ace of Spades')
196
+ end
197
+ end
198
+ end
199
+
200
+ describe '#to_sym' do
201
+ context "when a joker" do
202
+ it "returns :Joker" do
203
+ expect(joker.to_sym).to eq(:Joker)
204
+ end
205
+ end
206
+ context "when not a joker" do
207
+ it "returns :'Value of Suit'" do
208
+ expect(ace_spades.to_sym).to eq(:'Ace of Spades')
209
+ end
210
+ end
211
+ end
212
+
213
+ describe '#valid?' do
214
+ context "when valid" do
215
+ it "returns true" do
216
+ expect(ace_spades.valid?).to be(true)
217
+ expect(joker.valid?).to be(true)
218
+ end
219
+ end
220
+ context "when invalid" do
221
+ it "returns false" do
222
+ ace_spades.instance_variable_set(:@value, 'foo')
223
+ ace_spades.instance_variable_set(:@suit, 'bar')
224
+ expect(ace_spades.valid?).to be(false)
225
+ end
226
+ end
227
+ end
228
+ describe '#joker?' do
229
+ context "when a joker" do
230
+ it "returns true" do
231
+ expect(joker.joker?).to be(true)
232
+ end
233
+ end
234
+ context "when not a joker" do
235
+ it "returns false" do
236
+ expect(ace_spades.joker?).to be(false)
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '#validate!' do
242
+ context "when valid" do
243
+ it "returns self" do
244
+ expect(ace_spades.validate!).to be(ace_spades)
245
+ end
246
+ end
247
+ context "when invalid" do
248
+ it "raises error" do
249
+ ace_spades.instance_variable_set(:@value, false)
250
+ ace_spades.instance_variable_set(:@suit, false)
251
+ expect{ace_spades.validate!}.to raise_error
252
+ end
253
+ end
254
+ end
255
+
256
+ describe '#method_missing' do
257
+ context "when passed a valid predicate method" do
258
+ context "when a value" do
259
+ it "returns true or false" do
260
+ expect(ace_spades.ace?).to be(true)
261
+ expect(ace_spades.king?).to be(false)
262
+ expect(joker.ace?).to be(false)
263
+ end
264
+ end
265
+ context "when a suit" do
266
+ it "returns true or false" do
267
+ expect(ace_spades.spade?).to be(true)
268
+ expect(ace_spades.spades?).to be(true)
269
+ expect(ace_spades.hearts?).to be(false)
270
+ expect(joker.spades?).to be(false)
271
+ end
272
+ end
273
+ context "when a value and suit" do
274
+ it "returns true or false" do
275
+ expect(ace_spades.ace_spades?).to be(true)
276
+ expect(ace_spades.ace_of_spades?).to be(true)
277
+ expect(ace_spades.ace_of_hearts?).to be(false)
278
+ expect(ace_spades.king_spades?).to be(false)
279
+ end
280
+ end
281
+ end
282
+ context "when passed an invalid predicate method" do
283
+ it "raises error" do
284
+ expect{ace_spades.foo?}.to raise_error(NoMethodError)
285
+ expect{ace_spades.ace_foo?}.to raise_error(NoMethodError)
286
+ end
287
+ end
288
+ context "when passed an invalid method" do
289
+ it "raises error" do
290
+ expect{ace_spades.foo}.to raise_error(NoMethodError)
291
+ end
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,291 @@
1
+ require 'spec_helper'
2
+
3
+ describe Deck do
4
+ let(:deck) { Deck.new }
5
+
6
+ describe '.new' do
7
+ context "when passed valid params" do
8
+ it "initializes with 52 cards" do
9
+ expect(deck.length).to be(52)
10
+ end
11
+ it "initializes with 4 suits" do
12
+ expect(deck.group_by{|c|c.suit.to_sym}.count).to be(4)
13
+ end
14
+ it "initializes with 13 values" do
15
+ expect(deck.group_by{|c|c.value.to_sym}.count).to be(13)
16
+ end
17
+ it "initializes with 13 values per suit" do
18
+ values_per_suit = deck.group_by{|c|c.suit.to_sym}[:Spades].group_by{|c|c.value.to_sym}.count
19
+ expect(values_per_suit).to be(13)
20
+ end
21
+ it "initializes as shuffled" do
22
+ expect(deck[0..12].group_by{|c|c.suit.to_sym}.count).to_not be(1)
23
+ expect(deck[0..12].group_by{|c|c.value.to_sym}.count).to_not be(13)
24
+ end
25
+ it "initializes with aces high" do
26
+ expect(deck.aces_high?).to be(true)
27
+ end
28
+ context "when initialized with (false)" do
29
+ let(:deck) { Deck.new(false) }
30
+ it "creates the deck but does not add any cards" do
31
+ expect(deck.empty?).to be(true)
32
+ end
33
+ end
34
+ context "when initialized with {jokers: true}" do
35
+ let(:deck) { Deck.new(jokers: true) }
36
+ it "initializes with 54 cards" do
37
+ expect(deck.length).to be(54)
38
+ end
39
+ it "initializes with 2 jokers" do
40
+ expect(deck.find_cards(:joker).count).to be(2)
41
+ end
42
+ end
43
+ context "when initialized with {shuffle: false}" do
44
+ let(:deck) { Deck.new(shuffle: false) }
45
+ it "initializes in an unshuffled order by suit" do
46
+ expect(deck[0..12].group_by{|c|c.suit.to_sym}.count).to be(1)
47
+ end
48
+ it "initializes in an unshuffled order by value" do
49
+ deck[0..12].reduce do |memo, card|
50
+ expect(memo.value < card.value).to be(true)
51
+ card
52
+ end
53
+ end
54
+ end
55
+ context "when initialized with {aces_high: false}" do
56
+ let(:deck) { Deck.new(aces_high: false) }
57
+ it "initalizes with aces low" do
58
+ expect(deck.aces_high?).to be(false)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ describe '#find_card' do
65
+ context "when passed an argument" do
66
+ context "when the card exists" do
67
+ it "returns the card" do
68
+ card = deck.find_card(:ace, :spades)
69
+ expect(card.ace_of_spades?).to be(true)
70
+ end
71
+ end
72
+ context "when the card does not exist" do
73
+ it "returns nil" do
74
+ card = deck.find_card(:foo)
75
+ expect(card).to be_nil
76
+ end
77
+ end
78
+ end
79
+ context "when passed a block" do
80
+ it "returns the first card that yields the block as true" do
81
+ card = deck.find_card { |c| c.ace? }
82
+ expect(card.ace?).to be(true)
83
+ end
84
+ end
85
+ end
86
+
87
+ describe '#find_cards' do
88
+ context "when passed an argument" do
89
+ context "when there are cards that match the argument" do
90
+ it "returns an array of matching cards" do
91
+ cards = deck.find_cards(:ace)
92
+ expect(cards.count).to be(4)
93
+ cards.each { |c| expect(c.ace?).to be(true) }
94
+ end
95
+ end
96
+ context "when no cards match" do
97
+ it "returns an empty array" do
98
+ cards = deck.find_cards(:foo)
99
+ expect(cards.empty?).to be(true)
100
+ end
101
+ end
102
+ end
103
+ context "when passed a block" do
104
+ it "returns cards that yield as true" do
105
+ cards = deck.find_cards { |c| c.ace? }
106
+ expect(cards.count).to be(4)
107
+ cards.each { |c| expect(c.ace?).to be(true) }
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#add_card' do
113
+ it "adds specified card to self" do
114
+ deck.add_card('ace', 'spades')
115
+ expect(deck.count).to be(53)
116
+ expect(deck.last.to_s).to eq('Ace of Spades')
117
+ end
118
+ it "adds self as card.deck" do
119
+ expect(deck.sample.deck).to be(deck)
120
+ end
121
+ end
122
+
123
+ describe '#remove_cards' do
124
+ context "when passed an argument" do
125
+ let(:aces) { deck.remove_cards(:ace) }
126
+ it "removes matching cards from the deck" do
127
+ expect(aces.count).to be(4)
128
+ expect(deck.find{|c|c.ace?}).to be_nil
129
+ end
130
+ end
131
+ context "when passed a block" do
132
+ let(:spades) { deck.remove_cards {|c|c.spade?} }
133
+ it "removes cards that yield to true" do
134
+ expect(spades.count).to be(13)
135
+ expect(deck.find{|c|c.spade?}).to be_nil
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#aces_high?' do
141
+ context "when aces are high" do
142
+ it "returns true" do
143
+ expect(deck.aces_high?).to be(true)
144
+ end
145
+ end
146
+ context "when aces are low" do
147
+ it "returns false" do
148
+ deck.aces_high = false
149
+ expect(deck.aces_high?).to be(false)
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#jokers?' do
155
+ context "there is a joker or jokers in the deck" do
156
+ it "returns true" do
157
+ deck.add_card(:joker)
158
+ expect(deck.jokers?).to be(true)
159
+ end
160
+ end
161
+ context "when there are no jokers" do
162
+ it "returns false" do
163
+ expect(deck.jokers?).to be(false)
164
+ end
165
+ end
166
+ end
167
+
168
+ describe '#deal' do
169
+ context "when called with no argument" do
170
+ it "deals 1 card when called with no argument" do
171
+ expect(deck.deal).to be_a(Card)
172
+ end
173
+ end
174
+ context "when called with an integer argument" do
175
+ it "deals multiple cards in an array when called with an integer argument" do
176
+ cards = deck.deal(10)
177
+ expect(cards.count).to be(10)
178
+ expect(cards.all?{|c|c.instance_of?(Card)}).to be(true)
179
+ end
180
+ end
181
+ it "removes dealt cards from deck" do
182
+ deck.deal
183
+ expect(deck.count).to be(51)
184
+ deck.deal(9)
185
+ expect(deck.count).to be(42)
186
+ end
187
+ it "stores dealt cards in another array" do
188
+ expect(deck.deal).to be(deck.dealt.first)
189
+ deck.deal(9)
190
+ expect(deck.dealt.count).to be(10)
191
+ end
192
+ end
193
+
194
+ describe '#shuffle!' do
195
+ it "shuffles the deck" do
196
+ dup = deck.dup
197
+ expect(deck.shuffle!).to_not eq(dup)
198
+ end
199
+ it "returns dealt cards to the deck before shuffling" do
200
+ deck.deal(10)
201
+ deck.shuffle!
202
+ expect(deck.count).to be(52)
203
+ end
204
+ end
205
+
206
+ describe '#shuffle' do
207
+ it "shuffles the deck non-destructively" do
208
+ expect(deck.shuffle).to_not be(deck)
209
+ end
210
+ it "returns dealt cards to the deck before shuffling" do
211
+ deck.deal(10)
212
+ expect(deck.count).to be(42)
213
+ expect(deck.shuffle.count).to be(52)
214
+ end
215
+ end
216
+
217
+ describe '#unshuffle!' do
218
+ let(:deck) { Deck.new(jokers: true) }
219
+ it "returns any dealt cards to the deck" do
220
+ deck.deal(10)
221
+ expect(deck.unshuffle!.count).to be(54)
222
+ end
223
+ it "sorts the deck from lowest to highest" do
224
+ deck.remove_cards(:joker)
225
+ deck.unshuffle!.reduce do |memo, card|
226
+ expect(memo < card).to be(true)
227
+ card
228
+ end
229
+ end
230
+ end
231
+
232
+ describe '#unshuffle' do
233
+ let(:deck) { Deck.new(jokers: true, shuffle:true) }
234
+ it "it unshuffles nondestructively" do
235
+ deck.deal(10)
236
+ expect(deck.unshuffle.count).to be(54)
237
+ expect(deck.count).to be(44)
238
+ end
239
+ it "sorts the deck from lowest to highest nondestructively" do
240
+ deck.remove_cards(:joker)
241
+ deck.unshuffle.reduce do |memo, card|
242
+ expect(memo < card).to be(true)
243
+ card
244
+ end
245
+ first_suit = deck[0..12].group_by{|c|c.suit.to_sym}
246
+ expect(first_suit.count).to_not be(1)
247
+ end
248
+ end
249
+
250
+ describe '#valid?' do
251
+ describe "without jokers" do
252
+ context "when valid" do
253
+ it "returns true" do
254
+ expect(deck.valid?).to be(true)
255
+ end
256
+ end
257
+ context "when invalid" do
258
+ context "when wrong length" do
259
+ it "returns false" do
260
+ deck.remove_cards(:spades)
261
+ expect(deck.valid?).to be(false)
262
+ end
263
+ end
264
+ context "when wrong cards" do
265
+ it "returns false" do
266
+ deck.remove_cards(:ace, :spades)
267
+ deck.add_card(:king, :spades)
268
+ expect(deck.valid?).to be(false)
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+ describe "with jokers" do
275
+ let(:deck) { Deck.new(jokers: true) }
276
+ context "when valid" do
277
+ it "returns true" do
278
+ expect(deck.valid?).to be(true)
279
+ end
280
+ end
281
+ context "when invalid" do
282
+ context "when wrong cards" do
283
+ it "returns false" do
284
+ deck.remove_cards(:ace, :spades)
285
+ deck.add_card(:joker)
286
+ expect(deck.valid?).to be(false)
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end