Class: Text::Gen::Runner
- Inherits:
-
Object
- Object
- Text::Gen::Runner
- Defined in:
- lib/text/gen/runner.rb
Overview
Runner generates results from the builder that is found with the given key.
Instance Attribute Summary collapse
-
#filters ⇒ Object
readonly
Returns the value of attribute filters.
-
#key ⇒ Object
readonly
Returns the value of attribute key.
-
#lookup ⇒ Object
readonly
Returns the value of attribute lookup.
-
#max_attempts ⇒ Object
readonly
Returns the value of attribute max_attempts.
-
#max_recursion ⇒ Object
readonly
Returns the value of attribute max_recursion.
-
#meta ⇒ Object
readonly
Returns the value of attribute meta.
Instance Method Summary collapse
-
#initialize(key:, lookup:, max_recursion: 10, max_attempts: 10, filters: [], meta: {}) ⇒ Runner
constructor
A new instance of Runner.
- #random_from_dice(text) ⇒ Object
- #random_item(items, dice: nil) ⇒ Object
- #run(count: 1) ⇒ Object
-
#run_builder(context, builder) ⇒ Object
A builder is hash with a key field, items, filters, and meta.
- #run_dice_segment(seg) ⇒ Object
- #run_item(context, item) ⇒ Object
- #run_item_sequence(context, items) ⇒ Object
- #run_items(context, items) ⇒ Object
- #run_number_segment(seg) ⇒ Object
- #run_random_item(context, items) ⇒ Object
- #run_reference_segment(context, seg) ⇒ Object
- #run_segment(context, seg) ⇒ Object
- #run_simple_segment(context, seg) ⇒ Object
- #run_weighted_items(context, items) ⇒ Object
Constructor Details
#initialize(key:, lookup:, max_recursion: 10, max_attempts: 10, filters: [], meta: {}) ⇒ Runner
Returns a new instance of Runner.
12 13 14 15 16 17 18 19 |
# File 'lib/text/gen/runner.rb', line 12 def initialize(key:, lookup:, max_recursion: 10, max_attempts: 10, filters: [], meta: {}) @key = key.to_s.downcase @lookup = lookup @filters = filters @meta = @max_attempts = max_attempts @max_recursion = max_recursion end |
Instance Attribute Details
#filters ⇒ Object (readonly)
Returns the value of attribute filters.
10 11 12 |
# File 'lib/text/gen/runner.rb', line 10 def filters @filters end |
#key ⇒ Object (readonly)
Returns the value of attribute key.
10 11 12 |
# File 'lib/text/gen/runner.rb', line 10 def key @key end |
#lookup ⇒ Object (readonly)
Returns the value of attribute lookup.
10 11 12 |
# File 'lib/text/gen/runner.rb', line 10 def lookup @lookup end |
#max_attempts ⇒ Object (readonly)
Returns the value of attribute max_attempts.
10 11 12 |
# File 'lib/text/gen/runner.rb', line 10 def max_attempts @max_attempts end |
#max_recursion ⇒ Object (readonly)
Returns the value of attribute max_recursion.
10 11 12 |
# File 'lib/text/gen/runner.rb', line 10 def max_recursion @max_recursion end |
#meta ⇒ Object (readonly)
Returns the value of attribute meta.
10 11 12 |
# File 'lib/text/gen/runner.rb', line 10 def @meta end |
Instance Method Details
#random_from_dice(text) ⇒ Object
204 205 206 207 208 209 210 211 212 |
# File 'lib/text/gen/runner.rb', line 204 def random_from_dice(text) rolled = DiceNomShim.roll(text) parsed = JSON.parse(rolled).first["lhs"] # Keep tracks the dice that weren't discarded count = parsed["values"].select { |v| v["keep"] }.count total = parsed["total"] [total, count] end |
#random_item(items, dice: nil) ⇒ Object
123 124 125 126 127 128 129 130 131 |
# File 'lib/text/gen/runner.rb', line 123 def random_item(items, dice: nil) return items.sample if dice.nil? || dice.empty? || dice == "*" total, count = random_from_dice(dice) index = total - count # convert to 0-indexed raise NoItemMatched("roll #{total} exceeds #{items.length}") if index >= items.length items[index] end |
#run(count: 1) ⇒ Object
21 22 23 24 25 26 27 28 29 30 |
# File 'lib/text/gen/runner.rb', line 21 def run(count: 1) store = Store.new(lookup) builder = store.fetch(key) raise NoBuilderMatched, "{builder `#{key}` not found}" unless builder context = Context.new(store:, filters:, meta:, max_recursion:) ResultAccumulator.accumulate(count:, max_attempts:) do run_builder(context, builder) end end |
#run_builder(context, builder) ⇒ Object
A builder is hash with a key field, items, filters, and meta
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/text/gen/runner.rb', line 33 def run_builder(context, builder) context.descend!(builder) checkpoint = context.remember_checkpoint context.with_filters(builder["filters"]) do builder = context.apply_builder_filters(builder) unless builder context.restore_remember(checkpoint) return end # Filter mapping of builder to result is allowed return builder if builder.is_a?(Result) result = builder.is_a?(Result) ? builder : run_items(context, builder["items"]) unless result context.restore_remember(checkpoint) return end result.(builder["meta"]) result = context.apply_result_filters(result) unless result context.restore_remember(checkpoint) return end context.restore_remember(checkpoint) context.remember(result) end ensure context.ascend! end |
#run_dice_segment(seg) ⇒ Object
182 183 184 185 |
# File 'lib/text/gen/runner.rb', line 182 def run_dice_segment(seg) total, = random_from_dice(seg["text"]) Result.new(text: total.to_s, multiplier: total, type: :dice) end |
#run_item(context, item) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/text/gen/runner.rb', line 133 def run_item(context, item) item = context.apply_item_filters(item) return unless item # Filter mapping of item to result is allowed return item if item.is_a?(Result) context.with_filters(item["filters"]) do results = item["segments"].map { |seg| run_segment(context, seg) } return if results.any?(&:nil?) || results.empty? result = Result.merge(results, value: item["value"], multiplier: item["multiplier"], meta: item["meta"], type: context.current_key) context.apply_result_filters(result) end end |
#run_item_sequence(context, items) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/text/gen/runner.rb', line 80 def run_item_sequence(context, items) results = items.map do |item| weight = item["weight"]&.to_i || 100 next unless rand(100) < weight.clamp(1, 100) ResultAccumulator.accumulate(count: 1, max_attempts: @max_attempts) do run_item(context, item) end.first end results = results.compact return if results.empty? Result.merge(results, separator: context.current_modifier, type: :sequence) end |
#run_items(context, items) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/text/gen/runner.rb', line 66 def run_items(context, items) items = context.apply_item_list_filters(items) return if items.empty? case context.current_strategy when "sequence" run_item_sequence(context, items) when "weighted" run_weighted_items(context, items) else run_random_item(context, items) end end |
#run_number_segment(seg) ⇒ Object
187 188 189 190 |
# File 'lib/text/gen/runner.rb', line 187 def run_number_segment(seg) num = seg["text"].to_i Result.new(text: num.to_s, multiplier: num, type: :number) end |
#run_random_item(context, items) ⇒ Object
116 117 118 119 120 121 |
# File 'lib/text/gen/runner.rb', line 116 def run_random_item(context, items) item = random_item(items, dice: context.current_modifier) return unless item run_item(context, item) end |
#run_reference_segment(context, seg) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/text/gen/runner.rb', line 192 def run_reference_segment(context, seg) key = seg["text"] builder = context.store.fetch(key) return unless builder # Offset by one to ensure the segment filters are applied to the builder context.with_filters(seg["filters"], offset: 1) do run_builder(context, builder) end end |
#run_segment(context, seg) ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/text/gen/runner.rb', line 153 def run_segment(context, seg) # Note, a filter may expand a segment into multiple segments segments = context.apply_segment_filters(seg) return if segments.nil? # Filter mapping of segment to result is allowed return segments if segments.is_a?(Result) results = segments.map { |seg| run_simple_segment(context, seg) }.compact return if results.empty? return results[0] if results.length == 1 Result.merge(results, type: "segment expansion") end |
#run_simple_segment(context, seg) ⇒ Object
169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/text/gen/runner.rb', line 169 def run_simple_segment(context, seg) case seg["type"] when "dice" run_dice_segment(seg) when "number" run_number_segment(seg) when "reference" run_reference_segment(context, seg) else Result.new(text: seg["text"], value: seg["value"], type: :constant) end end |
#run_weighted_items(context, items) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/text/gen/runner.rb', line 96 def run_weighted_items(context, items) total_weight = items.sum { |item| [item.fetch("weight", 1).to_i, 1].max } dice = context.current_modifier rand_weight = if dice.nil? || dice.empty? || dice == "*" rand(total_weight) else total, count = random_from_dice(dice) total - count # convert to 0-indexed end return if rand_weight > total_weight current_weight = 0 item = items.find do |item| current_weight += [item.fetch("weight", 1).to_i, 1].max current_weight > rand_weight end run_item(context, item) end |