cardlike 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +278 -0
- data/Rakefile +4 -0
- data/cardlike.gemspec +21 -0
- data/examples/go_fish.rb +120 -0
- data/lib/cardlike/card.rb +73 -0
- data/lib/cardlike/deck.rb +103 -0
- data/lib/cardlike/hand.rb +28 -0
- data/lib/cardlike/score.rb +47 -0
- data/lib/cardlike/turn.rb +27 -0
- data/lib/cardlike/version.rb +3 -0
- data/lib/cardlike.rb +154 -0
- data/spec/integration/card_construction_spec.rb +105 -0
- data/spec/integration/dealing_spec.rb +42 -0
- data/spec/integration/deck_construction_spec.rb +143 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/units/cardlike_spec.rb +9 -0
- data/spec/units/deck_spec.rb +43 -0
- data/spec/units/hand_spec.rb +50 -0
- data/spec/units/score_spec.rb +73 -0
- data/spec/units/turn_spec.rb +24 -0
- metadata +110 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Payton Swick
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
# Cardlike
|
2
|
+
|
3
|
+
A DSL to design and test card games.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'cardlike'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install cardlike
|
18
|
+
|
19
|
+
Include the gem in your code to use it.
|
20
|
+
|
21
|
+
require 'cardlike'
|
22
|
+
|
23
|
+
## Summary
|
24
|
+
|
25
|
+
Cardlike provides a [DSL](http://en.wikipedia.org/wiki/Domain-specific_language)
|
26
|
+
for creating a card game. It's a layer of abstraction for making Cards (with
|
27
|
+
various properties), putting them into Decks, dealing them into Hands, and then
|
28
|
+
taking turns while keeping a running score.
|
29
|
+
|
30
|
+
Cardlike is agnostic about what kind of game you want to create (it may not have
|
31
|
+
turns or a score) and how you want to interact with the game. Here's a few
|
32
|
+
possibilities for how you could use it:
|
33
|
+
|
34
|
+
* Write tests for a physical card game to see if it will work.
|
35
|
+
* Write a text-based card game.
|
36
|
+
* Use as a backend for a web card game (backbone.js, anyone?).
|
37
|
+
* Use as a backend for a native app card game.
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
There are various ways to use the methods in Cardlike. Probably the easiest is
|
42
|
+
to define your whole game in a `Cardlike.game` block.
|
43
|
+
|
44
|
+
Cardlike.game do
|
45
|
+
card "Ace of Diamonds"
|
46
|
+
card "Ace of Hearts"
|
47
|
+
card "Ace of Spades"
|
48
|
+
card "Ace of Clubs"
|
49
|
+
|
50
|
+
deck "My Deck" do
|
51
|
+
copy_card "Ace of Diamonds"
|
52
|
+
copy_card "Ace of Hearts"
|
53
|
+
copy_card "Ace of Clubs"
|
54
|
+
copy_card "Ace of Spades"
|
55
|
+
end
|
56
|
+
|
57
|
+
the_deck("My Deck").shuffle!
|
58
|
+
|
59
|
+
hand "Player 1"
|
60
|
+
|
61
|
+
puts "Find the Ace of Spades!"
|
62
|
+
|
63
|
+
the_deck("My Deck").draw_into(the_hand("Player 1"))
|
64
|
+
|
65
|
+
if the_hand("Player 1").first.name == "Ace of Spades"
|
66
|
+
puts "Congratulations, you win!"
|
67
|
+
else
|
68
|
+
puts "Oh, well, try again."
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
You can also prefix the class methods with Cardlike.
|
73
|
+
|
74
|
+
Cardlike.type_of_card :playing_card do
|
75
|
+
has :suit
|
76
|
+
end
|
77
|
+
|
78
|
+
Cardlike.new_playing_card "Six of Spades" do
|
79
|
+
suit 'spades'
|
80
|
+
end
|
81
|
+
|
82
|
+
@deck = Cardlike.deck "My Deck" do
|
83
|
+
include_card "Six of Spades"
|
84
|
+
end
|
85
|
+
|
86
|
+
puts "Drawing #{@deck.draw}" # @deck.draw == Cardlike.the_deck("My Deck").draw
|
87
|
+
|
88
|
+
### Creating Cards
|
89
|
+
|
90
|
+
The `card` method can be used to create a new Card object. Created cards are
|
91
|
+
stored globally and can be accessed with `the_card` and the card's name.
|
92
|
+
|
93
|
+
Cardlike.card "Draw one Play one" # => creates (and returns) a new Card object
|
94
|
+
Cardlike.the_card "Draw one Play one" # => the Card object created above (NOT a copy)
|
95
|
+
|
96
|
+
A block passed to the `card` method will be executed in the new Card's context.
|
97
|
+
Card objects can have properties, but it's best to create a custom type of card
|
98
|
+
to do this.
|
99
|
+
|
100
|
+
Cardlike.type_of_card :action_card do
|
101
|
+
has :power_level
|
102
|
+
has :color
|
103
|
+
end
|
104
|
+
|
105
|
+
Cardlike.new_action_card "Magic Spell" do
|
106
|
+
power_level 5
|
107
|
+
color :red
|
108
|
+
end # => new Card object (actually a new ActionCard object)
|
109
|
+
|
110
|
+
Cardlike.the_card("Magic Spell")[:color] # => :red
|
111
|
+
Cardlike.the_card("Magic Spell")[:power_level] # => 5
|
112
|
+
|
113
|
+
The `type_of_card` method creates a new subclass of Card. Use the `has` method in
|
114
|
+
a `card` block to add a property. A new card creation method is added to the DSL
|
115
|
+
with the type of card prefixed by `new_`.
|
116
|
+
|
117
|
+
To assign a property to a new card in the card block, use a method of the same
|
118
|
+
name as the property. To retrieve that property, treat the card as a hash and
|
119
|
+
reference the property name as the key.
|
120
|
+
|
121
|
+
### Creating Decks
|
122
|
+
|
123
|
+
But what you really want to do is create a Deck of cards.
|
124
|
+
|
125
|
+
Cardlike.game do
|
126
|
+
|
127
|
+
type_of_card :action_card do
|
128
|
+
has :power_level
|
129
|
+
has :cost
|
130
|
+
end
|
131
|
+
|
132
|
+
deck "Action Deck" do
|
133
|
+
|
134
|
+
new_action_card "Magic Spell" do
|
135
|
+
power_level 5
|
136
|
+
cost 3
|
137
|
+
end
|
138
|
+
|
139
|
+
new_action_card "Subtle Strike" do
|
140
|
+
power_level 2
|
141
|
+
cost 2
|
142
|
+
end
|
143
|
+
|
144
|
+
new_action_card "Wide Swing" do
|
145
|
+
power_level 1
|
146
|
+
cost 1
|
147
|
+
end
|
148
|
+
|
149
|
+
3.times { copy_card "Magic Spell" }
|
150
|
+
3.times { copy_card "Subtle Strike" }
|
151
|
+
3.times { copy_card "Wide Swing" }
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
You can use the `deck` method to name a new deck and pass it a block which will
|
158
|
+
be evauluated in the context of the Deck. In this case, we put a bunch of `card`
|
159
|
+
creation methods in the block, or more specifically, `new_action_card` (the
|
160
|
+
method that was created by `type_of_card`).
|
161
|
+
|
162
|
+
Cards defined within the `deck` method will automatically be added to the Deck.
|
163
|
+
You can also add cards by treating the Deck as an Array (which it is).
|
164
|
+
|
165
|
+
Cardlike.game do
|
166
|
+
type_of_card :action_card
|
167
|
+
@card1 = new_action_card "Magic Spell"
|
168
|
+
@deck = deck "Action Deck"
|
169
|
+
|
170
|
+
@deck << @card1
|
171
|
+
end
|
172
|
+
|
173
|
+
Decks can be accessed using `the_deck` method, which takes the name of a Deck.
|
174
|
+
The cards within are inside an Array, so most Array methods will work
|
175
|
+
(particularly `Deck#shuffle!`).
|
176
|
+
|
177
|
+
Cardlike.game do
|
178
|
+
...
|
179
|
+
the_deck("Action Deck").shuffle!
|
180
|
+
the_deck("Action Deck").first.name # => could be "Magic Spell"
|
181
|
+
end
|
182
|
+
|
183
|
+
### Drawing into Hands
|
184
|
+
|
185
|
+
Often you probably want to draw the top card of the deck. Cardlike Decks have a
|
186
|
+
`draw` method and a `draw_into` method. Both remove and return the top Card from
|
187
|
+
that Deck. `draw_into` takes an additional argument which should be an Array,
|
188
|
+
Deck, or Hand (or something that responds to `<<`).
|
189
|
+
|
190
|
+
A Hand is a subclass of a Deck with some additional methods. Creating a Hand is
|
191
|
+
easiest with the `hand` method (which works like the `Deck` method).
|
192
|
+
|
193
|
+
Cardlike.game do
|
194
|
+
type_of_card :action_card
|
195
|
+
@card1 = new_action_card "Magic Spell"
|
196
|
+
@deck = deck "Action Deck" do
|
197
|
+
2.times { copy_card "Magic Spell" }
|
198
|
+
end
|
199
|
+
|
200
|
+
the_deck("Action Deck").draw # => <Card, :name => 'Magic Spell'>
|
201
|
+
|
202
|
+
@player1 = hand "Player 1"
|
203
|
+
|
204
|
+
the_deck("Action Deck").draw_into @player1 # => <Card, :name => 'Magic Spell'>
|
205
|
+
end
|
206
|
+
|
207
|
+
Hands can be accessed by `the_hand`, just like the other Cardlike objects.
|
208
|
+
|
209
|
+
Cardlike.game do
|
210
|
+
...
|
211
|
+
hand "Player 1"
|
212
|
+
the_deck("Action Deck").draw_into the_hand("Player 1")
|
213
|
+
the_hand("Player 1").size # => 1
|
214
|
+
end
|
215
|
+
|
216
|
+
### Defining a Turn
|
217
|
+
|
218
|
+
As most games have turns, you can define a block of code as a turn using the
|
219
|
+
`define_turn` method and then call it using `begin_new_turn`. Any number of
|
220
|
+
arguments can be passed to the block.
|
221
|
+
|
222
|
+
Cardlike.game do
|
223
|
+
...
|
224
|
+
|
225
|
+
define_turn do |current_hand|
|
226
|
+
the_deck("Action Deck").draw_into current_hand # Draw a card
|
227
|
+
puts "#{current_hand.name}'s Hand: #{current_hand}"
|
228
|
+
end
|
229
|
+
|
230
|
+
hands = []
|
231
|
+
hands << hand "Player 2"
|
232
|
+
hands << hand "Player 1"
|
233
|
+
|
234
|
+
begin
|
235
|
+
hands.rotate!
|
236
|
+
begin_new_turn(hands.first)
|
237
|
+
end while victory == false
|
238
|
+
end
|
239
|
+
|
240
|
+
### Keeping Score
|
241
|
+
|
242
|
+
Chances are there's scoring in your game too. You can use the `score` method to
|
243
|
+
add a score to a particular key. The key can be anything, so players can have
|
244
|
+
several scores or the game could keep score of several objects at once.
|
245
|
+
|
246
|
+
You can retrieve the score using `the_score` followed by the key or get a Hash
|
247
|
+
of all the scores with `scores`.
|
248
|
+
|
249
|
+
If you need to decrement the score or set it to a particular number, use
|
250
|
+
`set_score`.
|
251
|
+
|
252
|
+
Cardlike.game do
|
253
|
+
...
|
254
|
+
score "Player 1" # sets the score to 1
|
255
|
+
score "Player 2" # sets the score to 1
|
256
|
+
score "Player 1" # sets the score to 2
|
257
|
+
|
258
|
+
the_score "Player 1" # => 2
|
259
|
+
|
260
|
+
scores # => {"Player 1" => 2, "Player 2" => 1}
|
261
|
+
|
262
|
+
set_score "Player 2", 5
|
263
|
+
|
264
|
+
scores # => {"Player 1" => 2, "Player 2" => 5}
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
### Examples
|
269
|
+
|
270
|
+
See the examples in the `examples/` directory for some more good ideas.
|
271
|
+
|
272
|
+
## Contributing
|
273
|
+
|
274
|
+
1. Fork it
|
275
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
276
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
277
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
278
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/cardlike.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cardlike/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "cardlike"
|
8
|
+
gem.version = Cardlike::VERSION
|
9
|
+
gem.authors = ["Payton Swick"]
|
10
|
+
gem.email = ["payton@foolord.com"]
|
11
|
+
gem.summary = 'A library for developing and testing card games.'
|
12
|
+
gem.homepage = "https://github.com/sirbrillig/cardlike"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.add_development_dependency 'rspec'
|
20
|
+
gem.add_dependency 'activesupport'
|
21
|
+
end
|
data/examples/go_fish.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'cardlike'
|
2
|
+
require 'highline/import'
|
3
|
+
|
4
|
+
Cardlike.game do
|
5
|
+
puts "Setting up..."
|
6
|
+
|
7
|
+
type_of_card :playing_card do
|
8
|
+
has :face
|
9
|
+
has :value
|
10
|
+
has :suit
|
11
|
+
end
|
12
|
+
|
13
|
+
deck "Go Fish" do
|
14
|
+
['Spades', 'Clubs', 'Diamonds', 'Hearts'].each do |card_suit|
|
15
|
+
['Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Jack', 'Queen', 'King', 'Ace'].each_with_index do |name, index|
|
16
|
+
new_playing_card("#{name} of #{card_suit}") do
|
17
|
+
value index
|
18
|
+
face name
|
19
|
+
suit card_suit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
shuffle!
|
25
|
+
end
|
26
|
+
|
27
|
+
players = []
|
28
|
+
players << hand("Player 1")
|
29
|
+
players << hand("Player 2")
|
30
|
+
|
31
|
+
players.each do |player|
|
32
|
+
7.times { the_deck("Go Fish").draw_into player }
|
33
|
+
end
|
34
|
+
|
35
|
+
current_player = players.first
|
36
|
+
|
37
|
+
define_turn do
|
38
|
+
target_player = players.last
|
39
|
+
if players.size > 2
|
40
|
+
target_name = choose do |menu|
|
41
|
+
menu.prompt = "#{current_player.name}, who are you asking? "
|
42
|
+
menu.choices(*players.map { |p| p.name })
|
43
|
+
end
|
44
|
+
target_player = players.select { |p| p.name == target_name }.first
|
45
|
+
end
|
46
|
+
|
47
|
+
target_card_name = choose do |menu|
|
48
|
+
menu.prompt = "#{current_player.name}, what are you looking for? "
|
49
|
+
menu.choices(*current_player.map {|c| c.name })
|
50
|
+
end
|
51
|
+
target_card = current_player.select { |c| c.name == target_card_name }.first
|
52
|
+
puts "#{target_player.name}, do you have any #{target_card[:face]}s?\n"
|
53
|
+
|
54
|
+
another_turn = false
|
55
|
+
removed = target_player.remove_card_if { |c| c[:value] == target_card[:value] }
|
56
|
+
unless removed.empty?
|
57
|
+
current_player += removed
|
58
|
+
puts "Got one! Yes indeed, #{target_player.name} had #{removed.size} #{removed.first[:face]}#{removed.size == 1 ? '' : 's'}."
|
59
|
+
puts "You get another turn."
|
60
|
+
another_turn = true
|
61
|
+
|
62
|
+
# Check for matches.
|
63
|
+
matching_sets = current_player.group_by { |c| c[:value] }.select { |k, v| v.size >= 4 }
|
64
|
+
matching_sets.each do |matching_value, cards|
|
65
|
+
matching_card = cards.first
|
66
|
+
puts "You have a matching set of #{matching_card[:face]}s!"
|
67
|
+
current_player.remove_card_if { |c| c[:value] == matching_value }
|
68
|
+
score current_player.name
|
69
|
+
end
|
70
|
+
|
71
|
+
else
|
72
|
+
puts "Go Fish!\n"
|
73
|
+
drawn = the_deck("Go Fish").draw_into current_player
|
74
|
+
puts "You drew a #{drawn.name}."
|
75
|
+
|
76
|
+
# Check for matches.
|
77
|
+
matching_sets = current_player.group_by { |c| c[:value] }.select { |k, v| v.size >= 4 }
|
78
|
+
matching_sets.each do |matching_value, cards|
|
79
|
+
matching_card = cards.first
|
80
|
+
puts "Good fortune! You have a matching set of #{matching_card[:face]}s!"
|
81
|
+
puts "You get another turn."
|
82
|
+
another_turn = true
|
83
|
+
current_player.remove_card_if { |c| c[:value] == matching_value }
|
84
|
+
score current_player.name
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
another_turn
|
90
|
+
end
|
91
|
+
|
92
|
+
begin
|
93
|
+
puts "\nBeginning a new turn!"
|
94
|
+
|
95
|
+
begin
|
96
|
+
another_turn = begin_new_turn
|
97
|
+
|
98
|
+
if the_deck("Go Fish").empty?
|
99
|
+
puts "\nThe deck is empty!\n"
|
100
|
+
break
|
101
|
+
end
|
102
|
+
break if current_player.empty?
|
103
|
+
|
104
|
+
end while another_turn
|
105
|
+
players.rotate!
|
106
|
+
current_player = players.first
|
107
|
+
|
108
|
+
if the_deck("Go Fish").empty?
|
109
|
+
break
|
110
|
+
end
|
111
|
+
break if current_player.empty?
|
112
|
+
|
113
|
+
end until players.any? { |p| p.size < 1 }
|
114
|
+
|
115
|
+
puts "\n\nThe game has ended!"
|
116
|
+
|
117
|
+
winner = scores.max_by { |p, s| s }.first
|
118
|
+
puts "#{winner} is the winner!"
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# Represents a game card. Best used with the Card and Deck DSL. See Cardlike.
|
3
|
+
#
|
4
|
+
class Cardlike::Card
|
5
|
+
attr_accessor :name, :text
|
6
|
+
|
7
|
+
#
|
8
|
+
# Create an instance of a Card. Arguments are a hash that should include
|
9
|
+
# +:name+ at a minimum. Optionally all cards have a +text+ accessor that can
|
10
|
+
# be set in the constructor by including the option +:text+. Perhaps a better
|
11
|
+
# idea is to use Card.create or Cardlike.card.
|
12
|
+
#
|
13
|
+
def initialize(options={})
|
14
|
+
self.name = options[:name]
|
15
|
+
self.text = options[:text]
|
16
|
+
@properties = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Factory method to create an instance of a Card. This sets the name of the
|
21
|
+
# card and its properties using the Card DSL in the corresponding block. You
|
22
|
+
# can also use Cardlike.card, which does the same thing. Probably more useful
|
23
|
+
# is to create custom card types using Cardlike.type_of_card and create them
|
24
|
+
# using the +new_+ methods.
|
25
|
+
#
|
26
|
+
# Card.create "Big Monster" do
|
27
|
+
# text "Spend 1 Mana to Attack"
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Preferred over Card.new.
|
31
|
+
#
|
32
|
+
def self.create(name, &block)
|
33
|
+
c = self.new(name: name)
|
34
|
+
c.instance_eval(&block) if block_given?
|
35
|
+
c
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# DSL method for setting the card text. Still works as a getter if no args are
|
40
|
+
# passed.
|
41
|
+
#
|
42
|
+
def text(card_text=nil)
|
43
|
+
return @text unless card_text
|
44
|
+
@text = card_text
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Return custom properties of this Card. Custom properties can be set using
|
49
|
+
# the Card.has method, ideally inside a Cardlike.type_of_card block.
|
50
|
+
#
|
51
|
+
# @king_of_diamonds[:suit] # => 'Diamonds'
|
52
|
+
#
|
53
|
+
def [](prop)
|
54
|
+
@properties[prop]
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Class DSL method for setting custom properties for a Card. See
|
59
|
+
# Cardlike.type_of_card.
|
60
|
+
#
|
61
|
+
def self.has(prop)
|
62
|
+
define_method(prop, lambda { |arg| raise "Cards are immutable." if @properties.has_key? prop; @properties[prop] = arg })
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
t = []
|
67
|
+
t << "Name: #{name}"
|
68
|
+
t << "Text: #{text}" if text
|
69
|
+
@properties.each { |p,v| t << "#{p}: #{v}" }
|
70
|
+
t.join("\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# Represents a game deck. Best used with the Card and Deck DSL. See Cardlike.
|
3
|
+
#
|
4
|
+
class Cardlike::Deck < Array
|
5
|
+
attr_accessor :name
|
6
|
+
|
7
|
+
#
|
8
|
+
# Create a new Deck. Options should always include +:name+ and may optionally
|
9
|
+
# include +:cards+, which is an array of Card objects (ideally) to keep in
|
10
|
+
# this deck. It's better to use Cardlike.deck to create decks, though.
|
11
|
+
#
|
12
|
+
def initialize(options={})
|
13
|
+
self.name = options[:name]
|
14
|
+
options[:cards].each { |c| self << c } if options[:cards]
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Draw the top card from this deck and return it.
|
19
|
+
#
|
20
|
+
def draw
|
21
|
+
self.pop
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Shuffle this deck and return a copy Deck. Probably more useful is
|
26
|
+
# Deck#shuffle! which will shuffle this deck in place.
|
27
|
+
#
|
28
|
+
def shuffle
|
29
|
+
self.dup.shuffle!
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Append an array (or Deck) of cards to this Deck.
|
34
|
+
#
|
35
|
+
def +(ary)
|
36
|
+
ary.each { |a| self << a }
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# DSL method to add a pre-defined card to this Deck. This inserts the unique
|
42
|
+
# card into the deck. If you want to put a copy into the deck (or several
|
43
|
+
# copies), use Deck#copy_card instead.
|
44
|
+
#
|
45
|
+
# Cardlike.game do
|
46
|
+
# card "Super Strike"
|
47
|
+
#
|
48
|
+
# deck "My Deck" do
|
49
|
+
# include_card "Super Strike"
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def include_card(name)
|
54
|
+
raise "Card '#{name}' not found." unless card = Cardlike.the_card(name)
|
55
|
+
self << card
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# DSL method to add a duplicate of a pre-defined card to this Deck.
|
60
|
+
#
|
61
|
+
# Cardlike.game do
|
62
|
+
# card "Super Strike"
|
63
|
+
#
|
64
|
+
# deck "My Deck" do
|
65
|
+
# 4.times { copy_card "Super Strike" }
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
def copy_card(name)
|
70
|
+
raise "Card '#{name}' not found." unless card = Cardlike.the_card(name)
|
71
|
+
copy = card.dup
|
72
|
+
self << copy
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# DSL method to create a new card inside this deck. Also check out the +new_+
|
77
|
+
# methods created by Cardlike.type_of_card. This works just like Cardlike.card
|
78
|
+
# except that it automatically adds the card to this deck.
|
79
|
+
#
|
80
|
+
# Cardlike.deck "Options Deck" do
|
81
|
+
# card "Magic Spell"
|
82
|
+
# card "Arcane Mark"
|
83
|
+
# card "Simple Attack"
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
def card(name, &block)
|
87
|
+
c = Cardlike.card(name, &block)
|
88
|
+
self << c
|
89
|
+
c
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Like Deck#draw except that it draws a card and then adds it to a Deck or
|
94
|
+
# Hand. Also returns the card drawn.
|
95
|
+
#
|
96
|
+
# Cardlike.the_deck("Poker Deck").draw_into(Cardlike.the_hand("Player 1"))
|
97
|
+
#
|
98
|
+
def draw_into(deck)
|
99
|
+
card = draw
|
100
|
+
deck << card
|
101
|
+
card
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#
|
2
|
+
# Represents a game hand. Best used with the Card and Deck DSL. See Cardlike and
|
3
|
+
# Cardlike::Deck, from which this inherits.
|
4
|
+
#
|
5
|
+
class Cardlike::Hand < Cardlike::Deck
|
6
|
+
#
|
7
|
+
# Remove and return a Card from this hand by name.
|
8
|
+
#
|
9
|
+
def remove_card(card_name)
|
10
|
+
self.delete(self.select { |card| card.name == card_name }.first)
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Remove cards for which the block evaluates to true, returning removed cards
|
15
|
+
# in an Array. If no cards are found, returns an empty array.
|
16
|
+
#
|
17
|
+
def remove_card_if(&block)
|
18
|
+
matches = self.select { |card| yield(card) }
|
19
|
+
matches.collect { |match| self.delete(match) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
puts "Hand: #{name}"
|
24
|
+
self.each do |card|
|
25
|
+
puts "-> #{card}\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|