polyhedral 0.0.1
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/README +23 -0
- data/lib/polyhedral.rb +22 -0
- data/lib/polyhedral/die.rb +313 -0
- metadata +55 -0
data/README
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
== What
|
2
|
+
Ye classic polyhedral dice program, complete with personalization.
|
3
|
+
|
4
|
+
== How
|
5
|
+
require 'rubygems'
|
6
|
+
require 'polyhedral'
|
7
|
+
|
8
|
+
== Install
|
9
|
+
To construct a Ruby gem, just type:
|
10
|
+
|
11
|
+
rake gem
|
12
|
+
|
13
|
+
To install it, type:
|
14
|
+
|
15
|
+
sudo gem install polyhedral-0.0.1.gem
|
16
|
+
|
17
|
+
== Notes
|
18
|
+
I haven't tested this code with YARV, so YMMV.
|
19
|
+
|
20
|
+
== Contribute
|
21
|
+
The source is availble at github: git://github.com/yesmar/polyhedral.git. Feel
|
22
|
+
free to clone it and implement your own awesome ideas. You can also send your
|
23
|
+
patches by email to yesmar[at]speakeasy[dot]net.
|
data/lib/polyhedral.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# polyhedram.rb
|
4
|
+
# yesmar@speakeasy.net
|
5
|
+
|
6
|
+
module Polyhedral
|
7
|
+
Polyhedral::NAME = 'polyhedral'
|
8
|
+
Polyhedral::VERSION = '0.0.1'
|
9
|
+
Polyhedral::COPYRIGHT = 'Copyright (c) 2008 Ramsey Dow'
|
10
|
+
def self.copyright() Polyhedral::COPYRIGHT end
|
11
|
+
def self.version() Polyhedral::VERSION end
|
12
|
+
def self.libdir() File.expand_path(__FILE__).gsub(%r/\.rb$/, '') end
|
13
|
+
end
|
14
|
+
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)+'/polyhedral')
|
16
|
+
|
17
|
+
require 'die'
|
18
|
+
|
19
|
+
# TODO: bad developer, no unit tests
|
20
|
+
# TODO: need to finish developing example programs
|
21
|
+
|
22
|
+
raise RuntimeError, 'This library is for require only' if $0 == __FILE__
|
@@ -0,0 +1,313 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# die.rb
|
4
|
+
# yesmar@speakeasy.net
|
5
|
+
|
6
|
+
module Polyhedral
|
7
|
+
class Die
|
8
|
+
attr_reader :sides, :material, :color
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
DEFAULT_SIDES = 6
|
12
|
+
DEFAULT_TIMES = 1
|
13
|
+
|
14
|
+
# initialize Die instance
|
15
|
+
def initialize(sides=DEFAULT_SIDES, name=nil, material=nil, color=nil,
|
16
|
+
seed=nil
|
17
|
+
)
|
18
|
+
raise ArgumentError, 'nil sides' if sides.nil?
|
19
|
+
raise ArgumentError, 'invalid sides class' if !sides.is_a?(Integer)
|
20
|
+
if !name.nil?
|
21
|
+
raise ArgumentError, 'nil name' if name.nil?
|
22
|
+
raise ArgumentError, 'invalid name' if name.class != String
|
23
|
+
raise ArgumentError, 'empty name' if name.empty?
|
24
|
+
end
|
25
|
+
if !material.nil?
|
26
|
+
raise ArgumentError, 'nil material' if material.nil?
|
27
|
+
raise ArgumentError, 'invalid material' if material.class != String
|
28
|
+
raise ArgumentError, 'empty material' if material.empty?
|
29
|
+
end
|
30
|
+
if !color.nil?
|
31
|
+
raise ArgumentError, 'nil color' if color.nil?
|
32
|
+
raise ArgumentError, 'invalid color' if color.class != String
|
33
|
+
raise ArgumentError, 'empty color' if color.empty?
|
34
|
+
end
|
35
|
+
if !seed.nil?
|
36
|
+
raise ArgumentError, 'invalid seed class' if !seed.is_a?(Integer)
|
37
|
+
end
|
38
|
+
|
39
|
+
@sides = sides
|
40
|
+
@seed = seed
|
41
|
+
|
42
|
+
if !@seed.nil?
|
43
|
+
srand(@seed)
|
44
|
+
else
|
45
|
+
srand
|
46
|
+
end
|
47
|
+
|
48
|
+
@count = 0
|
49
|
+
@frequency = Array.new(@sides, 0)
|
50
|
+
|
51
|
+
@name = name
|
52
|
+
@material = material
|
53
|
+
@color = color
|
54
|
+
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# dump instance state to specified output stream
|
59
|
+
def dump(stream=STDOUT, indent=0)
|
60
|
+
raise ArgumentError, 'nil stream' if stream.nil?
|
61
|
+
raise ArgumentError, 'invalid stream class' if stream.class != IO
|
62
|
+
raise ArgumentError, 'nil indent' if indent.nil?
|
63
|
+
raise ArgumentError, 'invalid indent class' if !indent.is_a?(Integer)
|
64
|
+
raise ArgumentError, 'indent range error' if indent < 0
|
65
|
+
|
66
|
+
# instance
|
67
|
+
indent.times { stream << ' ' }
|
68
|
+
stream << "#{self.class}<id=#{self.object_id}>:\n"
|
69
|
+
|
70
|
+
indent += 2
|
71
|
+
|
72
|
+
# sides
|
73
|
+
indent.times { stream << ' ' }
|
74
|
+
stream << "sides: #{@sides}\n"
|
75
|
+
|
76
|
+
# seed
|
77
|
+
indent.times { stream << ' ' }
|
78
|
+
stream << "seed: #{@seed ? @seed : 'nil'}\n"
|
79
|
+
|
80
|
+
# count
|
81
|
+
indent.times { stream << ' ' }
|
82
|
+
stream << "count: #{@count}\n"
|
83
|
+
|
84
|
+
# frequency
|
85
|
+
indent.times { stream << ' ' }
|
86
|
+
stream << "frequency<id=#{@frequency.object_id}>:\n"
|
87
|
+
(indent-1).times { stream << ' ' }
|
88
|
+
@frequency.each { |f| stream << " #{f}" }
|
89
|
+
stream << "\n"
|
90
|
+
|
91
|
+
# name
|
92
|
+
indent.times { stream << ' ' }
|
93
|
+
stream << "name: #{@name ? @name : 'nil'}\n"
|
94
|
+
|
95
|
+
# material
|
96
|
+
indent.times { stream << ' ' }
|
97
|
+
stream << "material: #{@material ? @material : 'nil'}\n"
|
98
|
+
|
99
|
+
# color
|
100
|
+
indent.times { stream << ' ' }
|
101
|
+
stream << "color: #{@color ? @color : 'nil'}\n"
|
102
|
+
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# throw die n times (applying modifier as appropriate)
|
107
|
+
def throw(quantity=DEFAULT_TIMES, modifier=0)
|
108
|
+
raise ArgumentError, 'nil quantity' if quantity.nil?
|
109
|
+
raise ArgumentError, 'invalid quantity class' if !quantity.is_a?(Integer)
|
110
|
+
raise ArgumentError, 'invalid quantity value' if quantity < 1
|
111
|
+
raise ArgumentError, 'nil modifier' if modifier.nil?
|
112
|
+
raise ArgumentError, 'invalid modifier class' if !modifier.is_a?(Integer)
|
113
|
+
|
114
|
+
score = 0
|
115
|
+
|
116
|
+
# update throw count
|
117
|
+
@count += quantity
|
118
|
+
|
119
|
+
quantity.times do
|
120
|
+
result = rand(@sides) + 1
|
121
|
+
score += result
|
122
|
+
|
123
|
+
# update frequency count
|
124
|
+
@frequency[result-1] += 1
|
125
|
+
end
|
126
|
+
|
127
|
+
score+modifier
|
128
|
+
end
|
129
|
+
|
130
|
+
# emit Die instance as Integer
|
131
|
+
def to_i
|
132
|
+
@sides
|
133
|
+
end
|
134
|
+
|
135
|
+
# emit Die instance as String
|
136
|
+
def to_s
|
137
|
+
"d#{sides}"
|
138
|
+
end
|
139
|
+
|
140
|
+
# return description of the Die instance
|
141
|
+
def inspect
|
142
|
+
desc = []
|
143
|
+
|
144
|
+
desc << "#{@name} is " if !@name.nil? && !@name.empty?
|
145
|
+
desc << 'a'
|
146
|
+
desc << " #{@color}" if !@color.nil? && !@color.empty?
|
147
|
+
desc << " #{@material}" if !@material.nil? && !@material.empty?
|
148
|
+
desc << " #{self}"
|
149
|
+
|
150
|
+
desc << " (#{@count} throw"
|
151
|
+
if @count != 1
|
152
|
+
desc << 's)'
|
153
|
+
else
|
154
|
+
desc << ')'
|
155
|
+
end
|
156
|
+
|
157
|
+
desc.join
|
158
|
+
end
|
159
|
+
|
160
|
+
# dump frequency graph to specified output stream
|
161
|
+
def frequency(stream=STDOUT)
|
162
|
+
raise ArgumentError, 'nil stream' if stream.nil?
|
163
|
+
raise ArgumentError, 'invalid stream class' if stream.class != IO
|
164
|
+
|
165
|
+
num = max_frequency
|
166
|
+
min = min_frequency
|
167
|
+
min -= 1 if min > 0
|
168
|
+
|
169
|
+
stream << "Throws Frequency Graph"
|
170
|
+
stream << " for #{@name}" if !@name.nil? && !@name.empty?
|
171
|
+
stream << "\n\n"
|
172
|
+
|
173
|
+
while num > min do
|
174
|
+
printf(stream, "%6d", num)
|
175
|
+
|
176
|
+
@frequency.each do |f|
|
177
|
+
if f >= num
|
178
|
+
stream << ' *'
|
179
|
+
else
|
180
|
+
stream << ' '
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
stream << "\n"
|
185
|
+
num -= 1
|
186
|
+
end
|
187
|
+
|
188
|
+
stream << "\n "
|
189
|
+
@frequency.length.times do |f|
|
190
|
+
n = f+1
|
191
|
+
if n.to_s.length >= 2
|
192
|
+
stream << ' '
|
193
|
+
else
|
194
|
+
stream << ' '
|
195
|
+
end
|
196
|
+
stream << n
|
197
|
+
end
|
198
|
+
stream << "\n"
|
199
|
+
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
|
203
|
+
# return array of historically lowest scoring side(s)
|
204
|
+
def lowest
|
205
|
+
min_f = 1000000
|
206
|
+
low_side = @sides-1
|
207
|
+
|
208
|
+
@sides.times do |s|
|
209
|
+
if @frequency[s] < min_f
|
210
|
+
min_f = @frequency[s]
|
211
|
+
low_side = s
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
low_set = Array.new
|
216
|
+
low_set << low_side+1
|
217
|
+
|
218
|
+
@sides.times do |s|
|
219
|
+
next if s == low_side
|
220
|
+
low_set << s+1 if @frequency[s] == @frequency[low_side]
|
221
|
+
end
|
222
|
+
|
223
|
+
low_set
|
224
|
+
end
|
225
|
+
|
226
|
+
# return array of historically highest scoring side(s)
|
227
|
+
def highest
|
228
|
+
max_f = 0
|
229
|
+
high_side = 0
|
230
|
+
|
231
|
+
@sides.times do |s|
|
232
|
+
if @frequency[s] > max_f
|
233
|
+
max_f = @frequency[s]
|
234
|
+
high_side = s
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
high_set = Array.new
|
239
|
+
high_set << high_side+1
|
240
|
+
|
241
|
+
@sides.times do |s|
|
242
|
+
next if s == high_side
|
243
|
+
high_set << s+1 if @frequency[s] == @frequency[high_side]
|
244
|
+
end
|
245
|
+
|
246
|
+
high_set
|
247
|
+
end
|
248
|
+
|
249
|
+
# store Die instance to disk
|
250
|
+
def to_file(pathname)
|
251
|
+
# validate arguments
|
252
|
+
raise ArgumentError, 'nil pathname' if pathname.nil?
|
253
|
+
raise ArgumentError, 'invalid pathname class' if pathname.class != String
|
254
|
+
raise ArgumentError, 'empty pathname' if pathname.empty?
|
255
|
+
|
256
|
+
begin
|
257
|
+
f = File.open(pathname, 'w')
|
258
|
+
Marshal.dump(self, f)
|
259
|
+
rescue Exception => e
|
260
|
+
return false
|
261
|
+
ensure
|
262
|
+
f.close
|
263
|
+
end
|
264
|
+
|
265
|
+
return true
|
266
|
+
end
|
267
|
+
|
268
|
+
# load Die instance from disk
|
269
|
+
def Die.from_file(pathname)
|
270
|
+
# validate arguments
|
271
|
+
raise ArgumentError, 'nil pathname' if pathname.nil?
|
272
|
+
raise ArgumentError, 'invalid pathname class' if pathname.class != String
|
273
|
+
raise ArgumentError, 'empty pathname' if pathname.empty?
|
274
|
+
|
275
|
+
begin
|
276
|
+
f = File.open(pathname, 'r')
|
277
|
+
object = Marshal.load(f)
|
278
|
+
rescue Exception => e
|
279
|
+
return nil
|
280
|
+
ensure
|
281
|
+
f.close unless f.nil?
|
282
|
+
end
|
283
|
+
|
284
|
+
return object
|
285
|
+
end
|
286
|
+
|
287
|
+
private
|
288
|
+
|
289
|
+
# return minimum frequency
|
290
|
+
def min_frequency
|
291
|
+
min_f = 1000000
|
292
|
+
@frequency.each { |f| min_f = f if f < min_f }
|
293
|
+
min_f
|
294
|
+
end
|
295
|
+
|
296
|
+
# return maximum frequency
|
297
|
+
def max_frequency
|
298
|
+
max_f = 0
|
299
|
+
@frequency.each { |f| max_f = f if f > max_f }
|
300
|
+
max_f
|
301
|
+
end
|
302
|
+
|
303
|
+
@sides
|
304
|
+
@seed
|
305
|
+
@count
|
306
|
+
@frequency
|
307
|
+
@name
|
308
|
+
@material
|
309
|
+
@color
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
raise RuntimeError, 'This library is for require only' if $0 == __FILE__
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: polyhedral
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ramsey Dow
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-02-27 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: yesmar @nospam@ speakeasy.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- lib/polyhedral.rb
|
26
|
+
- lib/polyhedral/die.rb
|
27
|
+
- README
|
28
|
+
has_rdoc: true
|
29
|
+
homepage: http://polyhedral.rubyforge.org/
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: "0"
|
40
|
+
version:
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
rubyforge_project: http://rubyforge.org/projects/polyhedral/
|
50
|
+
rubygems_version: 1.0.1
|
51
|
+
signing_key:
|
52
|
+
specification_version: 2
|
53
|
+
summary: Ye classic polyhedral dice
|
54
|
+
test_files: []
|
55
|
+
|