patience 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.
Files changed (56) hide show
  1. data/CHANGELOG.md +6 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +17 -0
  4. data/LICENSE +19 -0
  5. data/README.md +111 -0
  6. data/Rakefile +11 -0
  7. data/bin/patience +5 -0
  8. data/lib/patience.rb +13 -0
  9. data/lib/patience/area.rb +62 -0
  10. data/lib/patience/card.rb +107 -0
  11. data/lib/patience/core_ext/class.rb +17 -0
  12. data/lib/patience/core_ext/core_ext.rb +1 -0
  13. data/lib/patience/core_ext/object.rb +37 -0
  14. data/lib/patience/core_ext/string.rb +33 -0
  15. data/lib/patience/cursor.rb +66 -0
  16. data/lib/patience/deck.rb +21 -0
  17. data/lib/patience/event_handlers/click.rb +99 -0
  18. data/lib/patience/event_handlers/drag.rb +36 -0
  19. data/lib/patience/event_handlers/drop.rb +147 -0
  20. data/lib/patience/foundation.rb +30 -0
  21. data/lib/patience/game.rb +10 -0
  22. data/lib/patience/pile.rb +78 -0
  23. data/lib/patience/processable.rb +87 -0
  24. data/lib/patience/rank.rb +56 -0
  25. data/lib/patience/scenes/game_scene.rb +66 -0
  26. data/lib/patience/sprites/card_deck.png +0 -0
  27. data/lib/patience/sprites/empty_stock.png +0 -0
  28. data/lib/patience/sprites/pile_background.png +0 -0
  29. data/lib/patience/stock.rb +20 -0
  30. data/lib/patience/suit.rb +73 -0
  31. data/lib/patience/tableau.rb +42 -0
  32. data/lib/patience/version.rb +3 -0
  33. data/lib/patience/waste.rb +18 -0
  34. data/test/patience/core_ext/test_class.rb +28 -0
  35. data/test/patience/core_ext/test_object.rb +15 -0
  36. data/test/patience/core_ext/test_string.rb +35 -0
  37. data/test/patience/event_handlers/test_click.rb +142 -0
  38. data/test/patience/event_handlers/test_drag.rb +45 -0
  39. data/test/patience/event_handlers/test_drop.rb +175 -0
  40. data/test/patience/helper.rb +8 -0
  41. data/test/patience/scenes/test_game_scene.rb +14 -0
  42. data/test/patience/test_area.rb +74 -0
  43. data/test/patience/test_card.rb +165 -0
  44. data/test/patience/test_cursor.rb +77 -0
  45. data/test/patience/test_deck.rb +53 -0
  46. data/test/patience/test_foundation.rb +38 -0
  47. data/test/patience/test_game.rb +29 -0
  48. data/test/patience/test_pile.rb +83 -0
  49. data/test/patience/test_processable.rb +159 -0
  50. data/test/patience/test_rank.rb +88 -0
  51. data/test/patience/test_stock.rb +43 -0
  52. data/test/patience/test_suit.rb +87 -0
  53. data/test/patience/test_tableau.rb +57 -0
  54. data/test/patience/test_version.rb +11 -0
  55. data/test/patience/test_waste.rb +35 -0
  56. metadata +135 -0
@@ -0,0 +1,142 @@
1
+ require_relative '../helper'
2
+
3
+ module Patience
4
+ class TestClick < TestCase
5
+
6
+ class Dummy < EventHandler::Click
7
+ attr_accessor :area, :pile, :card, :cards, :scenario
8
+
9
+ def initialize(mouse_pos, areas)
10
+ @mouse_pos = mouse_pos
11
+ @areas = areas
12
+ end
13
+ end
14
+
15
+ def setup
16
+ @mouse_pos_missclick = Ray::Vector2[0, 0]
17
+ @mouse_pos_hit = Ray::Vector2[32, 166]
18
+ @deck = Deck.new
19
+ @areas = { :tableau => Tableau.new(@deck.shuffle_off! 28),
20
+ :waste => Waste.new,
21
+ :stock => Stock.new(@deck.shuffle_off! 10) }
22
+ @dummy_click_miss = Dummy.new(@mouse_pos_missclick, @areas)
23
+ @dummy_click_hit = Dummy.new(@mouse_pos_hit, @areas)
24
+ @stock_hit = Dummy.new(Ray::Vector2[32, 24], @areas)
25
+ end
26
+
27
+ def fill_attributes_up(click)
28
+ click.area = click.send(:detect_area)
29
+ click.pile = click.send(:detect_pile)
30
+ cards = click.send(:collect_cards)
31
+ click.cards = cards.map(&:first) if cards
32
+ click.card = click.cards.first
33
+ end
34
+
35
+ test 'An area can be selected by click' do
36
+ assert_nil @dummy_click_hit.area
37
+ @dummy_click_hit.area = @dummy_click_hit.send(:detect_area)
38
+ assert_instance_of Tableau, @dummy_click_hit.area
39
+ assert_kind_of Area, @dummy_click_hit.area
40
+
41
+ assert_nil @dummy_click_miss.area
42
+ @dummy_click_miss.area = @dummy_click_miss.send(:detect_area)
43
+ assert_nil @dummy_click_miss.area
44
+ end
45
+
46
+ test 'A pile can be selected by click' do
47
+ assert_nil @dummy_click_hit.pile
48
+ @dummy_click_hit.pile = @dummy_click_hit.send(:detect_pile)
49
+ assert_instance_of Pile, @dummy_click_hit.pile
50
+
51
+ assert_nil @dummy_click_miss.pile
52
+ @dummy_click_miss.pile = @dummy_click_miss.send(:detect_pile)
53
+ assert_nil @dummy_click_miss.pile
54
+ end
55
+
56
+ test 'A card can be selected by click' do
57
+ # Hit
58
+ tableau_click = Dummy.new(Ray::Vector2[472, 270], @areas)
59
+ assert_nil tableau_click.card
60
+ assert_nil tableau_click.cards
61
+ tableau_click.pile = @areas[:tableau].piles[4]
62
+
63
+ [Card.new(3, 1), Card.new(2, 4)].each do |card|
64
+ @areas[:tableau].piles[4] << card
65
+ card.pos = @areas[:tableau].piles[4].cards[-2].pos + [0, 20]
66
+ end
67
+
68
+ tableau_click.cards = tableau_click.send(:collect_cards).map(&:first)
69
+ assert tableau_click.cards
70
+ assert_instance_of Card::Rank::Four, tableau_click.cards[0].rank
71
+ assert_instance_of Card::Rank::Three, tableau_click.cards[1].rank
72
+ assert_instance_of Card::Rank::Two, tableau_click.cards[2].rank
73
+
74
+ # Miss
75
+ assert_nil @dummy_click_miss.card
76
+ assert_nil @dummy_click_miss.cards
77
+ @dummy_click_miss.cards = @dummy_click_miss.send(:collect_cards)
78
+ assert_nil @dummy_click_miss.card
79
+ assert_nil @dummy_click_miss.cards
80
+ end
81
+
82
+ test 'Stock can be refilled by a click' do
83
+ assert_equal 10, @areas[:stock].cards.size
84
+ assert_equal 0, @areas[:waste].cards.size
85
+
86
+ @areas[:stock].cards.each do |card|
87
+ @areas[:waste].piles[0] << card
88
+ end
89
+ @areas[:stock].piles[0].cards.clear
90
+
91
+ assert_equal 0, @areas[:stock].cards.size
92
+ assert_equal 10, @areas[:waste].cards.size
93
+
94
+ @dummy_click_hit.send(:refill_stock)
95
+ assert_equal 10, @areas[:stock].cards.size
96
+ assert_equal 0, @areas[:waste].cards.size
97
+ end
98
+
99
+ test 'A card can be displaced from Stock to Waste by a click' do
100
+ assert_equal 10, @areas[:stock].cards.size
101
+ assert_equal 0, @areas[:waste].cards.size
102
+
103
+ fill_attributes_up(@stock_hit)
104
+ @stock_hit.send(:displace_to_waste)
105
+ assert_equal 9, @areas[:stock].cards.size
106
+ assert_equal 1, @areas[:waste].cards.size
107
+
108
+ fill_attributes_up(@stock_hit)
109
+ @stock_hit.send(:displace_to_waste)
110
+ assert_equal 8, @areas[:stock].cards.size
111
+ assert_equal 2, @areas[:waste].cards.size
112
+ end
113
+
114
+ test 'Stock can change its background as a function of conditions' do
115
+ stock = @areas[:stock].piles[0]
116
+ @stock_hit.scenario = -> { @stock_hit.send(:stock) }
117
+ normal_background = @areas[:stock].piles[0].background.object_id
118
+ assert normal_background, stock.background.object_id
119
+ assert_equal 10, stock.size
120
+
121
+ fill_attributes_up(@stock_hit)
122
+ @stock_hit.scenario.call
123
+ assert normal_background, stock.background.object_id
124
+ assert_equal 9, stock.size
125
+
126
+ stock.size.times do
127
+ fill_attributes_up(@stock_hit)
128
+ @stock_hit.scenario.call
129
+ end
130
+
131
+ empty_background = stock.background.object_id
132
+ refute_equal normal_background, empty_background
133
+ assert_equal 0, stock.size
134
+
135
+ fill_attributes_up(@stock_hit)
136
+ @stock_hit.scenario.call
137
+ refute_equal empty_background, stock.background.object_id
138
+ refute_equal empty_background, normal_background
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,45 @@
1
+ require_relative '../helper'
2
+
3
+ module Patience
4
+ class TestDrag < TestCase
5
+
6
+ class Dummy < EventHandler::Drag
7
+ def initialize
8
+ @card = Card.new(1, 1)
9
+ @tail_cards = {@card => 0, Card.new(2, 2) => 0, Card.new(3, 3) => 0}
10
+ @offset = Ray::Vector2[20, 20]
11
+ end
12
+ end
13
+
14
+ def setup
15
+ @mouse_pos = Ray::Vector2[0, 0]
16
+ @dummy = Dummy.new
17
+ @areas = { :tableau => Tableau.new([Card.new(1, 1)]) }
18
+ @cursor = Cursor.new
19
+ @cursor.mouse_pos = @mouse_pos
20
+ @cursor.click = EventHandler::Click.new(@cursor.mouse_pos, @areas)
21
+ end
22
+
23
+ test 'A drag can move cards' do
24
+ assert_equal Ray::Vector2[0, 0], @dummy.card.sprite.pos
25
+
26
+ @mouse_pos += [200, 200]
27
+ @dummy.move(@mouse_pos)
28
+ assert_equal Ray::Vector2[220, 220], @dummy.card.sprite.pos
29
+
30
+ @mouse_pos += [-231, -365]
31
+ @dummy.move(@mouse_pos)
32
+ assert_equal Ray::Vector2[-11, -145], @dummy.card.sprite.pos
33
+ end
34
+
35
+ test 'A drag can check, whether a card is draggable' do
36
+ assert @dummy.draggable?
37
+
38
+ @cursor.mouse_pos = Ray::Vector2[800, 500]
39
+ @cursor.click = EventHandler::Click.new(@cursor.mouse_pos, @areas)
40
+ drag = EventHandler::Drag.new(@cursor)
41
+ refute drag.draggable?
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,175 @@
1
+ require_relative '../helper'
2
+
3
+ module Patience
4
+ class TestDrop < TestCase
5
+
6
+ class EventHandler::Click
7
+ attr_accessor :mouse_pos
8
+ end
9
+
10
+ class Dummy < EventHandler::Drop
11
+ attr_accessor :card_to_drop, :pile_beneath, :card_beneath, :area, :pile
12
+ end
13
+
14
+ def setup
15
+ @mouse_pos = Ray::Vector2[361, 243]
16
+ @deck = Deck.new
17
+ @areas = { :tableau => Tableau.new(@deck.shuffle_off! 28),
18
+ :waste => Waste.new,
19
+ :foundation => Foundation.new }
20
+ @click = EventHandler::Click.new(@mouse_pos, @areas)
21
+ @drop = Dummy.new(@click, @areas)
22
+ end
23
+
24
+ def fill_attributes_up(drop)
25
+ @drop.card_beneath = @drop.send(:find_card_beneath)
26
+ @drop.pile_beneath = @drop.send(:find_pile_beneath)
27
+ end
28
+
29
+ test 'An area can be found by drop' do
30
+ @drop.card_to_drop.pos = Ray::Vector2[0, 0]
31
+ fill_attributes_up(@drop)
32
+
33
+ assert_nil @drop.send(:find_area_beneath)
34
+
35
+ @drop.card_to_drop.pos = Ray::Vector2[252, 218]
36
+ fill_attributes_up(@drop)
37
+ assert_nil @drop.send(:find_area_beneath)
38
+
39
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
40
+ fill_attributes_up(@drop)
41
+ assert_equal Tableau, @drop.send(:find_area_beneath).class
42
+
43
+ assert_kind_of Area, @drop.send(:find_area_beneath)
44
+ end
45
+
46
+ test 'A pile can be found by drop' do
47
+ @drop.card_to_drop.pos = Ray::Vector2[0, 0]
48
+ assert_nil @drop.send(:find_pile_beneath)
49
+
50
+ @drop.card_to_drop.pos = Ray::Vector2[252, 218]
51
+ assert_nil @drop.send(:find_pile_beneath)
52
+
53
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
54
+ @drop.card_beneath = @drop.send(:find_card_beneath)
55
+
56
+ assert_equal Pile, @drop.send(:find_pile_beneath).class
57
+ end
58
+
59
+ test 'A card can be found by drop' do
60
+ @drop.card_to_drop.pos = Ray::Vector2[0, 0]
61
+ assert_nil @drop.send(:find_card_beneath)
62
+
63
+ @drop.card_to_drop.pos = Ray::Vector2[252, 218]
64
+ assert_nil @drop.send(:find_card_beneath)
65
+
66
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
67
+ assert_equal Card, @drop.send(:find_card_beneath).class
68
+ end
69
+
70
+ test 'A drop can decide whether a card meets conditions of Tableau' do
71
+ @drop.card_to_drop = Card.new(8, 1)
72
+ nine_of_clubs = Card.new(9, 4)
73
+ king_of_diamonds = Card.new(13, 2)
74
+ nine_of_hearts = Card.new(9, 1)
75
+
76
+ assert @drop.send(:tableau_conditions?, nine_of_clubs)
77
+ refute @drop.send(:tableau_conditions?, king_of_diamonds)
78
+ refute @drop.send(:tableau_conditions?, nine_of_hearts)
79
+ end
80
+
81
+ test 'A drop can decide whether a card meets conditions of Foundation' do
82
+ @drop.card_to_drop = Card.new(10, 1)
83
+ nine_of_clubs = Card.new(9, 4)
84
+ king_of_diamonds = Card.new(13, 2)
85
+ nine_of_hearts = Card.new(9, 1)
86
+
87
+ refute @drop.send(:foundation_conditions?, nine_of_clubs)
88
+ refute @drop.send(:foundation_conditions?, king_of_diamonds)
89
+ assert @drop.send(:foundation_conditions?, nine_of_hearts)
90
+ end
91
+
92
+ test 'A dropped card can be called off' do
93
+ assert_equal Ray::Vector2[361, 243], @drop.card_to_drop.pos
94
+
95
+ @drop.card_to_drop.pos = Ray::Vector2[0, 0]
96
+ assert_equal Ray::Vector2[0, 0], @drop.card_to_drop.pos
97
+
98
+ @drop.send(:call_off)
99
+ assert_equal Ray::Vector2[361, 243], @drop.card_to_drop.pos
100
+ end
101
+
102
+ test 'A dropped card can be added to the pile beneath' do
103
+ @drop.pile_beneath = Pile.new
104
+ @drop.pile = Pile.new([Card.new(1, 1)])
105
+
106
+ assert_equal 0, @drop.pile_beneath.size
107
+ assert_equal 1, @drop.pile.size
108
+
109
+ @drop.send(:add_to_pile_beneath, @drop.pile.cards[0])
110
+ assert_equal 1, @drop.pile_beneath.size
111
+ assert_instance_of Card, @drop.pile_beneath.cards[0]
112
+ assert_equal Card::Rank::Ace, @drop.pile_beneath.cards[0].rank.class
113
+ assert_equal 0, @drop.pile.size
114
+ end
115
+
116
+ # Not complete.
117
+ test "A dropped card can be checked, if it's allowed to be put in Tableau" do
118
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
119
+ fill_attributes_up(@drop)
120
+ assert @drop.send(:can_put_in_tableau?)
121
+
122
+ @drop.card_to_drop = Card.new(1, 1)
123
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
124
+ refute @drop.send(:can_put_in_tableau?)
125
+ end
126
+
127
+ # Not complete.
128
+ test "A dropped card can be checked, if it's allowed to be put in Foundation" do
129
+ @drop.card_to_drop = Card.new(1, 1)
130
+ @drop.card_to_drop.pos = Ray::Vector2[362, 23]
131
+ fill_attributes_up(@drop)
132
+ assert @drop.send(:can_put_in_foundation?)
133
+
134
+ @drop.card_to_drop = Card.new(10, 1)
135
+ refute @drop.send(:can_put_in_foundation?)
136
+ end
137
+
138
+ test 'A dropped card can be put in Tableau' do
139
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
140
+ fill_attributes_up(@drop)
141
+ assert_equal 5, @areas[:tableau].piles[4].size
142
+ @drop.send(:put_in_tableau)
143
+ assert_equal 6, @areas[:tableau].piles[4].size
144
+
145
+ @areas[:tableau].piles[3] << Card.new(2, 3)
146
+ @areas[:tableau].piles[3].cards.last.pos = @mouse_pos
147
+ @click = EventHandler::Click.new(@mouse_pos, @areas)
148
+ @drop = Dummy.new(@click, @areas)
149
+ @drop.card_to_drop.pos = Ray::Vector2[472, 270]
150
+ fill_attributes_up(@drop)
151
+ @drop.send(:put_in_tableau)
152
+ assert_equal 7, @areas[:tableau].piles[4].size
153
+ end
154
+
155
+ test 'A dropped card can be put in Foundation' do
156
+ @click = EventHandler::Click.new(Ray::Vector2[32, 165], @areas)
157
+ @drop = Dummy.new(@click, @areas)
158
+ @drop.card_to_drop.pos = Ray::Vector2[362, 23]
159
+ fill_attributes_up(@drop)
160
+ @drop.send(:put_in_foundation)
161
+ assert_equal 1, @areas[:foundation].piles[0].size
162
+
163
+ last_card = @areas[:tableau].piles[2].cards.last
164
+ @areas[:tableau].piles[2].cards.delete(last_card)
165
+ @areas[:tableau].piles[2].cards.last.flip!
166
+ @click = EventHandler::Click.new(Ray::Vector2[252, 192], @areas)
167
+ @drop = Dummy.new(@click, @areas)
168
+ @drop.card_to_drop.pos = Ray::Vector2[362, 23]
169
+ fill_attributes_up(@drop)
170
+ @drop.send(:put_in_foundation)
171
+ assert_equal 2, @areas[:foundation].piles[0].size
172
+ end
173
+
174
+ end
175
+ end
@@ -0,0 +1,8 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../../lib/patience'
3
+
4
+ class TestCase < MiniTest::Unit::TestCase
5
+ def self.test(name, &blk)
6
+ define_method('test_' + name, &blk) if blk
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../helper'
2
+
3
+ module Patience
4
+ class TestGameScene < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @game = Patience::Game.new
8
+ @scene = @game.registered_scene(:game_scene)
9
+ @scene.register
10
+ @scene.setup
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ require_relative 'helper'
2
+
3
+ module Patience
4
+ class TestArea < TestCase
5
+
6
+ def setup
7
+ @area = Area.new
8
+ @cards = [Card.new(1, 1)]
9
+ end
10
+
11
+ test 'A new area should contain at least one pile' do
12
+ assert_equal 1, Area.new.piles.size
13
+ end
14
+
15
+ test 'An area can contain several piles' do
16
+ assert_equal 1, Area.new(@cards, 1).piles.size
17
+ assert_equal 10, Area.new(@cards, 10).piles.size
18
+ assert_equal 100, Area.new(@cards, 100).piles.size
19
+ end
20
+
21
+ test 'An area can return array of the piles' do
22
+ assert_instance_of Array, @area.piles
23
+ assert_instance_of Pile, @area.piles.first
24
+ end
25
+
26
+ test 'An area can be disposed' do
27
+ assert_equal Ray::Vector2[0, 0], @area.piles[0].pos
28
+ @area.pos = [20, 20]
29
+ assert_equal Ray::Vector2[20, 20], @area.piles[0].pos
30
+ @area.pos = [-100, 20]
31
+ assert_equal Ray::Vector2[-100, 20], @area.piles[0].pos
32
+
33
+ area = Area.new([], 4)
34
+ assert_equal Ray::Vector2[0, 0], area.piles[0].pos
35
+ assert_equal Ray::Vector2[0, 0], area.piles[3].pos
36
+ area.pos = [20, 20]
37
+ assert_equal Ray::Vector2[20, 20], area.piles[0].pos
38
+ assert_equal Ray::Vector2[20, 20], area.piles[3].pos
39
+ area.pos = [-100, 20]
40
+ assert_equal Ray::Vector2[-100, 20], area.piles[0].pos
41
+ assert_equal Ray::Vector2[-100, 20], area.piles[3].pos
42
+ end
43
+
44
+ test 'An area can tell its position' do
45
+ assert_equal Ray::Vector2[0, 0], @area.pos
46
+ @area.pos = [20, 20]
47
+ assert_equal Ray::Vector2[20, 20], @area.pos
48
+ @area.pos = [-100, 20]
49
+ assert_equal Ray::Vector2[-100, 20], @area.pos
50
+ end
51
+
52
+ test 'An area can show all its cards' do
53
+ assert_equal [], @area.cards
54
+ @area.piles[0].cards << @cards
55
+ assert_equal ["Ace of Hearts"], @area.cards.map(&:to_s)
56
+ end
57
+
58
+ test 'An area can tell, if its card has been clicked' do
59
+ assert @area.hit?(Ray::Vector2[20, 20])
60
+ refute @area.hit?(Ray::Vector2[1000, 0])
61
+ end
62
+
63
+ test 'A card can be added to the first pile of area from the given pile' do
64
+ pile = Pile.new([Card.new(1, 1)])
65
+ assert pile.cards.first.face_up?
66
+ assert_equal 0, @area.cards.size
67
+ @area.add_from(pile, pile.cards.first)
68
+ assert_equal 1, @area.cards.size
69
+ assert @area.cards.first.face_down?
70
+ assert_instance_of Card, @area.cards.first
71
+ end
72
+
73
+ end
74
+ end