gambit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/AUTHORS +2 -0
  2. data/CHANGELOG +7 -0
  3. data/COPYING +340 -0
  4. data/INSTALL +23 -0
  5. data/LICENSE +7 -0
  6. data/README +25 -0
  7. data/Rakefile +87 -0
  8. data/TODO +6 -0
  9. data/examples/bird_wars/game.rb +434 -0
  10. data/examples/bird_wars/html/chest.rhtml +37 -0
  11. data/examples/bird_wars/html/combat.rhtml +38 -0
  12. data/examples/bird_wars/html/fight.rhtml +71 -0
  13. data/examples/bird_wars/html/fridge.rhtml +31 -0
  14. data/examples/bird_wars/html/help.rhtml +30 -0
  15. data/examples/bird_wars/html/house.rhtml +36 -0
  16. data/examples/bird_wars/html/images/albatross.jpg +0 -0
  17. data/examples/bird_wars/html/images/alka_seltzer_small.png +0 -0
  18. data/examples/bird_wars/html/images/bazooka_small.png +0 -0
  19. data/examples/bird_wars/html/images/bb_gun_small.png +0 -0
  20. data/examples/bird_wars/html/images/cat_small.png +0 -0
  21. data/examples/bird_wars/html/images/combat.png +0 -0
  22. data/examples/bird_wars/html/images/crossbow_small.png +0 -0
  23. data/examples/bird_wars/html/images/cuckoo.jpg +0 -0
  24. data/examples/bird_wars/html/images/fridge.png +0 -0
  25. data/examples/bird_wars/html/images/gull.jpg +0 -0
  26. data/examples/bird_wars/html/images/house.jpg +0 -0
  27. data/examples/bird_wars/html/images/items.png +0 -0
  28. data/examples/bird_wars/html/images/not_found.png +0 -0
  29. data/examples/bird_wars/html/images/note.jpg +0 -0
  30. data/examples/bird_wars/html/images/oil_small.png +0 -0
  31. data/examples/bird_wars/html/images/one_stone_small.png +0 -0
  32. data/examples/bird_wars/html/images/owl.jpg +0 -0
  33. data/examples/bird_wars/html/images/poisoned_seeds_small.png +0 -0
  34. data/examples/bird_wars/html/images/rice_small.png +0 -0
  35. data/examples/bird_wars/html/images/shotgun_small.png +0 -0
  36. data/examples/bird_wars/html/images/slingshot_small.png +0 -0
  37. data/examples/bird_wars/html/images/soda_rings_small.png +0 -0
  38. data/examples/bird_wars/html/images/spear_small.png +0 -0
  39. data/examples/bird_wars/html/images/stork.jpg +0 -0
  40. data/examples/bird_wars/html/images/weapons.png +0 -0
  41. data/examples/bird_wars/html/images/woodpecker.jpg +0 -0
  42. data/examples/bird_wars/html/images/you.jpg +0 -0
  43. data/examples/bird_wars/html/index.rhtml +13 -0
  44. data/examples/bird_wars/html/weapons.rhtml +35 -0
  45. data/examples/cli/blackjack.rb +86 -0
  46. data/examples/cli/go.rb +93 -0
  47. data/examples/cli/space_trader2/planet_data +12280 -0
  48. data/examples/cli/space_trader2/space_trader2.rb +248 -0
  49. data/examples/cli/space_trader2/world.rb +127 -0
  50. data/examples/cli/spacetrader.rb +262 -0
  51. data/examples/cli/yahtzee.rb +161 -0
  52. data/examples/galactic_courier/Rakefile +9 -0
  53. data/examples/galactic_courier/bin/galactic_courier.rb +44 -0
  54. data/examples/galactic_courier/html/console.rhtml +15 -0
  55. data/examples/galactic_courier/html/images/barren.jpg +0 -0
  56. data/examples/galactic_courier/html/images/industrial.jpg +0 -0
  57. data/examples/galactic_courier/html/images/jungle.jpg +0 -0
  58. data/examples/galactic_courier/html/images/oceanic.jpg +0 -0
  59. data/examples/galactic_courier/html/images/tundra.jpg +0 -0
  60. data/examples/galactic_courier/html/images/volcanic.jpg +0 -0
  61. data/examples/galactic_courier/lib/galaxy.rb +64 -0
  62. data/examples/galactic_courier/lib/planet.rb +30 -0
  63. data/examples/galactic_courier/lib/sector.rb +84 -0
  64. data/examples/galactic_courier/lib/station.rb +18 -0
  65. data/examples/galactic_courier/test/tc_galaxy.rb +24 -0
  66. data/examples/galactic_courier/test/tc_planet.rb +22 -0
  67. data/examples/galactic_courier/test/tc_sector.rb +55 -0
  68. data/examples/galactic_courier/test/ts_all.rb +12 -0
  69. data/examples/gambit_mail/html/compose.rhtml +39 -0
  70. data/examples/gambit_mail/html/index.rhtml +3 -0
  71. data/examples/gambit_mail/html/mailbox.rhtml +47 -0
  72. data/examples/gambit_mail/html/message.rhtml +34 -0
  73. data/examples/gambit_mail/mail.rb +75 -0
  74. data/lib/gambit.rb +10 -0
  75. data/lib/gambit/server.rb +269 -0
  76. data/lib/gambit/server/gambiterror.rb +28 -0
  77. data/lib/gambit/server/game.rb +185 -0
  78. data/lib/gambit/server/game/eventform.rb +174 -0
  79. data/lib/gambit/server/message.rb +85 -0
  80. data/lib/gambit/server/player.rb +40 -0
  81. data/lib/gambit/tools.rb +13 -0
  82. data/lib/gambit/tools/board.rb +275 -0
  83. data/lib/gambit/tools/cards.rb +11 -0
  84. data/lib/gambit/tools/cards/card.rb +158 -0
  85. data/lib/gambit/tools/cards/deck.rb +99 -0
  86. data/lib/gambit/tools/cards/hand.rb +86 -0
  87. data/lib/gambit/tools/cards/pile.rb +107 -0
  88. data/lib/gambit/tools/currency.rb +148 -0
  89. data/lib/gambit/tools/dice.rb +219 -0
  90. data/lib/gambit/tools/movehistory.rb +164 -0
  91. data/lib/gambit/tools/scorecard.rb +333 -0
  92. data/lib/gambit/viewable.rb +71 -0
  93. data/setup.rb +1360 -0
  94. data/test/tc_board.rb +167 -0
  95. data/test/tc_cards.rb +182 -0
  96. data/test/tc_currency.rb +99 -0
  97. data/test/tc_dice.rb +83 -0
  98. data/test/tc_error.rb +34 -0
  99. data/test/tc_event.rb +367 -0
  100. data/test/tc_game.rb +29 -0
  101. data/test/tc_message.rb +35 -0
  102. data/test/tc_movehistory.rb +38 -0
  103. data/test/tc_player.rb +60 -0
  104. data/test/tc_scorecard.rb +112 -0
  105. data/test/tc_views.rb +353 -0
  106. data/test/test_game.rb +19 -0
  107. data/test/test_view.rhtml +2 -0
  108. data/test/ts_all.rb +12 -0
  109. data/test/ts_server.rb +14 -0
  110. data/test/ts_tools.rb +14 -0
  111. metadata +183 -0
@@ -0,0 +1,164 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # movehistory.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-05-30.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ require "gambit/viewable"
9
+
10
+ module Gambit
11
+ module Tools
12
+ # An objectified move history object.
13
+ class MoveHistory
14
+ include Gambit::Viewable
15
+ register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/^\t{3}/, ""))
16
+ <table class="movehistory">
17
+ % turns = @moves.keys.sort
18
+ <tr>
19
+ <th>&nbsp;</th>
20
+ % @players.each do |player|
21
+ <th id="<%= player %>"><%= player %></th>
22
+ % end
23
+ <%= "</tr>" %>
24
+ % even = true
25
+ % turns.each do |turn|
26
+ % even = (not even)
27
+ % if even
28
+ <tr class="even">
29
+ % else
30
+ <tr class="odd">
31
+ % end
32
+ <td class="turn"><%= turn %></td>
33
+ % @players.each do |player|
34
+ <td id="<%= player %><%= turn %>"><%= @moves[turn][player] ||
35
+ "&nbsp;" %></td>
36
+ % end
37
+ % if turn == turns.last
38
+ </tr>
39
+ % else
40
+ <%= "</tr>" %>
41
+ % end
42
+ % end
43
+ </table>
44
+ END_HTML_VIEW
45
+ register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/^\t+/, ""))
46
+ % turns = @moves.keys.sort
47
+ % len_cmp = lambda { |a, b| a.length <=> b.length }
48
+ % turn_width = turns.map { |t| t.to_s }.max(&len_cmp).length
49
+ % move_width = [ @moves.map { |(turn, moves)| moves.values }.
50
+ % flatten.max(&len_cmp).length,
51
+ % @players.max(&len_cmp).length ].max
52
+ <%= ("%\#{turn_width}s " + "%\#{move_width}s " * @players.size) %
53
+ ["", *@players] %>
54
+ % turns.each do |turn|
55
+ % unless @moves[turn][@players.first].nil?
56
+ <%= ("%\#{turn_width}d: " + "%\#{move_width}s " * @players.size) %
57
+ [turn, *@players.map { |p| @moves[turn][p] }] %>
58
+ % end
59
+ % end
60
+ END_TEXT_VIEW
61
+
62
+ #
63
+ # Creates a new MoveHistory object, optionally initialized with some
64
+ # _players_.
65
+ #
66
+ def initialize( *players )
67
+ @players = Array.new
68
+ @turn = 1
69
+ @next_player = 0
70
+ @moves = Hash.new { |moves, turn| moves[turn] = Hash.new }
71
+
72
+ add_players(*players)
73
+ end
74
+
75
+ # The current turn number or name.
76
+ attr_accessor :turn
77
+
78
+ #
79
+ # Returns the moves for the provided _turn_ (optionally for the
80
+ # given _player_.)
81
+ #
82
+ def []( turn, player = nil )
83
+ if player.nil?
84
+ @moves[turn]
85
+ else
86
+ @moves[turn][player]
87
+ end
88
+ end
89
+
90
+ #
91
+ # Add _move_ to the current player for the current turn.
92
+ #
93
+ # Returns +self+ for method chaining.
94
+ #
95
+ def <<( move )
96
+ @moves[@turn][@players[@next_player]] = move
97
+
98
+ find_next_player
99
+
100
+ self
101
+ end
102
+
103
+ #
104
+ # Add a _player_ to this MoveHistory.
105
+ #
106
+ # Returns +self+ for method chaining.
107
+ #
108
+ def add_player( player )
109
+ @players << player
110
+
111
+ self
112
+ end
113
+
114
+ #
115
+ # Add a group of _players_ to this MoveHistory.
116
+ #
117
+ # Returns +self+ for method chaining.
118
+ #
119
+ def add_players( *players )
120
+ players.each { |player| add_player(player) }
121
+
122
+ self
123
+ end
124
+
125
+ # Iterate over each move, by turn and then by player.
126
+ def each( )
127
+ @moves.each do |(turn, moves)|
128
+ @players.each { |player| yield moves[player] }
129
+ end
130
+ end
131
+
132
+ #
133
+ # Add's an _event_ to the given _player_ for the current turn. If
134
+ # the player already has and event for that turn, an Array is used
135
+ # to hold all added events in order.
136
+ #
137
+ # Returns +self+ for method chaining.
138
+ #
139
+ def record( player, event )
140
+ if @moves[@turn][player].nil?
141
+ @moves[@turn][player] = event
142
+ elsif @moves[@turn][player].is_a? Array
143
+ @moves[@turn][player] << event
144
+ else
145
+ @moves[@turn][player] = [@moves[@turn][player]]
146
+ @moves[@turn][player] << event
147
+ end
148
+
149
+ self
150
+ end
151
+
152
+ private
153
+
154
+ # Loops through players, circularly.
155
+ def find_next_player( )
156
+ @next_player += 1
157
+ if @next_player >= @players.size
158
+ @next_player = 0
159
+ @turn += 1
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,333 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # scorecard.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-05-29.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+
8
+ require "gambit/viewable"
9
+
10
+ module Gambit
11
+ module Tools
12
+ # A tool for managing the score of just about any game.
13
+ class Scorecard
14
+ include Gambit::Viewable
15
+ register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/^\t{3}/, ""))
16
+ <table class="scorecard">
17
+ <tr>
18
+ <th>&nbsp;</th>
19
+ % @players.each do |player|
20
+ <th id="<%= player %>"><%= player %></th>
21
+ % end
22
+ <%= "</tr>" %>
23
+ % even = true
24
+ % @order.each do |cat|
25
+ % even = (not even)
26
+ % if even
27
+ <tr class="even">
28
+ % else
29
+ <tr class="odd">
30
+ % end
31
+ <td class="category"><%= cat %></td>
32
+ % @players.each do |player|
33
+ % if @categories.assoc(cat)
34
+ <td id="<%= player %>--<%= cat %>"><%=
35
+ @categories_score[player][cat] || "&nbsp;" %></td>
36
+ % elsif subtotal = @totals.assoc(cat)
37
+ <td id="<%= player %>--<%= cat %>"><%=
38
+ figure_total(player, subtotal[1], subtotal[2]) ||
39
+ "&nbsp;" %></td>
40
+ % end
41
+ % end
42
+ % if cat == @order.last
43
+ </tr>
44
+ % else
45
+ <%= "</tr>" %>
46
+ % end
47
+ % end
48
+ </table>
49
+ END_HTML_VIEW
50
+ register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/^\t+/, ""))
51
+ % max_len = @players.max { |a, b| a.length <=> b.length }.length
52
+ % cat_len = @order.max { |a, b| a.length <=> b.length }.length
53
+ <%= "%\#{cat_len}s" % "" %> <%= "%\#{max_len}s " * @players.size %
54
+ @players %>
55
+ % last = nil
56
+ % @order.each do |cat|
57
+ % if @categories.assoc(cat)
58
+ % if @totals.assoc(last)
59
+
60
+ % end
61
+ <%= "%\#{cat_len}s" % cat %>: <%= "%\#{max_len}s " *
62
+ @players.size % @players.map { |player|
63
+ @categories_score[player][cat] } %>
64
+ % elsif subtotal = @totals.assoc(cat)
65
+ <%= "-" * cat_len %> <%=
66
+ (("-" * max_len) + " ") * @players.size %>
67
+ <%= "%\#{cat_len}s" % cat %>: <%= "%\#{max_len}s " *
68
+ @players.size % @players.map { |player|
69
+ figure_total(player, subtotal[1], subtotal[2]) } %>
70
+ % end
71
+ % last = cat
72
+ % end
73
+ END_TEXT_VIEW
74
+
75
+ #
76
+ # Creates a new Scorecard object, optionally initialized with some
77
+ # _players_.
78
+ #
79
+ def initialize( *players )
80
+ @score = Hash.new(0)
81
+ @players = Array.new
82
+
83
+ @use_categories = false
84
+ @categories = Array.new
85
+ @totals = Array.new
86
+ @order = Array.new
87
+ @categories_score = Hash.new do |names, cat|
88
+ names[cat] = Hash.new(0)
89
+ end
90
+ @win_category = nil
91
+
92
+ add_players(*players)
93
+ end
94
+
95
+ #
96
+ # Add a new _player_ to this Scorecard.
97
+ #
98
+ # Returns +self+ for method chaining.
99
+ #
100
+ def add_player( player )
101
+ @players << player
102
+
103
+ self
104
+ end
105
+
106
+ #
107
+ # Add a group of _players_ to this Scorecard.
108
+ #
109
+ # Returns +self+ for method chaining.
110
+ #
111
+ def add_players( *players )
112
+ players.each { |player| add_player(player) }
113
+
114
+ self
115
+ end
116
+
117
+ #
118
+ # Returns the losing player, as determined by the provided winning
119
+ # _condition_. If a _condition_ block is not given, scores will be
120
+ # compared numerically, with the highest score considered to be the
121
+ # best.
122
+ #
123
+ def loser( &condition )
124
+ if @use_categories
125
+ scores = @categories_score.map do |(name, scores)|
126
+ if @categories.assoc(@win_category)
127
+ @score[@win_category]
128
+ elsif total = @totals.assoc(@win_category)
129
+ figure_total(name, total[1], total[2])
130
+ end
131
+ end
132
+ best_score = scores.min(&condition)
133
+ @categories_score.find do |(name, score)|
134
+ if @categories.assoc(@win_category)
135
+ @score[@win_category] == best_score
136
+ elsif total = @totals.assoc(@win_category)
137
+ figure_total(name, total[1], total[2]) == best_score
138
+ end
139
+ end.first
140
+ else
141
+ best_score = @score.values.min(&condition)
142
+ @score.find { |(player, score)| score == best_score }.first
143
+ end
144
+ end
145
+
146
+ #
147
+ # Forces this Scorecard to score by category only.
148
+ #
149
+ # Returns +self+ for method chaining.
150
+ #
151
+ def require_categories( )
152
+ @use_categories = true
153
+
154
+ self
155
+ end
156
+
157
+ #
158
+ # Can be used to add _points_ to the score of the given _player_ or
159
+ # to fetch the score for a _player_, by omitting the _points_. If
160
+ # given, a score will be modified or fetched for the given
161
+ # _category_.
162
+ #
163
+ def score( player, points = 0, category = nil )
164
+ if @use_categories
165
+ cat = @categories.assoc(category) or
166
+ raise ArgumentError, "Invalid category."
167
+
168
+ total = @categories_score[player][category]
169
+ total += points
170
+
171
+ if cat.size == 2 and cat.last.is_a? Proc
172
+ unless cat.last[total]
173
+ raise ArgumentError, "Invalid score for " +
174
+ "#{cat.first} category."
175
+ end
176
+ elsif cat.size == 2 and cat.last.is_a? Array
177
+ unless cat.last.include?(total)
178
+ raise ArgumentError, "Invalid score for " +
179
+ "#{cat.first} category."
180
+ end
181
+ elsif cat.size == 3
182
+ unless cat[1] <= total and total <= cat[2]
183
+ raise ArgumentError, "Invalid score for " +
184
+ "#{cat.first} category."
185
+ end
186
+ end
187
+
188
+ @categories_score[player][category] = total
189
+
190
+ total(player, category)
191
+ else
192
+ @score[player] += points
193
+ @score[player] = yield(@score[player]) if block_given?
194
+
195
+ total(player)
196
+ end
197
+ end
198
+
199
+ #
200
+ # This method adds a "total" field to this Scorecard, by _name_.
201
+ # A total can be calculated _from_ any other fields on the card,
202
+ # including other totals.
203
+ #
204
+ # A _validation_ block can be given, if needed. The block will be
205
+ # called each time the total is display and passed the sum of all
206
+ # _from_ fields. The return value of the block will be the total
207
+ # displayed.
208
+ #
209
+ # Returns +self+ for method chaining.
210
+ #
211
+ def set_total( name, *from, &validation )
212
+ @order << name
213
+ @totals << [name, from, validation]
214
+
215
+ self
216
+ end
217
+
218
+ #
219
+ # Sets the score catrgory used to determine a winner.
220
+ #
221
+ # Returns +self+ for method chaining.
222
+ #
223
+ def set_win( name )
224
+ @win_category = name
225
+
226
+ self
227
+ end
228
+
229
+ #
230
+ # Sets a starting score for each field. *WARNING*: Should be
231
+ # called before any calls to score(), for predictable results.
232
+ #
233
+ def start_at=( minimum_score )
234
+ @score.default = minimum_score
235
+ end
236
+
237
+ #
238
+ # Returns the total score for the given _player_ or for the given
239
+ # _player_ in the optional _category_.
240
+ #
241
+ def total( player, category = nil )
242
+ if category.nil?
243
+ @score[player]
244
+ else
245
+ if @categories.assoc(category)
246
+ @categories_score[player][category]
247
+ elsif total = @totals.assoc(category)
248
+ figure_total(player, total[1], total[2])
249
+ else
250
+
251
+ end
252
+ end
253
+ end
254
+
255
+ #
256
+ # Adds a _category_ to this Scorecard.
257
+ #
258
+ # Returns +self+ for method chaining.
259
+ #
260
+ # :call-seq:
261
+ # use_category(cat) { |score| ... } -> self
262
+ # use_category(cat, ary_of_accepted_values) -> self
263
+ # use_category(cat, low, high) -> self
264
+ #
265
+ def use_category( category, *details, &validation )
266
+ @order << category
267
+
268
+ if not validation.nil?
269
+ @categories << [category, validation]
270
+ elsif details.size == 1 and details[0].is_a? Array
271
+ @categories << [category, details[0]]
272
+ elsif details.size == 2 and details.all? { |n| n.is_a? Integer }
273
+ @categories << [category, *details]
274
+ else
275
+ raise ArgumentError, "Invalid category definition."
276
+ end
277
+
278
+ self
279
+ end
280
+
281
+ #
282
+ # Returns the winning player, as determined by the provided winning
283
+ # _condition_. If a _condition_ block is not given, scores will be
284
+ # compared numerically, with the highest score considered to be the
285
+ # best.
286
+ #
287
+ def winner( &condition )
288
+ if @use_categories
289
+ scores = @categories_score.map do |(name, scores)|
290
+ if @categories.assoc(@win_category)
291
+ @score[@win_category]
292
+ elsif total = @totals.assoc(@win_category)
293
+ figure_total(name, total[1], total[2])
294
+ end
295
+ end
296
+ best_score = scores.max(&condition)
297
+ @categories_score.find do |(name, score)|
298
+ if @categories.assoc(@win_category)
299
+ @score[@win_category] == best_score
300
+ elsif total = @totals.assoc(@win_category)
301
+ figure_total(name, total[1], total[2]) == best_score
302
+ end
303
+ end.first
304
+ else
305
+ best_score = @score.values.max(&condition)
306
+ @score.find { |(player, score)| score == best_score }.first
307
+ end
308
+ end
309
+
310
+ private
311
+
312
+ #
313
+ # Calculates a total field as described in set_total() and returns
314
+ # the score.
315
+ #
316
+ def figure_total( player, from, validate )
317
+ total = 0
318
+
319
+ from.each do |cat_or_total|
320
+ if @categories.assoc(cat_or_total)
321
+ total += @categories_score[player][cat_or_total]
322
+ elsif subtotal = @totals.assoc(cat_or_total)
323
+ total += figure_total(player, subtotal[1], subtotal[2])
324
+ end
325
+ end
326
+
327
+ total = validate[total] unless validate.nil?
328
+
329
+ total
330
+ end
331
+ end
332
+ end
333
+ end