gambit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +2 -0
- data/CHANGELOG +7 -0
- data/COPYING +340 -0
- data/INSTALL +23 -0
- data/LICENSE +7 -0
- data/README +25 -0
- data/Rakefile +87 -0
- data/TODO +6 -0
- data/examples/bird_wars/game.rb +434 -0
- data/examples/bird_wars/html/chest.rhtml +37 -0
- data/examples/bird_wars/html/combat.rhtml +38 -0
- data/examples/bird_wars/html/fight.rhtml +71 -0
- data/examples/bird_wars/html/fridge.rhtml +31 -0
- data/examples/bird_wars/html/help.rhtml +30 -0
- data/examples/bird_wars/html/house.rhtml +36 -0
- data/examples/bird_wars/html/images/albatross.jpg +0 -0
- data/examples/bird_wars/html/images/alka_seltzer_small.png +0 -0
- data/examples/bird_wars/html/images/bazooka_small.png +0 -0
- data/examples/bird_wars/html/images/bb_gun_small.png +0 -0
- data/examples/bird_wars/html/images/cat_small.png +0 -0
- data/examples/bird_wars/html/images/combat.png +0 -0
- data/examples/bird_wars/html/images/crossbow_small.png +0 -0
- data/examples/bird_wars/html/images/cuckoo.jpg +0 -0
- data/examples/bird_wars/html/images/fridge.png +0 -0
- data/examples/bird_wars/html/images/gull.jpg +0 -0
- data/examples/bird_wars/html/images/house.jpg +0 -0
- data/examples/bird_wars/html/images/items.png +0 -0
- data/examples/bird_wars/html/images/not_found.png +0 -0
- data/examples/bird_wars/html/images/note.jpg +0 -0
- data/examples/bird_wars/html/images/oil_small.png +0 -0
- data/examples/bird_wars/html/images/one_stone_small.png +0 -0
- data/examples/bird_wars/html/images/owl.jpg +0 -0
- data/examples/bird_wars/html/images/poisoned_seeds_small.png +0 -0
- data/examples/bird_wars/html/images/rice_small.png +0 -0
- data/examples/bird_wars/html/images/shotgun_small.png +0 -0
- data/examples/bird_wars/html/images/slingshot_small.png +0 -0
- data/examples/bird_wars/html/images/soda_rings_small.png +0 -0
- data/examples/bird_wars/html/images/spear_small.png +0 -0
- data/examples/bird_wars/html/images/stork.jpg +0 -0
- data/examples/bird_wars/html/images/weapons.png +0 -0
- data/examples/bird_wars/html/images/woodpecker.jpg +0 -0
- data/examples/bird_wars/html/images/you.jpg +0 -0
- data/examples/bird_wars/html/index.rhtml +13 -0
- data/examples/bird_wars/html/weapons.rhtml +35 -0
- data/examples/cli/blackjack.rb +86 -0
- data/examples/cli/go.rb +93 -0
- data/examples/cli/space_trader2/planet_data +12280 -0
- data/examples/cli/space_trader2/space_trader2.rb +248 -0
- data/examples/cli/space_trader2/world.rb +127 -0
- data/examples/cli/spacetrader.rb +262 -0
- data/examples/cli/yahtzee.rb +161 -0
- data/examples/galactic_courier/Rakefile +9 -0
- data/examples/galactic_courier/bin/galactic_courier.rb +44 -0
- data/examples/galactic_courier/html/console.rhtml +15 -0
- data/examples/galactic_courier/html/images/barren.jpg +0 -0
- data/examples/galactic_courier/html/images/industrial.jpg +0 -0
- data/examples/galactic_courier/html/images/jungle.jpg +0 -0
- data/examples/galactic_courier/html/images/oceanic.jpg +0 -0
- data/examples/galactic_courier/html/images/tundra.jpg +0 -0
- data/examples/galactic_courier/html/images/volcanic.jpg +0 -0
- data/examples/galactic_courier/lib/galaxy.rb +64 -0
- data/examples/galactic_courier/lib/planet.rb +30 -0
- data/examples/galactic_courier/lib/sector.rb +84 -0
- data/examples/galactic_courier/lib/station.rb +18 -0
- data/examples/galactic_courier/test/tc_galaxy.rb +24 -0
- data/examples/galactic_courier/test/tc_planet.rb +22 -0
- data/examples/galactic_courier/test/tc_sector.rb +55 -0
- data/examples/galactic_courier/test/ts_all.rb +12 -0
- data/examples/gambit_mail/html/compose.rhtml +39 -0
- data/examples/gambit_mail/html/index.rhtml +3 -0
- data/examples/gambit_mail/html/mailbox.rhtml +47 -0
- data/examples/gambit_mail/html/message.rhtml +34 -0
- data/examples/gambit_mail/mail.rb +75 -0
- data/lib/gambit.rb +10 -0
- data/lib/gambit/server.rb +269 -0
- data/lib/gambit/server/gambiterror.rb +28 -0
- data/lib/gambit/server/game.rb +185 -0
- data/lib/gambit/server/game/eventform.rb +174 -0
- data/lib/gambit/server/message.rb +85 -0
- data/lib/gambit/server/player.rb +40 -0
- data/lib/gambit/tools.rb +13 -0
- data/lib/gambit/tools/board.rb +275 -0
- data/lib/gambit/tools/cards.rb +11 -0
- data/lib/gambit/tools/cards/card.rb +158 -0
- data/lib/gambit/tools/cards/deck.rb +99 -0
- data/lib/gambit/tools/cards/hand.rb +86 -0
- data/lib/gambit/tools/cards/pile.rb +107 -0
- data/lib/gambit/tools/currency.rb +148 -0
- data/lib/gambit/tools/dice.rb +219 -0
- data/lib/gambit/tools/movehistory.rb +164 -0
- data/lib/gambit/tools/scorecard.rb +333 -0
- data/lib/gambit/viewable.rb +71 -0
- data/setup.rb +1360 -0
- data/test/tc_board.rb +167 -0
- data/test/tc_cards.rb +182 -0
- data/test/tc_currency.rb +99 -0
- data/test/tc_dice.rb +83 -0
- data/test/tc_error.rb +34 -0
- data/test/tc_event.rb +367 -0
- data/test/tc_game.rb +29 -0
- data/test/tc_message.rb +35 -0
- data/test/tc_movehistory.rb +38 -0
- data/test/tc_player.rb +60 -0
- data/test/tc_scorecard.rb +112 -0
- data/test/tc_views.rb +353 -0
- data/test/test_game.rb +19 -0
- data/test/test_view.rhtml +2 -0
- data/test/ts_all.rb +12 -0
- data/test/ts_server.rb +14 -0
- data/test/ts_tools.rb +14 -0
- 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> </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
|
+
" " %></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> </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] || " " %></td>
|
36
|
+
% elsif subtotal = @totals.assoc(cat)
|
37
|
+
<td id="<%= player %>--<%= cat %>"><%=
|
38
|
+
figure_total(player, subtotal[1], subtotal[2]) ||
|
39
|
+
" " %></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
|