cardshark 0.0.1 → 0.0.2
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.
- 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
|