Skip to content

Instantly share code, notes, and snippets.

@zph
Created May 5, 2014 15:20
Show Gist options
  • Select an option

  • Save zph/11539697 to your computer and use it in GitHub Desktop.

Select an option

Save zph/11539697 to your computer and use it in GitHub Desktop.
require 'singleton' # => true
module Kernel
def d
DiceBag.instance
end
end
class DiceDSL
class << self
def parse(dices)
%r{^
(?<top_or_bottom>t|b)? # Flag for finding the top or bottom dice
(?<tb_number>\d)? # Number of top or bottom dice
\[(?<dice>.*)\] # The main dice string, to be parsed later
(?<explode>\*)? # Reroll (explode) die rolls? (i.e. 6 on 1d6)
(?<plus>(\+|\-)\d+)? # Additional points to add to roll (or subtract)
\=? # = Target Number or range follows...
(?<target>.*)?$ # The target number (single 4, range 4..6, above 4+, below 4-)
}x =~ dices
die_set = eval "[" + string_to_dice_array(dice) + "]"
dice_pool({set: die_set, top: tb_number.to_i, plus: plus.to_i})
end
def dice_pool(*args)
DicePool.new(*args)
end
def string_to_dice_array(dice)
dice.gsub(/\s+/, '').gsub(/[\[\]]/, ''). # => "1d4+1d6+1d8+1d10+1d12+1d20"
split("+"). # => ["1d4", "1d6", "1d8", "1d10", "1d12", "1d20"]
map{|ds| ds.split('d') }. # => [["1", "4"], ["1", "6"], ["1", "8"], ["1", "10"], ["1", "12"], ["1", "20"]]
collect {|how_many, dice_face| (["d^#{dice_face}"]*how_many.to_i ).join(",") }. # => ["d^4", "d^6", "d^8", "d^10", "d^12", "d^20"]
join(",") # => "d^4,d^6,d^8,d^10,d^12,d^20"
end
end
end
str = "[1d4+1d6+1d8+1d10+1d12+1d20]" # => "[1d4+1d6+1d8+1d10+1d12+1d20]"
DiceDSL.string_to_dice_array(str) # => "d^4,d^6,d^8,d^10,d^12,d^20"
class DiceBag
include Singleton # => DiceBag
def ^(max)
@max = max
Die.new(sides: max)
end
def roll(what: self, plus: 0, keep: nil, how_many: nil)
puts "Rolling Away..."
if what.is_a? Array
keep = what.length if keep.nil?
DiceSet.new set: what, top: keep, plus: plus
elsif what.is_a? Die
raise "Must include :how_many if you pass an individual Die" if how_many.nil?
keep = how_many if keep.nil?
DicePool.new set: how_many.times.collect{ what.clone.reroll }, top: keep, plus: plus
else
raise what.class.inspect
end
end
end
class Die
def initialize(sides: 6, plus: 0)
@plus = plus
@sides = sides
roll
end
def +(x)
@plus = x
reroll
end
def inspect; "d#{@sides}#{"+#{@plus}" unless @plus.zero?}=>#{value}"; end
def value; @value; end
def roll; @value = rand(1..@sides)+@plus; self end
def reroll; roll; end
def to_s; value; end
def to_i; value; end
end
class DicePool
attr_reader :how_many, :top_number_of_dice, :results # => nil
attr_writer :how_many, :top_number_of_dice, :results # => nil
def initialize(set: nil, plus: 0, top: 2)
@set = set.is_a?(Die) ? [set] : set
raise 'Dice passed to DicePool must be instances of Die.' unless @set.any?{|s| s.is_a?(Die) }
@plus = plus
@how_many = 100_000
@top_number_of_dice = top
generate_results
end
def highest(top: @top_number_of_dice, re_roll: false)
reroll if re_roll
highest_results = @set.sort_by{|d| d.value }.reverse[0..top-1]
return highest_results.map(&:to_i).inject(0){|sum,i| sum += i } + @plus
end
def reroll
@set.map(&:reroll)
self
end
def roll; reroll; end
def generate_results
@results = Hash.new(0)
@how_many.times { @results[highest(top: @top_number_of_dice, re_roll: true)] += 1 }
self
end
def graph(r=@results)
raise "There are no statistical results to graph. Run :generate_results" if r.empty?
data = ""
data << "Running #{@how_many} times, taking the top #{@top_number_of_dice}, the breakdown of results by value rolled are:"
data << "\n" + ("…" * 100) + "\n"
r.sort.each do |total, count|
percentage = (count.to_f/@how_many.to_f)
data << sprintf("%3d ", total)
data << '('
data << sprintf("%5.2f", percentage * 100)
data << "%) "
dots = (percentage*500).to_i
if dots > 100
data << '°' * 80
data << "*(#{dots})"
else
data << '°' * (percentage*500).to_i
end
data << "\n"
end
data
end
def results
"results: #{(@results.sort_by {|k,v| k }).to_h.inspect}"
end
def total
# @set.map(&:value).inject(0){|sum, i| sum += i} + @plus
highest(top: @top_number_of_dice)
end
def inspect
puts "<DicePool:\n @set: #{@set.inspect}, @plus: #{@plus}, @total: #{total}, @top: #{@top_number_of_dice}>"
end
def self.inspect
puts "<DicePool:\n @set: #{@set.inspect}, @plus: #{@plus}, @total: #{total}, @top: #{@top_number_of_dice}>"
end
end
Dice = DiceBag.instance # => #<DiceBag:0x007fc3c9904518>
# ds=DiceDSL.parse("3[4d6]"); [ds.reroll.total, ds.reroll.total, ds.reroll.total, ds.reroll.total, ds.reroll.total, ds.reroll.total]
@zph

zph commented May 5, 2014

Copy link
Copy Markdown
Author

If this input string is correct and I'm using method correctly (no promises :), then it looks like it's dropping the d4 from this string.

Unless of course, we add a gsub to remove [ or ].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment