ace_of_spades 0.0.1.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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