gambit 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/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
|