rpg_lib 1.0.0 → 1.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +4 -0
- data/Rakefile +4 -0
- data/grammar/dice_expression.treetop +95 -0
- data/lib/rpg_lib.rb +7 -1
- data/lib/rpg_lib/api.rb +25 -1
- data/lib/rpg_lib/dice_roller.rb +9 -41
- data/lib/rpg_lib/die_roller.rb +14 -0
- data/lib/rpg_lib/parser/dice_expression_nodes.rb +16 -0
- data/lib/rpg_lib/parser/dice_expression_parser.rb +677 -0
- data/lib/rpg_lib/parser/dice_parser.rb +29 -0
- data/lib/rpg_lib/version.rb +1 -1
- data/rpg_lib.gemspec +2 -0
- data/spec/rpg_lib/api_spec.rb +67 -0
- data/spec/rpg_lib/dice_roller_spec.rb +82 -0
- data/spec/rpg_lib/die_roller_spec.rb +28 -0
- data/spec/rpg_lib/parser/dice_expression_nodes_spec.rb +13 -0
- data/spec/rpg_lib/parser/dice_parser_spec.rb +51 -0
- metadata +25 -2
- data/lib/rpg_lib/roll_descriptor.rb +0 -54
@@ -0,0 +1,29 @@
|
|
1
|
+
module RpgLib
|
2
|
+
module Parser
|
3
|
+
##
|
4
|
+
# DiceParser
|
5
|
+
#
|
6
|
+
class DiceParser
|
7
|
+
attr_reader :parser
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@parser = DiceExpressionParser.new
|
11
|
+
@cache = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse(dice)
|
15
|
+
return @cache[dice] if @cache.key?(dice)
|
16
|
+
parse_new_dice(dice)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse_new_dice(dice)
|
22
|
+
tree = @parser.parse(dice)
|
23
|
+
raise Exception, "Parse error #{@parser.failure_reason} at offset: #{@parser.index}" if tree.nil?
|
24
|
+
@cache[dice] = tree
|
25
|
+
tree
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rpg_lib/version.rb
CHANGED
data/rpg_lib.gemspec
CHANGED
@@ -35,6 +35,8 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.files = `git ls-files -z`.split("\x0")
|
36
36
|
spec.require_paths = ['lib']
|
37
37
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
38
|
+
|
39
|
+
spec.add_dependency 'treetop', '~> 1.6'
|
38
40
|
spec.add_development_dependency 'bundler', '~> 1.14'
|
39
41
|
spec.add_development_dependency 'rake', '~> 12.0'
|
40
42
|
spec.add_development_dependency 'rspec', '~> 3.5'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RpgLib
|
4
|
+
describe Api do
|
5
|
+
let(:api) { Class.new { include Api }.new }
|
6
|
+
|
7
|
+
describe 'roll' do
|
8
|
+
it 'returns Integer' do
|
9
|
+
expect(api.roll('3d6')).to be_instance_of(Integer)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'responds in the specified range' do
|
13
|
+
20.times do
|
14
|
+
roll = api.roll('3d6')
|
15
|
+
expect(roll < 3).to be false
|
16
|
+
expect(roll > 18).to be false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'roll_and_ignore' do
|
22
|
+
it 'returns Integer' do
|
23
|
+
expect(api.roll_and_ignore('3d6', 5)).to be_instance_of(Integer)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'response in the specified range' do
|
27
|
+
20.times do
|
28
|
+
roll = api.roll_and_ignore('3d6', 5)
|
29
|
+
expect(roll < 3).to be false
|
30
|
+
expect(roll > 18).to be false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'ignores a passed number' do
|
35
|
+
20.times do
|
36
|
+
roll = api.roll_and_ignore('3d6', 5)
|
37
|
+
expect(roll).not_to eq(5)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'ignores a passed range' do
|
42
|
+
20.times do
|
43
|
+
roll = api.roll_and_ignore('3d6', 6..10)
|
44
|
+
expect(6..10).not_to include(roll)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'ignores a passed roll set' do
|
49
|
+
evens = RollSet.new(4, 6, 8, 10, 12, 14, 16, 18)
|
50
|
+
20.times do
|
51
|
+
roll = api.roll_and_ignore('3d6', evens)
|
52
|
+
expect(roll).to be_odd
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'choose' do
|
58
|
+
let(:values) { [:foo, :bar, :baz, :stoo] }
|
59
|
+
|
60
|
+
it 'selects a single item from the list' do
|
61
|
+
10.times do
|
62
|
+
expect(values).to include(api.choose(values))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RpgLib
|
4
|
+
describe DiceRoller do
|
5
|
+
let(:roller) { DiceRoller.clone.instance }
|
6
|
+
let(:die_roller) { roller.roller }
|
7
|
+
|
8
|
+
it 'can be accessed' do
|
9
|
+
expect { DiceRoller.instance }.not_to raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'roll_die' do
|
13
|
+
before(:each) do
|
14
|
+
allow(die_roller).to receive(:roll) { 3 }
|
15
|
+
@value = roller.roll_die(6)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'defers to the roller' do
|
19
|
+
expect(die_roller).to have_received(:roll).with(6)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'relays the result' do
|
23
|
+
expect(@value).to eq(3)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'roll' do
|
28
|
+
let(:parser) { roller.parser }
|
29
|
+
let(:tree) { double('tree') }
|
30
|
+
|
31
|
+
before(:each) do
|
32
|
+
allow(parser).to receive(:parse) { tree }
|
33
|
+
allow(tree).to receive(:eval) { 9 }
|
34
|
+
@value = roller.roll('3d6')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'defers to the parser' do
|
38
|
+
expect(parser).to have_received(:parse).with('3d6')
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'evals the result from the parser' do
|
42
|
+
expect(tree).to have_received(:eval).with(die_roller)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'relays the result of eval' do
|
46
|
+
expect(@value).to eq(9)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'roll_and_ignore' do
|
51
|
+
describe 'with no collision' do
|
52
|
+
before(:each) do
|
53
|
+
allow(roller).to receive(:roll) { 4 }
|
54
|
+
@value = roller.roll_and_ignore('d6', RollSet.new(0, 9, 100))
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'rolls only once' do
|
58
|
+
expect(roller).to have_received(:roll).with('d6').once
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'relays the roll' do
|
62
|
+
expect(@value).to eq(4)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'with collisions' do
|
67
|
+
before(:each) do
|
68
|
+
allow(roller).to receive(:roll).and_return(1, 2, 3, 4)
|
69
|
+
@value = roller.roll_and_ignore('d6', RollSet.new(1..3))
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'rolls multiple times until it finds a value that is not ignored' do
|
73
|
+
expect(roller).to have_received(:roll).exactly(4).times
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'relays the roll' do
|
77
|
+
expect(@value).to eq(4)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RpgLib
|
4
|
+
describe DieRoller do
|
5
|
+
it 'can be created' do
|
6
|
+
expect { DieRoller.new }.not_to raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'roll' do
|
10
|
+
let(:generator) { double('generator') }
|
11
|
+
let(:value) { 99 }
|
12
|
+
let(:roller) { DieRoller.new(generator) }
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(generator).to receive(:rand) { value }
|
16
|
+
@result = roller.roll(10)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'defers to the generator' do
|
20
|
+
expect(generator).to have_received(:rand).with(1..10)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'relays the generated value' do
|
24
|
+
expect(@result).to eq(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RpgLib
|
4
|
+
module Parser
|
5
|
+
module DiceExpression
|
6
|
+
describe BinaryOperation do
|
7
|
+
it 'is touched when we parse a dice descriptor with an operator' do
|
8
|
+
expect(RpgLib::Parser::DiceParser.new.parse('3+4').eval(nil)).to eq(7)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RpgLib
|
4
|
+
module Parser
|
5
|
+
describe DiceParser do
|
6
|
+
it 'can be created' do
|
7
|
+
expect { DiceParser.new }.not_to raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'parse' do
|
11
|
+
let(:parser) { DiceParser.new }
|
12
|
+
let(:treetop) { parser.parser }
|
13
|
+
let(:tree) { double('tree') }
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
allow(treetop).to receive(:parse) { tree }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'uncached' do
|
20
|
+
before(:each) do
|
21
|
+
@value = parser.parse('3d6')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'defers to the treetop parser' do
|
25
|
+
expect(treetop).to have_received(:parse).with('3d6')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'relays the parsed tree' do
|
29
|
+
expect(@value).to eq(tree)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'cached' do
|
34
|
+
before(:each) do
|
35
|
+
parser.parse('3d6')
|
36
|
+
parser.parse('3d6')
|
37
|
+
@value = parser.parse('3d6')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'uses the cached value' do
|
41
|
+
expect(treetop).to have_received(:parse).once
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'relays the cached value' do
|
45
|
+
expect(@value).to eq(tree)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rpg_lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Hale
|
@@ -10,6 +10,20 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2015-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: treetop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,14 +106,23 @@ files:
|
|
92
106
|
- Gemfile
|
93
107
|
- Gemfile.lock
|
94
108
|
- Rakefile
|
109
|
+
- grammar/dice_expression.treetop
|
95
110
|
- lib/rpg_lib.rb
|
96
111
|
- lib/rpg_lib/api.rb
|
97
112
|
- lib/rpg_lib/dice_roller.rb
|
98
|
-
- lib/rpg_lib/
|
113
|
+
- lib/rpg_lib/die_roller.rb
|
114
|
+
- lib/rpg_lib/parser/dice_expression_nodes.rb
|
115
|
+
- lib/rpg_lib/parser/dice_expression_parser.rb
|
116
|
+
- lib/rpg_lib/parser/dice_parser.rb
|
99
117
|
- lib/rpg_lib/roll_set.rb
|
100
118
|
- lib/rpg_lib/string.rb
|
101
119
|
- lib/rpg_lib/version.rb
|
102
120
|
- rpg_lib.gemspec
|
121
|
+
- spec/rpg_lib/api_spec.rb
|
122
|
+
- spec/rpg_lib/dice_roller_spec.rb
|
123
|
+
- spec/rpg_lib/die_roller_spec.rb
|
124
|
+
- spec/rpg_lib/parser/dice_expression_nodes_spec.rb
|
125
|
+
- spec/rpg_lib/parser/dice_parser_spec.rb
|
103
126
|
- spec/rpg_lib/roll_set_spec.rb
|
104
127
|
- spec/rpg_lib/string_spec.rb
|
105
128
|
- spec/rpg_lib_spec.rb
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# Copyright 2017 Jamie Hale
|
2
|
-
#
|
3
|
-
# This file is part of the RpgLib gem.
|
4
|
-
#
|
5
|
-
# RpgLib is free software: you can redistribute it and/or modify
|
6
|
-
# it under the terms of the GNU General Public License as published by
|
7
|
-
# the Free Software Foundation, either version 3 of the License, or
|
8
|
-
# (at your option) any later version.
|
9
|
-
#
|
10
|
-
# RpgLib is distributed in the hope that it will be useful,
|
11
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
-
# GNU General Public License for more details.
|
14
|
-
#
|
15
|
-
# You should have received a copy of the GNU General Public License
|
16
|
-
# along with RpgLib. If not, see <http://www.gnu.org/licenses/>.
|
17
|
-
|
18
|
-
module RpgLib
|
19
|
-
##
|
20
|
-
# RollDescriptor
|
21
|
-
#
|
22
|
-
class RollDescriptor
|
23
|
-
attr_reader :die, :count, :drop_lowest, :drop_highest
|
24
|
-
|
25
|
-
def initialize(match)
|
26
|
-
initialize_die(match)
|
27
|
-
initialize_count(match)
|
28
|
-
initialize_dropped_dice(match)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def initialize_die(match)
|
34
|
-
@die = match[2].to_i
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize_count(match)
|
38
|
-
@count = 1
|
39
|
-
@count = match[1].to_i unless match[1].empty?
|
40
|
-
end
|
41
|
-
|
42
|
-
def initialize_dropped_dice(match)
|
43
|
-
@drop_lowest = 0
|
44
|
-
@drop_highest = 0
|
45
|
-
if match[4] == 'dl'
|
46
|
-
@drop_lowest = 1
|
47
|
-
@drop_lowest = match[5].to_i unless match[5].empty?
|
48
|
-
elsif match[6] == 'dh'
|
49
|
-
@drop_highest = 1
|
50
|
-
@drop_highest = match[7].to_i unless match[7].empty?
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|