# frozen_string_literal: true require 'helpers' # This spec demonstrates that documentation from the README.md works as intended describe GamesDice do describe '#create' do it "converts a string such as '3d6+6' into a GamesDice::Dice object" do d = GamesDice.create '3d6+6' expect(d).to be_a GamesDice::Dice end it "takes a parameter 'dice_description', which is a string such as '3d6' or '2d4-1'" do d = GamesDice.create '3d6' expect(d).to be_a GamesDice::Dice d = GamesDice.create '2d4-1' expect(d).to be_a GamesDice::Dice end it "takes an optional parameter 'prng', which should be an object that has a method 'rand( integer )'" do prng = TestPRNG.new d = GamesDice.create '3d6', prng expect(d).to be_a GamesDice::Dice (0..5).each do |dresult| allow(prng).to receive(:rand).and_return(dresult) expect(prng).to receive(:rand).with(6) expect(d.roll).to eql (dresult + 1) * 3 end end end end describe GamesDice::Dice do before :each do srand(67_809) end let(:dice) { GamesDice.create '3d6' } describe '#roll' do it 'simulates rolling the dice as they were described in the constructor' do expected_results = [11, 15, 11, 12, 12, 9] expected_results.each do |expected| expect(dice.roll).to eql expected end end end describe '#result' do it 'returns the value from the last call to roll' do expected_results = [11, 15, 11, 12, 12, 9] expected_results.each do |expected| dice.roll expect(dice.result).to eql expected end end it 'will be nil if no roll has been made yet' do expect(dice.result).to be_nil end end describe '#explain_result' do it 'attempts to show how the result from the last call to roll was composed' do expected_results = [ '3d6: 3 + 6 + 2 = 11', '3d6: 4 + 5 + 6 = 15', '3d6: 3 + 6 + 2 = 11', '3d6: 5 + 6 + 1 = 12' ] expected_results.each do |expected| dice.roll expect(dice.explain_result).to eql expected end end it 'will be nil if no roll has been made yet' do expect(dice.explain_result).to be_nil end end describe '#max' do it 'returns the maximum possible value from a roll of the dice' do expect(dice.max).to eql 18 end end describe '#min' do it 'returns the minimum possible value from a roll of the dice' do expect(dice.min).to eql 3 end end describe '#minmax' do it 'returns an array [ dice.min, dice.max ]' do expect(dice.minmax).to eql [3, 18] end end describe '#probabilities' do it 'calculates probability distribution for the dice' do pd = dice.probabilities expect(pd).to be_a GamesDice::Probabilities expect(pd.p_eql(3)).to be_within(1e-10).of 1.0 / 216 expect(pd.p_eql(11)).to be_within(1e-10).of 27.0 / 216 end end end describe GamesDice::Probabilities do let(:probs) { GamesDice.create('3d6').probabilities } describe '#to_h' do it 'returns a hash representation of the probability distribution' do h = probs.to_h expect(h).to be_valid_distribution expect(h[3]).to be_within(1e-10).of 1.0 / 216 expect(h[11]).to be_within(1e-10).of 27.0 / 216 end end describe '#max' do it 'returns maximum result in the probability distribution' do expect(probs.max).to eql 18 end end describe '#min' do it 'returns minimum result in the probability distribution' do expect(probs.min).to eql 3 end end describe '#p_eql( n )' do it 'returns the probability of a result equal to the integer n' do expect(probs.p_eql(3)).to be_within(1e-10).of 1.0 / 216 expect(probs.p_eql(2)).to eql 0.0 end end describe '#p_gt( n )' do it 'returns the probability of a result greater than the integer n' do expect(probs.p_gt(17)).to be_within(1e-10).of 1.0 / 216 expect(probs.p_gt(2)).to eql 1.0 end end describe '#p_ge( n )' do it 'returns the probability of a result greater than the integer n' do expect(probs.p_ge(17)).to be_within(1e-10).of 4.0 / 216 expect(probs.p_ge(3)).to eql 1.0 end end describe '#p_le( n )' do it 'returns the probability of a result less than or equal to the integer n' do expect(probs.p_le(17)).to be_within(1e-10).of 215.0 / 216 expect(probs.p_le(3)).to be_within(1e-10).of 1.0 / 216 end end describe '#p_lt( n )' do it 'returns the probability of a result less than the integer n' do expect(probs.p_lt(17)).to be_within(1e-10).of 212.0 / 216 expect(probs.p_lt(3)).to eql 0.0 end end end describe 'String Dice Description' do before :each do srand(35_241) end describe "'1d6'" do it 'returns expected results from rolling' do d = GamesDice.create '1d6' expect((1..20).map { |_n| d.roll }).to eql [6, 3, 2, 3, 4, 6, 4, 2, 6, 3, 3, 5, 6, 6, 3, 6, 5, 2, 1, 4] end end describe "'2d6 + 1d4'" do it 'returns expected results from rolling' do d = GamesDice.create '2d6 + 1d4' expect((1..5).map { |_n| d.roll }).to eql [11, 10, 12, 12, 14] end end describe "'1d100 + 1d20 - 5'" do it 'returns expected results from rolling' do d = GamesDice.create '1d100 + 1d20 - 5' expect((1..5).map { |_n| d.roll }).to eql [75, 78, 24, 102, 32] end end describe "'1d10x'" do it 'returns expected results from rolling' do d = GamesDice.create '1d10x' expect((1..20).map { |_n| d.roll }).to eql [2, 3, 4, 7, 6, 7, 4, 2, 6, 3, 7, 5, 6, 7, 6, 6, 5, 19, 4, 19] end end describe "'1d6r1'" do it 'returns expected results from rolling' do d = GamesDice.create '1d6r1' expect((1..20).map { |_n| d.roll }).to eql [6, 3, 2, 3, 4, 6, 4, 2, 6, 3, 3, 5, 6, 6, 3, 6, 5, 2, 4, 2] end end describe "'5d10r:10,add.k2'" do it 'returns expected results from rolling' do d = GamesDice.create '5d10r:10,add.k2' expect((1..5).map { |_n| d.roll }).to eql [13, 13, 14, 38, 15] end end describe "'3d10m6'" do it 'returns expected results from rolling' do d = GamesDice.create '3d10m6' expect((1..6).map { |_n| d.roll }).to eql [0, 3, 1, 1, 3, 2] end end describe "'5d10k2'" do it 'returns expected results from rolling' do d = GamesDice.create '5d10k2' expect((1..5).map { |_n| d.roll }).to eql [13, 13, 14, 19, 19] end end describe "'5d10x'" do it "is the same as '5d10r:10,add.'" do srand(235_241) d = GamesDice.create '5d10x' results1 = (1..50).map { d.roll } srand(235_241) d = GamesDice.create '5d10r:10,add.' results2 = (1..50).map { d.roll } expect(results1).to eql results2 end end describe "'1d6r:1.'" do it "should return same as '1d6r1'" do srand(235_241) d = GamesDice.create '1d6r:1.' results1 = (1..50).map { d.roll } srand(235_241) d = GamesDice.create '1d6r1' results2 = (1..50).map { d.roll } expect(results1).to eql results2 end end describe "'1d10r:10,replace,1.'" do it 'should roll a 10-sided die, re-roll a result of 10 and take the value of the second roll' do d = GamesDice.create '1d10r:10,replace,1.' expect((1..27).map do d.roll end).to eql [2, 3, 4, 7, 6, 7, 4, 2, 6, 3, 7, 5, 6, 7, 6, 6, 5, 9, 4, 9, 8, 3, 1, 6, 7, 1, 1] end end describe "'1d20r:<=10,use_best,1.'" do it 'should roll a 20-sided die, re-roll a result if 10 or lower, and use best result' do d = GamesDice.create '1d20r:<=10,use_best,1.' expect((1..20).map do d.roll end).to eql [18, 19, 20, 20, 3, 11, 7, 20, 15, 19, 6, 16, 17, 16, 15, 11, 9, 15, 20, 16] end end describe "'5d10r:10,add.k2', '5d10xk2' and '5d10x.k2'" do it 'should all be equivalent' do srand(135_241) d = GamesDice.create '5d10r:10,add.k2' results1 = (1..50).map { d.roll } srand(135_241) d = GamesDice.create '5d10xk2' results2 = (1..50).map { d.roll } srand(135_241) d = GamesDice.create '5d10x.k2' results3 = (1..50).map { d.roll } expect(results1).to eql results2 expect(results1).to eql results3 end end describe "'5d10r:>8,add.'" do it 'returns expected results from rolling' do d = GamesDice.create '5d10r:>8,add.' expect((1..5).map { |_n| d.roll }).to eql [22, 22, 31, 64, 26] end end describe "'9d6x.m:10.'" do it 'returns expected results from rolling' do d = GamesDice.create '9d6x.m:10.' expect((1..5).map { |_n| d.roll }).to eql [1, 2, 1, 1, 1] end it 'can be explained as number of exploding dice scoring 10+' do d = GamesDice.create '9d6x.m:10.' expect((1..5).map do |_n| d.roll d.explain_result end).to eql [ '9d6: [6+3] 9, 2, 3, 4, [6+4] 10, 2, [6+3] 9, 3, 5. Successes: 1', '9d6: [6+6+3] 15, [6+5] 11, 2, 1, 4, 2, 1, 3, 5. Successes: 2', '9d6: 1, [6+6+1] 13, 2, 1, 1, 3, [6+1] 7, 5, 4. Successes: 1', '9d6: [6+4] 10, 3, 4, 5, 5, 1, [6+3] 9, 3, 5. Successes: 1', '9d6: [6+3] 9, 3, [6+5] 11, 4, 2, 2, 1, 4, 5. Successes: 1' ] end end describe "'9d6x.m:10,1,S.'" do it 'returns expected results from rolling' do d = GamesDice.create '9d6x.m:10,1,S.' expect((1..5).map { |_n| d.roll }).to eql [1, 2, 1, 1, 1] end it "includes the string 'S' next to each success" do d = GamesDice.create '9d6x.m:10,1,S.' expect((1..5).map do |_n| d.roll d.explain_result end).to eql [ '9d6: [6+3] 9, 2, 3, 4, [6+4] 10 S, 2, [6+3] 9, 3, 5. Successes: 1', '9d6: [6+6+3] 15 S, [6+5] 11 S, 2, 1, 4, 2, 1, 3, 5. Successes: 2', '9d6: 1, [6+6+1] 13 S, 2, 1, 1, 3, [6+1] 7, 5, 4. Successes: 1', '9d6: [6+4] 10 S, 3, 4, 5, 5, 1, [6+3] 9, 3, 5. Successes: 1', '9d6: [6+3] 9, 3, [6+5] 11 S, 4, 2, 2, 1, 4, 5. Successes: 1' ] end end describe "'5d10m:>=6,1,S.m:==1,-1,F.'" do it 'returns expected results from rolling' do d = GamesDice.create '5d10m:>=6,1,S.m:==1,-1,F.' expect((1..10).map { |_n| d.roll }).to eql [2, 2, 4, 3, 2, 1, 1, 3, 3, 0] end it "includes the string 'S' next to each success, and 'F' next to each 'fumble'" do d = GamesDice.create '5d10m:>=6,1,S.m:==1,-1,F.' expect((1..5).map do |_n| d.roll d.explain_result end).to eql [ '5d10: 2, 3, 4, 7 S, 6 S. Successes: 2', '5d10: 7 S, 4, 2, 6 S, 3. Successes: 2', '5d10: 7 S, 5, 6 S, 7 S, 6 S. Successes: 4', '5d10: 6 S, 5, 10 S, 9 S, 4. Successes: 3', '5d10: 10 S, 9 S, 8 S, 3, 1 F. Successes: 2' ] end end describe "'4d6k:3.r:1,replace,1.'" do it 'represents roll 4 six-sided dice, re-roll any 1s, and keep best 3.' do d = GamesDice.create '4d6k:3.r:1,replace,1.' expect((1..10).map { |_n| d.roll }).to eql [12, 14, 14, 18, 11, 17, 11, 15, 14, 14] end it 'includes re-rolls and keeper choice in explanations' do d = GamesDice.create '4d6k:3.r:1,replace,1.' expect((1..5).map do |_n| d.roll d.explain_result end).to eql [ '4d6: 6, 3, 2, 3. Keep: 3 + 3 + 6 = 12', '4d6: 4, 6, 4, 2. Keep: 4 + 4 + 6 = 14', '4d6: 6, 3, 3, 5. Keep: 3 + 5 + 6 = 14', '4d6: 6, 6, 3, 6. Keep: 6 + 6 + 6 = 18', '4d6: 5, 2, [1|4] 4, 2. Keep: 2 + 4 + 5 = 11' ] end end describe "'2d20k:1,worst.'" do it 'represents roll 2 twenty-sided dice, return lowest of the two results' do d = GamesDice.create '2d20k:1,worst.' expect((1..10).map { |_n| d.roll }).to eql [18, 6, 2, 3, 5, 10, 15, 1, 7, 10] end it 'includes keeper choice in explanations' do d = GamesDice.create '2d20k:1,worst.' expect((1..5).map do |_n| d.roll d.explain_result end).to eql [ '2d20: 18, 19. Keep: 18', '2d20: 20, 6. Keep: 6', '2d20: 20, 2. Keep: 2', '2d20: 3, 11. Keep: 3', '2d20: 5, 7. Keep: 5' ] end end end