cardshark 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/rspec.yml +20 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +20 -0
- data/Gemfile +2 -2
- data/Guardfile +23 -0
- data/LICENSE +19 -0
- data/README.md +49 -0
- data/Rakefile +2 -0
- data/cardshark.gemspec +20 -13
- data/lib/cardshark.rb +8 -1
- data/lib/cardshark/abstract.rb +30 -0
- data/lib/cardshark/card.rb +31 -6
- data/lib/cardshark/deck.rb +72 -25
- data/lib/cardshark/dimension.rb +57 -0
- data/lib/cardshark/error.rb +11 -0
- data/lib/cardshark/version.rb +3 -1
- data/spec/cardshark/abstract_spec.rb +27 -0
- data/spec/cardshark/card_spec.rb +86 -0
- data/spec/cardshark/deck_spec.rb +252 -0
- data/spec/cardshark/dimension_spec.rb +113 -0
- data/spec/cardshark/error/abstract_class_spec.rb +21 -0
- data/spec/cardshark/error_spec.rb +21 -0
- data/spec/cardshark_spec.rb +6 -0
- data/spec/spec_helper.rb +20 -0
- metadata +117 -78
- data/README +0 -8
- data/lib/cardshark/canasta_deck.rb +0 -21
- data/lib/cardshark/french_deck.rb +0 -20
- data/lib/cardshark/piquet_deck.rb +0 -16
- data/lib/cardshark/uno_deck.rb +0 -20
- data/spec/canasta_deck_spec.rb +0 -13
- data/spec/card_spec.rb +0 -15
- data/spec/deck_spec.rb +0 -39
- data/spec/french_deck_spec.rb +0 -17
- data/spec/piquet_deck_spec.rb +0 -13
- data/spec/uno_deck_spec.rb +0 -47
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cardshark
|
4
|
+
class Error < StandardError
|
5
|
+
class AbstractClass < Error; end
|
6
|
+
class DeckAlreadyShuffled < Error; end
|
7
|
+
class DeckExhausted < Error; end
|
8
|
+
class DeckOrderInvalid < Error; end
|
9
|
+
class DimensionInheritanceLimit < Error; end
|
10
|
+
end
|
11
|
+
end
|
data/lib/cardshark/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cardshark/abstract'
|
4
|
+
|
5
|
+
RSpec.describe Cardshark::Abstract do
|
6
|
+
context 'included into a new class' do
|
7
|
+
let(:klass) { Class.new { include Cardshark::Abstract } }
|
8
|
+
|
9
|
+
describe '::new' do
|
10
|
+
it 'raises a Cardshark::Error::AbstractClass' do
|
11
|
+
expect { klass.new }.to raise_error(Cardshark::Error::AbstractClass)
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'it has been subclassed' do
|
15
|
+
let(:subclass) { Class.new(klass) }
|
16
|
+
|
17
|
+
it 'does not raise an error' do
|
18
|
+
expect { subclass.new }.not_to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns an instance of the subclass' do
|
22
|
+
expect(subclass.new).to be_a(subclass)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cardshark/card'
|
4
|
+
|
5
|
+
RSpec.describe Cardshark::Card do
|
6
|
+
let(:rank) { Class.new(Cardshark::Dimension) }
|
7
|
+
let(:suit) { Class.new(Cardshark::Dimension) }
|
8
|
+
|
9
|
+
let(:ace) { rank.new(:ace) }
|
10
|
+
let(:spades) { suit.new(:spades) }
|
11
|
+
let(:clubs) { suit.new(:clubs) }
|
12
|
+
|
13
|
+
describe '::new' do
|
14
|
+
context 'invalid arguments' do
|
15
|
+
context 'no arguments' do
|
16
|
+
it 'raises an ArgumentError' do
|
17
|
+
expect { described_class.new }.to raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'invalid argument type' do
|
22
|
+
it 'raises an ArgumentError' do
|
23
|
+
expect { described_class.new('invalid-arguments') }
|
24
|
+
.to raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'more than one argument of the same dimension' do
|
29
|
+
it 'raises an ArgumentError' do
|
30
|
+
expect { described_class.new(spades, clubs) }
|
31
|
+
.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'valid arguments' do
|
37
|
+
it 'does not raise an error' do
|
38
|
+
expect { described_class.new(ace, spades) }.not_to raise_error
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#dimensions' do
|
44
|
+
let(:card) { described_class.new(ace, spades) }
|
45
|
+
|
46
|
+
before do
|
47
|
+
@result = card.dimensions
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns an Array' do
|
51
|
+
expect(@result).to be_an(Array)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'contains one entry for each type of dimension' do
|
55
|
+
expect(@result.count).to eq(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'contains a symbol for the first dimension' do
|
59
|
+
expect(@result).to include(rank.id)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'contains a symbol for the second dimension' do
|
63
|
+
expect(@result).to include(suit.id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'dynamic dimension methods' do
|
68
|
+
let(:card) { described_class.new(ace, spades) }
|
69
|
+
|
70
|
+
it 'has a method for the first dimension' do
|
71
|
+
expect(card.respond_to?(suit.id)).to eq(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'has a method for the second dimension' do
|
75
|
+
expect(card.respond_to?(rank.id)).to eq(true)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns the correct value for the first dimension' do
|
79
|
+
expect(card.send(suit.id)).to eq(spades)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns the correct value for the second dimension' do
|
83
|
+
expect(card.send(rank.id)).to eq(ace)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cardshark/card'
|
4
|
+
require 'cardshark/deck'
|
5
|
+
|
6
|
+
RSpec.describe Cardshark::Deck do
|
7
|
+
context 'class methods' do
|
8
|
+
describe '::new' do
|
9
|
+
context 'invalid arguments' do
|
10
|
+
context 'no arguments' do
|
11
|
+
it 'raises an ArgumentError' do
|
12
|
+
expect { described_class.new }.to raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'invalid arguments' do
|
17
|
+
it 'raises an ArgumentError' do
|
18
|
+
expect { described_class.new('invalid-arguments') }
|
19
|
+
.to raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'valid arguments' do
|
24
|
+
it 'does not raise an error' do
|
25
|
+
expect { described_class.new {} }.not_to raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'instance methods' do
|
33
|
+
let(:suit) { Class.new(Cardshark::Dimension) }
|
34
|
+
let!(:spades) { suit.new(:spades) }
|
35
|
+
let!(:hearts) { suit.new(:hearts) }
|
36
|
+
|
37
|
+
let(:rank) { Class.new(Cardshark::Dimension) }
|
38
|
+
let!(:king) { rank.new(:king) }
|
39
|
+
let!(:queen) { rank.new(:queen) }
|
40
|
+
let!(:jack) { rank.new(:jack) }
|
41
|
+
|
42
|
+
let(:cards) do
|
43
|
+
cards = []
|
44
|
+
suit.all.each do |suit|
|
45
|
+
rank.all.each do |rank|
|
46
|
+
cards << Cardshark::Card.new(rank, suit)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
cards
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#draw' do
|
53
|
+
context 'new deck' do
|
54
|
+
let(:deck) { described_class.new { cards } }
|
55
|
+
|
56
|
+
before do
|
57
|
+
@one = deck.draw
|
58
|
+
@two = deck.draw
|
59
|
+
@three = deck.draw
|
60
|
+
@four = deck.draw
|
61
|
+
@five = deck.draw
|
62
|
+
@six = deck.draw
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns a Cardshark::Card' do
|
66
|
+
expect(@one).to be_a(Cardshark::Card)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns the first card first' do
|
70
|
+
expect(@one).to eq(cards[0])
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns the second card second' do
|
74
|
+
expect(@two).to eq(cards[1])
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns the third card third' do
|
78
|
+
expect(@three).to eq(cards[2])
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns the fourth card fourth' do
|
82
|
+
expect(@four).to eq(cards[3])
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns the fifth card fifth' do
|
86
|
+
expect(@five).to eq(cards[4])
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns the sixth card sixth' do
|
90
|
+
expect(@six).to eq(cards[5])
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'all cards drawn' do
|
94
|
+
it 'raises an error' do
|
95
|
+
expect { deck.draw }.to raise_error(Cardshark::Error::DeckExhausted)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'shuffled deck' do
|
101
|
+
let(:deck) { described_class.new { cards } }
|
102
|
+
|
103
|
+
before do
|
104
|
+
deck.shuffle!([3, 4, 1, 5, 2, 0])
|
105
|
+
|
106
|
+
@one = deck.draw
|
107
|
+
@two = deck.draw
|
108
|
+
@three = deck.draw
|
109
|
+
@four = deck.draw
|
110
|
+
@five = deck.draw
|
111
|
+
@six = deck.draw
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns a Cardshark::Card' do
|
115
|
+
expect(@one).to be_a(Cardshark::Card)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns the fourth card first' do
|
119
|
+
expect(@one).to eq(cards[3])
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'returns the fifth card second' do
|
123
|
+
expect(@two).to eq(cards[4])
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'returns the second card third' do
|
127
|
+
expect(@three).to eq(cards[1])
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'returns the sixth card fourth' do
|
131
|
+
expect(@four).to eq(cards[5])
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'returns the third card fifth' do
|
135
|
+
expect(@five).to eq(cards[2])
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'returns the first card sixth' do
|
139
|
+
expect(@six).to eq(cards[0])
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'all cards drawn' do
|
143
|
+
it 'raises an error' do
|
144
|
+
expect { deck.draw }.to raise_error(Cardshark::Error::DeckExhausted)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#count' do
|
151
|
+
let(:deck) { described_class.new { cards } }
|
152
|
+
|
153
|
+
it 'returns the correct number of cards' do
|
154
|
+
expect(deck.count).to eq(6)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#shuffled?' do
|
159
|
+
context 'new deck' do
|
160
|
+
let(:deck) { described_class.new { cards } }
|
161
|
+
|
162
|
+
it 'returns false' do
|
163
|
+
expect(deck.shuffled?).to eq(false)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'shuffled deck' do
|
168
|
+
let(:deck) { described_class.new { cards } }
|
169
|
+
|
170
|
+
before do
|
171
|
+
deck.shuffle!([3, 1, 2, 0, 5, 4])
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'returns true' do
|
175
|
+
expect(deck.shuffled?).to eq(true)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe 'shuffle!' do
|
181
|
+
let(:deck) { described_class.new { cards } }
|
182
|
+
|
183
|
+
context 'invalid arguments' do
|
184
|
+
context 'no arguments' do
|
185
|
+
it 'raises an ArgumentError' do
|
186
|
+
expect { deck.shuffle! }.to raise_error(ArgumentError)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'wrong argument type' do
|
191
|
+
it 'raises an ArgumentError' do
|
192
|
+
expect { deck.shuffle!('invalid-argument') }
|
193
|
+
.to raise_error(ArgumentError)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'invalid deck mapping' do
|
198
|
+
context 'non-integer elements' do
|
199
|
+
it 'raises an Cardshark::Error::DeckOrderInvalid' do
|
200
|
+
expect { deck.shuffle!(['a', 1, 2, 3, 4, 5]) }
|
201
|
+
.to raise_error(Cardshark::Error::DeckOrderInvalid)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'elements out of bounds' do
|
206
|
+
it 'raises an Cardshark::Error::DeckOrderInvalid' do
|
207
|
+
expect { deck.shuffle!([0, 1, 2, 3, 4, 6]) }
|
208
|
+
.to raise_error(Cardshark::Error::DeckOrderInvalid)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'too many elements' do
|
213
|
+
it 'raises an Cardshark::Error::DeckOrderInvalid' do
|
214
|
+
expect { deck.shuffle!([0, 1, 2, 3, 4, 5, 6]) }
|
215
|
+
.to raise_error(Cardshark::Error::DeckOrderInvalid)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'too few elements' do
|
220
|
+
it 'raises an Cardshark::Error::DeckOrderInvalid' do
|
221
|
+
expect { deck.shuffle!([0, 1, 2, 3]) }
|
222
|
+
.to raise_error(Cardshark::Error::DeckOrderInvalid)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context 'valid arguments' do
|
229
|
+
let(:deck_mapping) { [5, 4, 3, 2, 1, 0] }
|
230
|
+
|
231
|
+
before do
|
232
|
+
@result = deck.shuffle!(deck_mapping)
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'returns an instance of the described class' do
|
236
|
+
expect(@result).to be_a(described_class)
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'returns the same instance' do
|
240
|
+
expect(@result).to equal(deck)
|
241
|
+
end
|
242
|
+
|
243
|
+
context 'alredy shuffled' do
|
244
|
+
it 'raises a Cardshark::Error::AlreadyShuffled' do
|
245
|
+
expect { deck.shuffle!(deck_mapping) }
|
246
|
+
.to raise_error(Cardshark::Error::DeckAlreadyShuffled)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cardshark/dimension'
|
4
|
+
|
5
|
+
RSpec.describe Cardshark::Dimension do
|
6
|
+
describe '::new' do
|
7
|
+
it 'raises an Error::AbstractClass' do
|
8
|
+
expect { described_class.new(:id) }
|
9
|
+
.to raise_exception(Cardshark::Error::AbstractClass)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when subclassed' do
|
14
|
+
let(:subclass) { Class.new(described_class) }
|
15
|
+
|
16
|
+
describe '::new' do
|
17
|
+
context 'invalid arguments' do
|
18
|
+
context 'no arguments' do
|
19
|
+
it 'raises an ArgumentError' do
|
20
|
+
expect { subclass.new }.to raise_exception(ArgumentError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'invalid arguments' do
|
25
|
+
it 'raises an ArgumentError' do
|
26
|
+
expect { subclass.new('invalid-argument') }
|
27
|
+
.to raise_exception(ArgumentError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'valid arguments' do
|
33
|
+
before do
|
34
|
+
@result = subclass.new(:id)
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'return value' do
|
38
|
+
it 'is a an instance of the class' do
|
39
|
+
expect(@result).to be_an_instance_of(subclass)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'is a subclass of the described class' do
|
43
|
+
expect(@result.class.superclass).to eq(described_class)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns the same object for the same id' do
|
47
|
+
expect(@result).to equal(subclass.new(:id))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '::all' do
|
54
|
+
before { @result = subclass.all }
|
55
|
+
|
56
|
+
it 'returns an Array' do
|
57
|
+
expect(@result).to be_an(Array)
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'it has not been instantiated' do
|
61
|
+
it 'returns an empty Array' do
|
62
|
+
expect(@result.count).to eq(0)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'it has been instantiated' do
|
67
|
+
before do
|
68
|
+
@instances = []
|
69
|
+
3.times { |i| @instances.push(subclass.new(i.to_s.to_sym)) }
|
70
|
+
@result = subclass.all
|
71
|
+
end
|
72
|
+
after { clear_instance_metadata }
|
73
|
+
|
74
|
+
it 'returns an array of instances' do
|
75
|
+
expect(@result.first.class).to eq(subclass)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns all of the instances' do
|
79
|
+
expect(@result).to match_array(@instances)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '::id' do
|
85
|
+
it 'returns a symbol' do
|
86
|
+
expect(subclass.id.class).to eq(Symbol)
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'named subclass' do
|
90
|
+
before { class NamedClassExample < described_class; end }
|
91
|
+
after { Object.send(:remove_const, 'NamedClassExample') }
|
92
|
+
|
93
|
+
it 'returns a symbol version of the class name' do
|
94
|
+
expect(NamedClassExample.id).to eq(:named_class_example)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#to_s' do
|
100
|
+
let(:instance) { subclass.new(:name) }
|
101
|
+
|
102
|
+
it 'returns a capitalized string' do
|
103
|
+
expect(instance.to_s).to eq('Name')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# This cleans up metadata about subclasses so that
|
109
|
+
# these specs can be run reliably in any order
|
110
|
+
def clear_instance_metadata
|
111
|
+
subclass.instance_variable_set(:@instances, {})
|
112
|
+
end
|
113
|
+
end
|