cardlike 0.0.1 → 0.1.0

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.
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" do
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, :text
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. 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.
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
- self.text = options[:text]
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" do
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 << "Text: #{text}" if text
69
- @properties.each { |p,v| t << "#{p}: #{v}" }
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.select { |card| card.name == card_name }.first)
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
- puts "Hand: #{name}"
24
- self.each do |card|
25
- puts "-> #{card}\n"
26
- end
42
+ cards = ["Hand: #{name}"]
43
+ cards += self.collect { |c| "-> #{c}" }
44
+ cards.join("\n")
27
45
  end
28
46
  end
@@ -34,7 +34,7 @@ module Cardlike
34
34
  #
35
35
  def self.the_score(target)
36
36
  @scores ||= {}
37
- @scores[target]
37
+ @scores[target] || 0
38
38
  end
39
39
 
40
40
  #
@@ -1,3 +1,3 @@
1
1
  module Cardlike
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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" do
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 field" do
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 field" do
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 field" do
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 field properly" do
95
+ it "sets the property properly" do
94
96
  @card[:suit].should eq 'Spades'
95
97
  end
96
98
 
97
- it "does not allow fields to be set again" do
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
- before do
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
- @drawn = @deck.draw
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
- it 'is one of the cards from the deck' do
20
- @cards.should include @drawn
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
- it 'is no longer in the deck' do
24
- @deck.should_not include @drawn
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 "when a card is drawn into a Hand" do
45
+ context "for a deck of custom cards" do
29
46
  before do
30
- @hand = Cardlike::Hand.new(name: 'Player 1')
31
- @drawn = @deck.draw_into @hand
32
- end
47
+ Cardlike.game do
48
+ type_of_card :monster_card do
49
+ has :strength
50
+ end
33
51
 
34
- it "inserts the card into the Hand" do
35
- @hand.should include @drawn
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
- it "removes the card from the Deck" do
39
- @deck.should_not include @drawn
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
@@ -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 specific card" do
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 with remove_card_if" do
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.1
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 00:00:00.000000000 Z
12
+ date: 2013-02-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec