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,99 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
|
2
|
+
|
|
3
|
+
# deck.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
|
+
module Cards
|
|
13
|
+
# A Deck manager for any class inheriting from Card.
|
|
14
|
+
class Deck
|
|
15
|
+
include Gambit::Viewable
|
|
16
|
+
register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/\t{4}/, ""))
|
|
17
|
+
<ol class="deck">
|
|
18
|
+
% each do |card|
|
|
19
|
+
<li><%= card.view(:gambit_text) %></li>
|
|
20
|
+
% end
|
|
21
|
+
</ol>
|
|
22
|
+
END_HTML_VIEW
|
|
23
|
+
register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/\t+/, ""))
|
|
24
|
+
% each do |card|
|
|
25
|
+
<%= card.view(:gambit_text) + "\\n" %>
|
|
26
|
+
% end
|
|
27
|
+
END_TEXT_VIEW
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Create a new Deck filled with _card_type_ (StandardCard by
|
|
31
|
+
# default).
|
|
32
|
+
#
|
|
33
|
+
def initialize( card_type = StandardCard )
|
|
34
|
+
@deck = Array.new
|
|
35
|
+
card_type.each { |card| @deck << card }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the Deck size.
|
|
39
|
+
def count( )
|
|
40
|
+
@deck.size
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Cuts the deck at the provided _count_ or a random default.
|
|
44
|
+
def cut!( count = rand(@deck.size / 2) + @deck.size / 4 )
|
|
45
|
+
@deck = @deck.values_at(count..-1, 0...count)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Deals cards to _hands_ in round robin fashion. If a Fixnum is
|
|
50
|
+
# included in the argument list, that many cards will be dealt
|
|
51
|
+
# to each hand. Otherwise, the entire deck is dealt.
|
|
52
|
+
#
|
|
53
|
+
# You may use Hand objects in _hands_ or anything that supports
|
|
54
|
+
# the << operator.
|
|
55
|
+
#
|
|
56
|
+
def deal( *hands )
|
|
57
|
+
if count = hands.find { |hand| hand.is_a? Fixnum }
|
|
58
|
+
hands.delete(count)
|
|
59
|
+
else
|
|
60
|
+
count = @deck.size
|
|
61
|
+
end
|
|
62
|
+
count.times do
|
|
63
|
+
hands.each do |hand|
|
|
64
|
+
hand << @deck.shift
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Removes and returns a card from the top of the deck.
|
|
70
|
+
def draw( )
|
|
71
|
+
@deck.shift
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
include Enumerable
|
|
75
|
+
|
|
76
|
+
# Iterate through the cards.
|
|
77
|
+
def each( &block )
|
|
78
|
+
@deck.each(&block)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Randomize the deck.
|
|
82
|
+
def shuffle!( )
|
|
83
|
+
@deck = @deck.sort_by { rand }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Sort the deck by card value.
|
|
87
|
+
def sort!( &block )
|
|
88
|
+
@deck.sort!(&block)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Replace _card_ on the bottom of the deck.
|
|
92
|
+
def replace( card )
|
|
93
|
+
@deck << card
|
|
94
|
+
end
|
|
95
|
+
alias_method :<<, :replace
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
|
2
|
+
|
|
3
|
+
# hand.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
|
+
module Cards
|
|
13
|
+
# An object for managing a Hand of cards.
|
|
14
|
+
class Hand
|
|
15
|
+
include Gambit::Viewable
|
|
16
|
+
register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/^\t{4}/, ""))
|
|
17
|
+
<ol class="hand">
|
|
18
|
+
% each do |card|
|
|
19
|
+
<li><%= card.view(:gambit_text) %></li>
|
|
20
|
+
% end
|
|
21
|
+
</ol>
|
|
22
|
+
END_HTML_VIEW
|
|
23
|
+
register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/^\t+/, ""))
|
|
24
|
+
% if @hand.size.zero?
|
|
25
|
+
an empty pile
|
|
26
|
+
% elsif @hand.size == 1
|
|
27
|
+
<%= @hand.first.view(:gambit_text) %>
|
|
28
|
+
% else
|
|
29
|
+
% first_cards = @hand[0..-2].map { |c| c.view(:gambit_text) }
|
|
30
|
+
% last_card = @hand[-1].view(:gambit_text)
|
|
31
|
+
<%= first_cards.join(', ') + " and " + last_card %>
|
|
32
|
+
% end
|
|
33
|
+
END_TEXT_VIEW
|
|
34
|
+
|
|
35
|
+
# Create an empty Hand of cards.
|
|
36
|
+
def initialize( )
|
|
37
|
+
@hand = Array.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Fetch a card by _index_.
|
|
41
|
+
def []( index )
|
|
42
|
+
@hand[index]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Add a card to this Hand.
|
|
47
|
+
#
|
|
48
|
+
# Returns +self+ for method chaining.
|
|
49
|
+
#
|
|
50
|
+
def <<( card )
|
|
51
|
+
@hand << card
|
|
52
|
+
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns the Hand size.
|
|
57
|
+
def count( )
|
|
58
|
+
@hand.size
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
include Enumerable
|
|
62
|
+
|
|
63
|
+
# Iterate through the cards.
|
|
64
|
+
def each( &block )
|
|
65
|
+
@hand.each(&block)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Removes and returns the card at the given _index_.
|
|
69
|
+
def discard( index )
|
|
70
|
+
@hand.delete_at(index)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Sort the Hand by card value.
|
|
75
|
+
#
|
|
76
|
+
# Returns +self+ for method chaining.
|
|
77
|
+
#
|
|
78
|
+
def sort!( &block )
|
|
79
|
+
@hand.sort!(&block)
|
|
80
|
+
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
|
2
|
+
|
|
3
|
+
# Pile.rb
|
|
4
|
+
#
|
|
5
|
+
# Created by James Edward Gray II on 2005-05-31.
|
|
6
|
+
# Copyright 2005 Gray Productions. All rights reserved.
|
|
7
|
+
|
|
8
|
+
require "gambit/viewable"
|
|
9
|
+
|
|
10
|
+
module Gambit
|
|
11
|
+
module Tools
|
|
12
|
+
module Cards
|
|
13
|
+
# A tool for managing Piles of cards.
|
|
14
|
+
class Pile
|
|
15
|
+
include Gambit::Viewable
|
|
16
|
+
register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/^\t+/, ""))
|
|
17
|
+
<ol class="pile">
|
|
18
|
+
% @pile.each_with_index do |card, index|
|
|
19
|
+
% if @face_up[index]
|
|
20
|
+
<%= "\\t" %><li><%= card.view(:gambit_text) %></li>
|
|
21
|
+
% else
|
|
22
|
+
<%= "\\t" %><li>A Face-down Card</li>
|
|
23
|
+
% end
|
|
24
|
+
% end
|
|
25
|
+
</ol>
|
|
26
|
+
END_HTML_VIEW
|
|
27
|
+
register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/^\t+/, ""))
|
|
28
|
+
% cards = to_a
|
|
29
|
+
% if cards.size.zero?
|
|
30
|
+
an empty pile
|
|
31
|
+
% elsif cards.size == 1
|
|
32
|
+
<%= cards.first.view(:gambit_text) %>
|
|
33
|
+
% else
|
|
34
|
+
% first_cards = cards[0..-2].map { |c| c.view(:gambit_text) }
|
|
35
|
+
% last_card = cards[-1].view(:gambit_text)
|
|
36
|
+
<%= first_cards.join(', ') + " and " + last_card %>
|
|
37
|
+
% end
|
|
38
|
+
END_TEXT_VIEW
|
|
39
|
+
|
|
40
|
+
# Creates an empty Pile of cards.
|
|
41
|
+
def initialize( )
|
|
42
|
+
@pile = Array.new
|
|
43
|
+
|
|
44
|
+
@add_to_top = true
|
|
45
|
+
@remove_from_top = true
|
|
46
|
+
|
|
47
|
+
@face_up = lambda { |index| true }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# When +true+ (default), add() will place cards on the top of
|
|
52
|
+
# the pile. Otherwise, they go to the bottom.
|
|
53
|
+
#
|
|
54
|
+
attr_accessor :add_to_top
|
|
55
|
+
#
|
|
56
|
+
# When +true+ (default), remove() will pull cards from the top
|
|
57
|
+
# of the pile. Otherwise, they come from the bottom.
|
|
58
|
+
#
|
|
59
|
+
attr_accessor :remove_from_top
|
|
60
|
+
#
|
|
61
|
+
# This method contains a Proc object that is passed card indices
|
|
62
|
+
# when rendering the default views. The Proc is excepted to
|
|
63
|
+
# return +true+ or +false+ indicating if the card at that index
|
|
64
|
+
# is face up or face down.
|
|
65
|
+
#
|
|
66
|
+
attr_accessor :face_up
|
|
67
|
+
|
|
68
|
+
# Fetch a card by _index_.
|
|
69
|
+
def []( index )
|
|
70
|
+
@pile[index]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Add _card_ to this Pile.
|
|
74
|
+
def add( card )
|
|
75
|
+
if @add_to_top
|
|
76
|
+
@pile.unshift(card)
|
|
77
|
+
else
|
|
78
|
+
@pile << card
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
alias_method :<<, :add
|
|
84
|
+
|
|
85
|
+
include Enumerable
|
|
86
|
+
|
|
87
|
+
# Iterate through the cards.
|
|
88
|
+
def each( )
|
|
89
|
+
@pile.each_index do |index|
|
|
90
|
+
next unless @face_up[index]
|
|
91
|
+
|
|
92
|
+
yield @pile[index]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Remove and return a card from this Pile.
|
|
97
|
+
def remove( )
|
|
98
|
+
if @remove_from_top
|
|
99
|
+
@pile.shift
|
|
100
|
+
else
|
|
101
|
+
@pile.pop
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
|
2
|
+
|
|
3
|
+
# currency.rb
|
|
4
|
+
#
|
|
5
|
+
# Created by Gregory Brown on 2005-05-30
|
|
6
|
+
# Copyright 2005 smtose.org. All rights reserved
|
|
7
|
+
|
|
8
|
+
require "gambit/viewable"
|
|
9
|
+
|
|
10
|
+
module Gambit
|
|
11
|
+
module Tools
|
|
12
|
+
#
|
|
13
|
+
# A general money tool for managing various denominations and the
|
|
14
|
+
# conversions between them.
|
|
15
|
+
#
|
|
16
|
+
class Currency
|
|
17
|
+
include Gambit::Viewable
|
|
18
|
+
register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/^\t{3}/, ""))
|
|
19
|
+
<dl class="currency">
|
|
20
|
+
% resources = @holdings.keys
|
|
21
|
+
% resources = resources.sort_by { |res| res.to_s }
|
|
22
|
+
% resources.each do |resource|
|
|
23
|
+
<dt><%= resource.to_s.capitalize %></dt>
|
|
24
|
+
<dd><%= @holdings[resource] %></dd>
|
|
25
|
+
% if resource != resources.last
|
|
26
|
+
|
|
27
|
+
% end
|
|
28
|
+
% end
|
|
29
|
+
</dl>
|
|
30
|
+
END_HTML_VIEW
|
|
31
|
+
register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/^\t+/, ""))
|
|
32
|
+
% resources = @holdings.keys
|
|
33
|
+
% resources = resources.sort_by { |res| res.to_s }
|
|
34
|
+
% res_width = resources.max { |a, b| a.to_s.length <=>
|
|
35
|
+
% b.to_s.length }.to_s.length
|
|
36
|
+
% amt_width = @holdings.values.max { |a, b| a.to_s.length <=>
|
|
37
|
+
% b.to_s.length }.
|
|
38
|
+
% to_s.length
|
|
39
|
+
% resources.each do |resource|
|
|
40
|
+
<%= "%\#{res_width}s" % resource.to_s.capitalize %>: <%=
|
|
41
|
+
"%\#{amt_width}d" % @holdings[resource] %>
|
|
42
|
+
% end
|
|
43
|
+
END_TEXT_VIEW
|
|
44
|
+
|
|
45
|
+
@@values = Hash.new(0)
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# Used to set a standard _ratio_ for a _resource_ Currency objects
|
|
49
|
+
# can use.
|
|
50
|
+
#
|
|
51
|
+
def self.[]=( resource, ratio )
|
|
52
|
+
@@values[resource] = ratio.to_f
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Fetch a standard ratio for a _resource_ Currency objects can use.
|
|
57
|
+
#
|
|
58
|
+
def self.[]( resource )
|
|
59
|
+
return @@values[resource]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Calculates a relative_value _from_resource_ _to_resource_.
|
|
63
|
+
def self.relative_value( from_resource, to_resource )
|
|
64
|
+
if @@values.key?(from_resource) && @@values.key?(to_resource)
|
|
65
|
+
@@values[from_resource] / @@values[to_resource]
|
|
66
|
+
else
|
|
67
|
+
raise ArgumentError, "Can't find conversion ratio!"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#
|
|
72
|
+
# Creates a new Currency object. Optionally, you may provide a Hash
|
|
73
|
+
# of _initial_holdings_.
|
|
74
|
+
#
|
|
75
|
+
def initialize( initial_holdings = nil )
|
|
76
|
+
if initial_holdings.is_a? Hash
|
|
77
|
+
@holdings = initial_holdings
|
|
78
|
+
else
|
|
79
|
+
@holdings = Hash.new(0)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The holdings Hash.
|
|
84
|
+
attr_reader :holdings
|
|
85
|
+
|
|
86
|
+
# Delegates to the holdings Hash.
|
|
87
|
+
def []( resource )
|
|
88
|
+
return @holdings[resource]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#
|
|
92
|
+
# Remove _amount_ of _resource_ from these holdings, optionally
|
|
93
|
+
# _as_resource_.
|
|
94
|
+
#
|
|
95
|
+
def withdraw( amount, resource, as_resource = nil )
|
|
96
|
+
if (@holdings[resource] - amount >= 0 && as_resource.nil?)
|
|
97
|
+
@holdings[resource] -= amount
|
|
98
|
+
return amount
|
|
99
|
+
elsif (@holdings[resource] - amount >= 0)
|
|
100
|
+
@holdings[resource] -= amount
|
|
101
|
+
return amount *
|
|
102
|
+
self.class.relative_value(resource, as_resource)
|
|
103
|
+
else
|
|
104
|
+
raise ArgumentError, "Insufficiant Funds"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Add _amount_ of _resource_ to these holdings.
|
|
109
|
+
def deposit( amount, resource )
|
|
110
|
+
if @@values.key?(resource)
|
|
111
|
+
@holdings[resource] += amount
|
|
112
|
+
return amount
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Combine holding between this Currency object and _other_currency_.
|
|
117
|
+
def +( other_currency )
|
|
118
|
+
new_holdings = Hash.new(0)
|
|
119
|
+
@holdings.each do |key,value|
|
|
120
|
+
new_holdings[key] += value
|
|
121
|
+
end
|
|
122
|
+
other_currency.holdings.each do |key,value|
|
|
123
|
+
new_holdings[key] += value
|
|
124
|
+
end
|
|
125
|
+
return Currency.new(new_holdings)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Remove holdings in _other_currency_ from this Currency object.
|
|
129
|
+
def -( other_currency )
|
|
130
|
+
new_holdings = Hash.new(0)
|
|
131
|
+
other_currency.holdings.each do |key,value|
|
|
132
|
+
new_holdings[key] = @holdings[key] - value
|
|
133
|
+
end
|
|
134
|
+
if new_holdings.values.min < 0
|
|
135
|
+
raise ArgumentError, "Detected negative value"
|
|
136
|
+
end
|
|
137
|
+
return Currency.new(new_holdings)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
include Enumerable
|
|
141
|
+
|
|
142
|
+
# Delegates to the holdings Hash.
|
|
143
|
+
def each(&block)
|
|
144
|
+
@holdings.each(&block)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
|
2
|
+
|
|
3
|
+
# dice.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
|
+
# An object for managing a roll of dice.
|
|
13
|
+
class Dice
|
|
14
|
+
include Gambit::Viewable
|
|
15
|
+
register_view(:gambit_html, <<-END_HTML_VIEW.gsub(/^\t{3}/, ""))
|
|
16
|
+
<ul class="dice">
|
|
17
|
+
% each do |die|
|
|
18
|
+
<li><%= die %></li>
|
|
19
|
+
% end
|
|
20
|
+
</ul>
|
|
21
|
+
END_HTML_VIEW
|
|
22
|
+
register_view(:gambit_text, <<-END_TEXT_VIEW.gsub(/^\t+/, ""))
|
|
23
|
+
% if @dice == 1
|
|
24
|
+
<%= "a \#{@roll.first}" %>
|
|
25
|
+
% else
|
|
26
|
+
<%= "\#{@roll[0..-2].join(', ')} and \#{@roll[-1]}" %>
|
|
27
|
+
% end
|
|
28
|
+
END_TEXT_VIEW
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Creates a new Dice object, rolling the indicated _count_ of the
|
|
32
|
+
# indicated _sided_ dice. The sides on a die need not be realistic,
|
|
33
|
+
# so this object can also be used to manage a coin toss (a two-sided
|
|
34
|
+
# die).
|
|
35
|
+
#
|
|
36
|
+
# :call-seq:
|
|
37
|
+
# Dice.new(count, sides) -> count_d_sides_roll
|
|
38
|
+
# Dice.new(sides) -> one_d_sides_roll
|
|
39
|
+
# Dice.new() -> one_d_six_roll
|
|
40
|
+
#
|
|
41
|
+
def initialize( *args )
|
|
42
|
+
@dice, @sides = if args.size == 2
|
|
43
|
+
args
|
|
44
|
+
elsif args.size == 1
|
|
45
|
+
[1, args.first]
|
|
46
|
+
else
|
|
47
|
+
[1, 6]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@roll = Array.new(@dice) { rand(@sides) + 1 }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Fetch the die at the provided _index_.
|
|
54
|
+
def []( index )
|
|
55
|
+
@roll[index]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#
|
|
59
|
+
# Returns a count of all dice in the roll matching the provided
|
|
60
|
+
# _pips_.
|
|
61
|
+
#
|
|
62
|
+
def count( pips )
|
|
63
|
+
@roll.inject(0) do |total, die|
|
|
64
|
+
if die == pips then total + 1 else total end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
include Enumerable
|
|
69
|
+
|
|
70
|
+
# Iterate over each die in the roll.
|
|
71
|
+
def each( &block )
|
|
72
|
+
@roll.each(&block)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#
|
|
76
|
+
# Count the number of "failure" dice in this roll. A die is
|
|
77
|
+
# a failure if it is less than or equal to the provided _maximum_.
|
|
78
|
+
#
|
|
79
|
+
def fail( maximum )
|
|
80
|
+
@roll.inject(0) do |total, die|
|
|
81
|
+
if die <= maximum then total + 1 else total end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
# A call to matches?() allows _pattern_ matching for dice rolls. A
|
|
87
|
+
# _pattern_ may contain single-letter Strings and/or Integers.
|
|
88
|
+
#
|
|
89
|
+
# Strings are used to locate recurring die patterns. For example,
|
|
90
|
+
# one could check for a full house with
|
|
91
|
+
# <tt>dice.matches?(*w{x x x y y})</tt>.
|
|
92
|
+
#
|
|
93
|
+
# Integers can be used to locate sequences of dice. For example,
|
|
94
|
+
# one could check for a small straight with
|
|
95
|
+
# <tt>dice.matches?(*(1..4).to_a)</tt>.
|
|
96
|
+
#
|
|
97
|
+
def matches?( *pattern )
|
|
98
|
+
digits, letters = pattern.partition { |e| e.is_a?(Integer) }
|
|
99
|
+
matches_digits?(digits) and matches_letters?(letters)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
#
|
|
103
|
+
# A call to this method will reroll the indicated dice. Dice can be
|
|
104
|
+
# selected by block or _indices_.
|
|
105
|
+
#
|
|
106
|
+
# :call-seq:
|
|
107
|
+
# reroll(*indices) -> dice
|
|
108
|
+
# reroll() { |die| ... } -> dice
|
|
109
|
+
#
|
|
110
|
+
def reroll( *indices )
|
|
111
|
+
if block_given?
|
|
112
|
+
@roll.each_with_index do |die, i|
|
|
113
|
+
@roll[i] = rand(6) + 1 if yield(die)
|
|
114
|
+
end
|
|
115
|
+
elsif indices.empty?
|
|
116
|
+
@roll = Array.new(@dice) { rand(@sides) + 1 }
|
|
117
|
+
else
|
|
118
|
+
indices.each { |i| @roll[i] = rand(6) + 1 }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
#
|
|
125
|
+
# Count the number of "success" dice in this roll. A die is
|
|
126
|
+
# a success if it is greater than or equal to the provided
|
|
127
|
+
# _minimum_.
|
|
128
|
+
#
|
|
129
|
+
def success( minimum )
|
|
130
|
+
@roll.inject(0) do |total, die|
|
|
131
|
+
if die >= minimum then total + 1 else total end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
#
|
|
136
|
+
# Returns a total sum of all dice in the roll matching the provided
|
|
137
|
+
# _pips_. If _pips_ is +nil+ (the default), all dice are totaled.
|
|
138
|
+
#
|
|
139
|
+
def sum( pips = nil )
|
|
140
|
+
if pips
|
|
141
|
+
@roll.inject(0) do |total, die|
|
|
142
|
+
if die == pips then total + die else total end
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
@roll.inject(0) { |total, die| total + die }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Add the sum() of this roll to an Integer.
|
|
150
|
+
def +( other ) sum + other end
|
|
151
|
+
# Subtract the sum() of this roll from an Integer.
|
|
152
|
+
def -( other ) sum - other end
|
|
153
|
+
# Multiply the sum() of this roll to an Integer.
|
|
154
|
+
def *( other ) sum * other end
|
|
155
|
+
# Divide the sum() of this roll by an Integer.
|
|
156
|
+
def /( other ) sum / other end
|
|
157
|
+
|
|
158
|
+
#
|
|
159
|
+
# Support for using Dice objects in ordinary Ruby math. Rolls are
|
|
160
|
+
# added to an Integer or Float by sum().
|
|
161
|
+
#
|
|
162
|
+
def coerce( other )
|
|
163
|
+
if other.is_a? Integer
|
|
164
|
+
[other, self.sum]
|
|
165
|
+
else
|
|
166
|
+
[Float(other), Float(self.sum)]
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
# The "number pattern" half of matches?().
|
|
173
|
+
def matches_digits?( digits )
|
|
174
|
+
return true if digits.size < 2
|
|
175
|
+
|
|
176
|
+
digits.sort!
|
|
177
|
+
test = @roll.uniq.sort
|
|
178
|
+
loop do
|
|
179
|
+
(0..(@roll.length - digits.length)).each do |index|
|
|
180
|
+
return true if test[index, digits.length] == digits
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
digits.collect! { |d| d + 1 }
|
|
184
|
+
break if digits.last > 6
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
false
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# The "letter pattern" half of matches?().
|
|
191
|
+
def matches_letters?( letters )
|
|
192
|
+
return true if letters.size < 2
|
|
193
|
+
|
|
194
|
+
counts = Hash.new(0)
|
|
195
|
+
letters.each { |l| counts[l] += 1 }
|
|
196
|
+
counts = counts.values.sort.reverse
|
|
197
|
+
|
|
198
|
+
pips = @roll.uniq
|
|
199
|
+
counts.each do |c|
|
|
200
|
+
return false unless match = pips.find { |p| count(p) >= c }
|
|
201
|
+
pips.delete(match)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
true
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
class Integer
|
|
211
|
+
#
|
|
212
|
+
# A shortcut for rolling dice in a program. For example:
|
|
213
|
+
#
|
|
214
|
+
# str = 3.d(6) + 2
|
|
215
|
+
#
|
|
216
|
+
def d( sides )
|
|
217
|
+
Gambit::Tools::Dice.new(self, sides)
|
|
218
|
+
end
|
|
219
|
+
end
|