cardlike 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +29 -3
- data/lib/cardlike.rb +3 -5
- data/lib/cardlike/card.rb +23 -21
- data/lib/cardlike/hand.rb +23 -5
- data/lib/cardlike/score.rb +1 -1
- data/lib/cardlike/version.rb +1 -1
- data/spec/integration/card_construction_spec.rb +38 -12
- data/spec/integration/dealing_spec.rb +66 -24
- data/spec/units/hand_spec.rb +44 -2
- metadata +2 -2
data/README.md
CHANGED
@@ -69,7 +69,8 @@ to define your whole game in a `Cardlike.game` block.
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
You can also prefix the class methods with Cardlike
|
72
|
+
You can also prefix the class methods with Cardlike and pass card, deck, and
|
73
|
+
hand objects around yourself.
|
73
74
|
|
74
75
|
Cardlike.type_of_card :playing_card do
|
75
76
|
has :suit
|
@@ -88,7 +89,8 @@ You can also prefix the class methods with Cardlike.
|
|
88
89
|
### Creating Cards
|
89
90
|
|
90
91
|
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
|
+
stored globally and can be accessed with `the_card` and the card's name (unless
|
93
|
+
they were created directly in a Deck).
|
92
94
|
|
93
95
|
Cardlike.card "Draw one Play one" # => creates (and returns) a new Card object
|
94
96
|
Cardlike.the_card "Draw one Play one" # => the Card object created above (NOT a copy)
|
@@ -100,23 +102,37 @@ to do this.
|
|
100
102
|
Cardlike.type_of_card :action_card do
|
101
103
|
has :power_level
|
102
104
|
has :color
|
105
|
+
has :speed
|
106
|
+
has_block :activate
|
103
107
|
end
|
104
108
|
|
105
109
|
Cardlike.new_action_card "Magic Spell" do
|
106
110
|
power_level 5
|
107
111
|
color :red
|
112
|
+
speed :fast
|
113
|
+
activate { "zzzzZZZZAP!!" }
|
108
114
|
end # => new Card object (actually a new ActionCard object)
|
109
115
|
|
110
116
|
Cardlike.the_card("Magic Spell")[:color] # => :red
|
111
117
|
Cardlike.the_card("Magic Spell")[:power_level] # => 5
|
118
|
+
Cardlike.the_card("Magic Spell").speed # => :fast (this style available after 0.0.2)
|
119
|
+
Cardlike.the_card("Magic Spell").activate.call # => "zzzzZZZZAP!!" (this style available after 0.0.2)
|
112
120
|
|
113
121
|
The `type_of_card` method creates a new subclass of Card. Use the `has` method in
|
114
122
|
a `card` block to add a property. A new card creation method is added to the DSL
|
115
123
|
with the type of card prefixed by `new_`.
|
116
124
|
|
125
|
+
A `has_block` method takes a block and stores it as a property on the Card.
|
126
|
+
|
117
127
|
To assign a property to a new card in the card block, use a method of the same
|
118
128
|
name as the property. To retrieve that property, treat the card as a hash and
|
119
|
-
reference the property name as the key.
|
129
|
+
reference the property name as the key. As of version 0.0.2, you can also get
|
130
|
+
the property value by calling a getter method of the same name as the key.
|
131
|
+
|
132
|
+
If you'd like to know what kind of card you're dealing with, just ask it with
|
133
|
+
`card_type`.
|
134
|
+
|
135
|
+
Cardlike.the_card("Magic Spell").card_type # => :action_card
|
120
136
|
|
121
137
|
### Creating Decks
|
122
138
|
|
@@ -213,6 +229,16 @@ Hands can be accessed by `the_hand`, just like the other Cardlike objects.
|
|
213
229
|
the_hand("Player 1").size # => 1
|
214
230
|
end
|
215
231
|
|
232
|
+
A Hand also has a few tricks up its sleve:
|
233
|
+
|
234
|
+
Cardlike.game do
|
235
|
+
the_hand("Player 1").remove_card_at(1) # Removes and returns the card at index 1 (0-based index)
|
236
|
+
|
237
|
+
the_hand("Player 1").remove_random_card # Removes and returns a random card from the hand.
|
238
|
+
|
239
|
+
the_hand("Player 1").remove_random_card { |card| card.name =~ /^Magic/ } # Removes and returns a random card from the cards in the hand for which the block returns true.
|
240
|
+
end
|
241
|
+
|
216
242
|
### Defining a Turn
|
217
243
|
|
218
244
|
As most games have turns, you can define a block of code as a turn using the
|
data/lib/cardlike.rb
CHANGED
@@ -97,9 +97,7 @@ module Cardlike
|
|
97
97
|
# the Card. Returns the Card (that can also be accessed with +the_card+). You
|
98
98
|
# may use the Card DSL in the block.
|
99
99
|
#
|
100
|
-
# Cardlike.card "Fire Monster"
|
101
|
-
# text "A red-hot monster."
|
102
|
-
# end
|
100
|
+
# Cardlike.card "Fire Monster"
|
103
101
|
#
|
104
102
|
def self.card(name, &block)
|
105
103
|
c = Card.create(name, &block)
|
@@ -145,8 +143,8 @@ module Cardlike
|
|
145
143
|
c = Object.const_get(klass_name)
|
146
144
|
c.class_eval(&block) if block_given?
|
147
145
|
|
148
|
-
Deck.send(:define_method, "new_#{name_underscored}", lambda { |arg, &blk| card = c.create(arg, &blk); self << card; card })
|
149
|
-
self.class.send(:define_method, "new_#{name_underscored}", lambda { |arg, &blk| card = c.create(arg, &blk); @cards ||= {}; @cards[arg] = card; card })
|
146
|
+
Deck.send(:define_method, "new_#{name_underscored}", lambda { |arg, &blk| card = c.create(arg, &blk); card.card_type = name_underscored.to_sym; self << card; card })
|
147
|
+
self.class.send(:define_method, "new_#{name_underscored}", lambda { |arg, &blk| card = c.create(arg, &blk); card.card_type = name_underscored.to_sym; @cards ||= {}; @cards[arg] = card; card })
|
150
148
|
|
151
149
|
c
|
152
150
|
end
|
data/lib/cardlike/card.rb
CHANGED
@@ -2,20 +2,26 @@
|
|
2
2
|
# Represents a game card. Best used with the Card and Deck DSL. See Cardlike.
|
3
3
|
#
|
4
4
|
class Cardlike::Card
|
5
|
-
attr_accessor :name
|
5
|
+
attr_accessor :name
|
6
|
+
|
7
|
+
attr_reader :card_type
|
6
8
|
|
7
9
|
#
|
8
10
|
# Create an instance of a Card. Arguments are a hash that should include
|
9
|
-
# +:name+ at a minimum.
|
10
|
-
#
|
11
|
-
# idea is to use Card.create or Cardlike.card.
|
11
|
+
# +:name+ at a minimum. Perhaps a better idea is to use Card.create or
|
12
|
+
# Cardlike.card. Also see Cardlike.type_of_card.
|
12
13
|
#
|
13
14
|
def initialize(options={})
|
14
15
|
self.name = options[:name]
|
15
|
-
|
16
|
+
@card_type = options[:card_type] || nil
|
16
17
|
@properties = {}
|
17
18
|
end
|
18
19
|
|
20
|
+
def card_type=(type)
|
21
|
+
raise "Cannot re-set type after it has been set." if @card_type
|
22
|
+
@card_type = type
|
23
|
+
end
|
24
|
+
|
19
25
|
#
|
20
26
|
# Factory method to create an instance of a Card. This sets the name of the
|
21
27
|
# card and its properties using the Card DSL in the corresponding block. You
|
@@ -23,9 +29,7 @@ class Cardlike::Card
|
|
23
29
|
# is to create custom card types using Cardlike.type_of_card and create them
|
24
30
|
# using the +new_+ methods.
|
25
31
|
#
|
26
|
-
# Card.create "Big Monster"
|
27
|
-
# text "Spend 1 Mana to Attack"
|
28
|
-
# end
|
32
|
+
# Card.create "Big Monster"
|
29
33
|
#
|
30
34
|
# Preferred over Card.new.
|
31
35
|
#
|
@@ -35,15 +39,6 @@ class Cardlike::Card
|
|
35
39
|
c
|
36
40
|
end
|
37
41
|
|
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
42
|
#
|
48
43
|
# Return custom properties of this Card. Custom properties can be set using
|
49
44
|
# the Card.has method, ideally inside a Cardlike.type_of_card block.
|
@@ -54,20 +49,27 @@ class Cardlike::Card
|
|
54
49
|
@properties[prop]
|
55
50
|
end
|
56
51
|
|
52
|
+
#
|
53
|
+
# Class DSL method for setting custom properties for a Card that include a
|
54
|
+
# block. See Cardlike.type_of_card.
|
55
|
+
#
|
56
|
+
def self.has_block(prop)
|
57
|
+
define_method(prop, lambda { |&block| return @properties[prop] unless block; raise "Cards are immutable." if @properties.has_key? prop; @properties[prop] = block })
|
58
|
+
end
|
59
|
+
|
57
60
|
#
|
58
61
|
# Class DSL method for setting custom properties for a Card. See
|
59
62
|
# Cardlike.type_of_card.
|
60
63
|
#
|
61
64
|
def self.has(prop)
|
62
|
-
define_method(prop, lambda { |arg| raise "Cards are immutable." if @properties.has_key? prop; @properties[prop] = arg })
|
65
|
+
define_method(prop, lambda { |arg=nil| return @properties[prop] unless arg; raise "Cards are immutable." if @properties.has_key? prop; @properties[prop] = arg })
|
63
66
|
end
|
64
67
|
|
65
68
|
def to_s
|
66
69
|
t = []
|
67
70
|
t << "Name: #{name}"
|
68
|
-
t << "
|
69
|
-
|
70
|
-
t.join("\n")
|
71
|
+
@properties.each { |p,v| t << "#{p}: #{v}" unless v.nil? or v.is_a? Proc }
|
72
|
+
"[ "+t.join("; ")+" ]"
|
71
73
|
end
|
72
74
|
|
73
75
|
end
|
data/lib/cardlike/hand.rb
CHANGED
@@ -7,7 +7,26 @@ class Cardlike::Hand < Cardlike::Deck
|
|
7
7
|
# Remove and return a Card from this hand by name.
|
8
8
|
#
|
9
9
|
def remove_card(card_name)
|
10
|
-
self.delete(self.
|
10
|
+
self.delete(self.find { |card| card.name == card_name })
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Remove and return a Card from this hand at the index.
|
15
|
+
#
|
16
|
+
def remove_card_at(index)
|
17
|
+
self.delete_at(index)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove_random_card(&block)
|
21
|
+
if block
|
22
|
+
options = []
|
23
|
+
self.each { |c| options << c if block.call(c) }
|
24
|
+
c = options.sample
|
25
|
+
return self.delete(c)
|
26
|
+
else
|
27
|
+
c = self.sample
|
28
|
+
return self.delete(c)
|
29
|
+
end
|
11
30
|
end
|
12
31
|
|
13
32
|
#
|
@@ -20,9 +39,8 @@ class Cardlike::Hand < Cardlike::Deck
|
|
20
39
|
end
|
21
40
|
|
22
41
|
def to_s
|
23
|
-
|
24
|
-
self.
|
25
|
-
|
26
|
-
end
|
42
|
+
cards = ["Hand: #{name}"]
|
43
|
+
cards += self.collect { |c| "-> #{c}" }
|
44
|
+
cards.join("\n")
|
27
45
|
end
|
28
46
|
end
|
data/lib/cardlike/score.rb
CHANGED
data/lib/cardlike/version.rb
CHANGED
@@ -5,9 +5,7 @@ describe 'Defining a card' do
|
|
5
5
|
|
6
6
|
context "for a basic card" do
|
7
7
|
before do
|
8
|
-
@card = Cardlike.card "Fire Monster"
|
9
|
-
text "A red-hot monster."
|
10
|
-
end
|
8
|
+
@card = Cardlike.card "Fire Monster"
|
11
9
|
end
|
12
10
|
|
13
11
|
it "creates a Card object" do
|
@@ -17,10 +15,6 @@ describe 'Defining a card' do
|
|
17
15
|
it "properly sets the card name" do
|
18
16
|
@card.name.should eq "Fire Monster"
|
19
17
|
end
|
20
|
-
|
21
|
-
it "properly sets the card text" do
|
22
|
-
@card.text.should eq "A red-hot monster."
|
23
|
-
end
|
24
18
|
end
|
25
19
|
|
26
20
|
end
|
@@ -45,7 +39,7 @@ describe 'Defining a card' do
|
|
45
39
|
@playing_card.name.should eq "PlayingCard"
|
46
40
|
end
|
47
41
|
|
48
|
-
it "creates an accessor for the custom
|
42
|
+
it "creates an accessor for the custom property" do
|
49
43
|
@playing_card.new.should respond_to :suit
|
50
44
|
end
|
51
45
|
|
@@ -64,9 +58,17 @@ describe 'Defining a card' do
|
|
64
58
|
@card.should be_a_kind_of Cardlike::Card
|
65
59
|
end
|
66
60
|
|
67
|
-
it "creates an accessor for the custom
|
61
|
+
it "creates an accessor for the custom property" do
|
68
62
|
@card.should respond_to :suit
|
69
63
|
end
|
64
|
+
|
65
|
+
it "responds to the Card.card_type method with the type" do
|
66
|
+
@card.card_type.should eq :playing_card
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does not allow setting of card_type" do
|
70
|
+
lambda { @card.card_type = 'Special Card' }.should raise_error(StandardError)
|
71
|
+
end
|
70
72
|
end
|
71
73
|
|
72
74
|
context "when instantiated" do
|
@@ -81,7 +83,7 @@ describe 'Defining a card' do
|
|
81
83
|
@card.should be_a_kind_of Cardlike::Card
|
82
84
|
end
|
83
85
|
|
84
|
-
it "creates an accessor for the custom
|
86
|
+
it "creates an accessor for the custom property" do
|
85
87
|
@card.should respond_to :suit
|
86
88
|
end
|
87
89
|
|
@@ -90,11 +92,15 @@ describe 'Defining a card' do
|
|
90
92
|
@card2.should_not respond_to :suit
|
91
93
|
end
|
92
94
|
|
93
|
-
it "sets the
|
95
|
+
it "sets the property properly" do
|
94
96
|
@card[:suit].should eq 'Spades'
|
95
97
|
end
|
96
98
|
|
97
|
-
it "
|
99
|
+
it "also returns the property from the accessor version of the setter" do
|
100
|
+
@card.suit.should eq 'Spades'
|
101
|
+
end
|
102
|
+
|
103
|
+
it "does not allow properties to be set again" do
|
98
104
|
lambda { @card.suit 'Clubs' }.should raise_error(StandardError)
|
99
105
|
end
|
100
106
|
|
@@ -102,4 +108,24 @@ describe 'Defining a card' do
|
|
102
108
|
|
103
109
|
end
|
104
110
|
|
111
|
+
context "that includes a block" do
|
112
|
+
before do
|
113
|
+
Cardlike.game do
|
114
|
+
type_of_card :monster_card do
|
115
|
+
has_block :action
|
116
|
+
end
|
117
|
+
|
118
|
+
new_monster_card "Ogre" do
|
119
|
+
action do
|
120
|
+
:action_complete
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it "completes the action" do
|
127
|
+
Cardlike.the_card("Ogre").action.call.should eq :action_complete
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
105
131
|
end
|
@@ -1,42 +1,84 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'A deck of cards' do
|
4
|
-
|
5
|
-
@cards = []
|
6
|
-
@cards << Cardlike::Card.new(name: 'Leonardo', text: 'Has two swords.')
|
7
|
-
@cards << Cardlike::Card.new(name: 'Donatello', text: 'Has a Bo staff.')
|
8
|
-
@cards << Cardlike::Card.new(name: 'Michaelangelo', text: 'Has Nunchuku.')
|
9
|
-
@cards << Cardlike::Card.new(name: 'Raphael', text: 'Has Sai.')
|
10
|
-
|
11
|
-
@deck = Cardlike::Deck.new(name: 'Turtles', cards: @cards)
|
12
|
-
end
|
13
|
-
|
14
|
-
context 'when a card is drawn' do
|
4
|
+
context "for a simple deck" do
|
15
5
|
before do
|
16
|
-
@
|
6
|
+
@cards = []
|
7
|
+
@cards << Cardlike::Card.new(name: 'Leonardo')
|
8
|
+
@cards << Cardlike::Card.new(name: 'Donatello')
|
9
|
+
@cards << Cardlike::Card.new(name: 'Michaelangelo')
|
10
|
+
@cards << Cardlike::Card.new(name: 'Raphael')
|
11
|
+
|
12
|
+
@deck = Cardlike::Deck.new(name: 'Turtles', cards: @cards)
|
17
13
|
end
|
18
14
|
|
19
|
-
|
20
|
-
|
15
|
+
context 'when a card is drawn' do
|
16
|
+
before do
|
17
|
+
@drawn = @deck.draw
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is one of the cards from the deck' do
|
21
|
+
@cards.should include @drawn
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is no longer in the deck' do
|
25
|
+
@deck.should_not include @drawn
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
|
-
|
24
|
-
|
29
|
+
context "when a card is drawn into a Hand" do
|
30
|
+
before do
|
31
|
+
@hand = Cardlike::Hand.new(name: 'Player 1')
|
32
|
+
@drawn = @deck.draw_into @hand
|
33
|
+
end
|
34
|
+
|
35
|
+
it "inserts the card into the Hand" do
|
36
|
+
@hand.should include @drawn
|
37
|
+
end
|
38
|
+
|
39
|
+
it "removes the card from the Deck" do
|
40
|
+
@deck.should_not include @drawn
|
41
|
+
end
|
25
42
|
end
|
26
43
|
end
|
27
44
|
|
28
|
-
context "
|
45
|
+
context "for a deck of custom cards" do
|
29
46
|
before do
|
30
|
-
|
31
|
-
|
32
|
-
|
47
|
+
Cardlike.game do
|
48
|
+
type_of_card :monster_card do
|
49
|
+
has :strength
|
50
|
+
end
|
33
51
|
|
34
|
-
|
35
|
-
|
52
|
+
deck "Monsters" do
|
53
|
+
new_monster_card("Giant Snake") { strength 5 }
|
54
|
+
new_monster_card("Giant Spider") { strength 3 }
|
55
|
+
new_monster_card("Giant Giant") { strength 8 }
|
56
|
+
new_monster_card("Giant Cow") { strength 2 }
|
57
|
+
end
|
58
|
+
end
|
36
59
|
end
|
37
60
|
|
38
|
-
|
39
|
-
|
61
|
+
context "when a card is drawn into a Hand" do
|
62
|
+
before do
|
63
|
+
@top_card = Cardlike.game do
|
64
|
+
hand "My Hand"
|
65
|
+
top_card = the_deck("Monsters").last
|
66
|
+
the_deck("Monsters").draw_into the_hand("My Hand")
|
67
|
+
top_card
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "removes a card from the deck" do
|
72
|
+
Cardlike.the_deck("Monsters").size.should eq 3
|
73
|
+
end
|
74
|
+
|
75
|
+
it "adds a card to the Hand" do
|
76
|
+
Cardlike.the_hand("My Hand").size.should eq 1
|
77
|
+
end
|
78
|
+
|
79
|
+
it "the drawn card is the top card from the deck" do
|
80
|
+
@top_card.should eq Cardlike.the_hand("My Hand").first
|
81
|
+
end
|
40
82
|
end
|
41
83
|
end
|
42
84
|
end
|
data/spec/units/hand_spec.rb
CHANGED
@@ -19,7 +19,7 @@ describe "A hand of cards" do
|
|
19
19
|
Cardlike.the_hand("Player 1").first.name.should eq "Boring Card One"
|
20
20
|
end
|
21
21
|
|
22
|
-
context "when removing a
|
22
|
+
context "when removing a card by name" do
|
23
23
|
before do
|
24
24
|
@card1 = Cardlike.the_hand("Player 1").remove_card "Boring Card One"
|
25
25
|
end
|
@@ -33,7 +33,49 @@ describe "A hand of cards" do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
context "when removing a card
|
36
|
+
context "when removing a card at random" do
|
37
|
+
before do
|
38
|
+
@card1 = Cardlike.the_hand("Player 1").remove_random_card
|
39
|
+
end
|
40
|
+
|
41
|
+
it "removes a Card" do
|
42
|
+
@card1.should be_a_kind_of Cardlike::Card
|
43
|
+
end
|
44
|
+
|
45
|
+
it "removes the card from the Hand" do
|
46
|
+
Cardlike.the_hand("Player 1").size.should eq 3
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when removing a card at random with specific parameters (a block)" do
|
51
|
+
before do
|
52
|
+
@card1 = Cardlike.the_hand("Player 1").remove_random_card { |c| c.name =~ /^Boring Card T/ }
|
53
|
+
end
|
54
|
+
|
55
|
+
it "removes a card matching the parameters" do
|
56
|
+
@card1.name.should satisfy { |s| ["Boring Card Two", "Boring Card Three"].include?(s) }
|
57
|
+
end
|
58
|
+
|
59
|
+
it "removes the card from the Hand" do
|
60
|
+
Cardlike.the_hand("Player 1").size.should eq 3
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when removing a card by index" do
|
65
|
+
before do
|
66
|
+
@card1 = Cardlike.the_hand("Player 1").remove_card_at 3
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can remove a specific card" do
|
70
|
+
@card1.name.should eq "Boring Card Four"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "removes the card from the Hand" do
|
74
|
+
Cardlike.the_hand("Player 1").size.should eq 3
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when removing cards with remove_card_if" do
|
37
79
|
before do
|
38
80
|
@card1 = Cardlike.the_hand("Player 1").remove_card_if { |c| c.name =~ /Three/ }.first
|
39
81
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cardlike
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|