Skip to content

Instantly share code, notes, and snippets.

@elight
Created April 19, 2009 17:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elight/98135 to your computer and use it in GitHub Desktop.
Save elight/98135 to your computer and use it in GitHub Desktop.
class DieCode < String
cattr_accessor :wild_die
attr_reader :dice, :pips
REGEXP = /(^\d+)D\+?([12])?$/
class << self
def valid?(code)
code =~ REGEXP
end
def from_int(code)
return unless code.is_a?(Fixnum)
str = "#{code / 3}D"
pip = code % 3
str += "+#{pip}" if pip != 0
str
end
end
def initialize(code = "")
val = code
unless DieCode.valid? code
val = DieCode.from_int code
raise Exception.new("Invalid die code '#{code}'") unless val
end
val =~ REGEXP
@dice = $1.to_i
@pips = $2.to_i
super val
end
def to_i
(3 * dice) + pips
end
def +(other_code)
DieCode.new(to_i + other_code.to_i)
end
def -(other_code)
DieCode.new((to_i - other_code.to_i).abs)
end
def roll
first_roll = true
wild_result = nil
the_roll = (1..self.dice).inject(0) { |total, v|
if first_roll
first_roll = false
roll, wild_result = handle_wild_die
total += roll
else
total += die_roll
end
total
} + self.pips
@@wild_die ? [the_roll, wild_result] : the_roll
end
def die_roll
rand(6) + 1
end
private
def handle_wild_die
return die_roll unless @@wild_die
wild_result = nil
total = 0
first_roll = true
begin
roll = die_roll
total += roll
if first_roll && roll == 1
wild_result = :FAIL
elsif roll == 6
wild_result ||= :SUCCESS
end
first_roll = false
end while roll == 6
[total, wild_result]
end
end
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe DieCode, "that is invalid" do
it "should raise when created" do
lambda { DieCode.new "" }.should raise_error
end
end
describe DieCode, "provided as an int" do
it "should be parseable into a DieCode" do
lambda { DieCode.new 10 }.should_not raise_error
end
end
describe DieCode, "that is valid" do
before do
@valid_code1 = DieCode.new "1D"
end
it "should start with an integer value representing the number of dice" do
@valid_code1.should match(/^\d+/)
end
it "should have a 'D' following the number of dice" do
@valid_code1.should match(/^\d+D/)
end
it "should end with an optional '+1' or '+2'" do
@valid_code1.should match(/(\+[12])?$/)
end
it "can be resolved to an integer by multiplying the number before the D by 3 and adding the number after the '+'" do
@valid_code1.to_i.should == 3
end
it "can be rolled" do
@valid_code1.should respond_to(:roll)
end
describe "when the wild die is disabled" do
before do
DieCode.wild_die = false
end
it "should return the die roll and the wild die result" do
@valid_code1.roll.should be_a(Fixnum)
end
end
describe "when the wild die is enabled" do
before do
DieCode.wild_die = true
end
it "should return the die roll and the wild die result" do
@valid_code1.roll.should have(2).values
end
it "should indicate a wild die failure if the wild die turns up a 1 on its first roll" do
@valid_code1.should_receive(:die_roll).at_least(:once).and_return(1)
total, wild_die_result = @valid_code1.roll
wild_die_result.should == :FAIL
end
it "should indicate a wild die success if the wild die rolls one or more 6s" do
@valid_code1.stub!(:die_roll).and_return(6,5)
total, wild_die_result = @valid_code1.roll
wild_die_result.should == :SUCCESS
end
it "should roll an additional die on the occurrence of a 6 on the wild die" do
@valid_code1.stub!(:die_roll).and_return(6,5)
total, wild_die_result = @valid_code1.roll
total.should == 11
end
end
end
describe "Two valid die codes" do
before do
@code1 = DieCode.new "3D+1"
@code2 = DieCode.new 13
end
it "should be additive" do
(@code1 + @code2).should == DieCode.new("7D+2")
end
it "should be subtractible" do
(@code2 - @code1).should == DieCode.new("1D")
end
it "should be comparable" do
@code2.should == DieCode.new("4D+1")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment