tabletop-companion/app/services/dice_roller.rb

74 lines
1.7 KiB
Ruby

# frozen_string_literal: true
require "dice"
class DiceRoller
attr_reader :dice
attr_reader :result
def initialize(roll_command, stat: nil)
@roll_command = roll_command
@stat = stat&.value
@dice = []
end
def roll
roll_command = parse_roll_command_references(roll_command)
Dice.roll(roll_command)
end
def valid?
return if @roll_command.blank?
# No repeated math operators
return false if @roll_command.match?(/[+\-*\/]{2,}/)
# No leading or trailing math operators
return false if @roll_command.match?(/\A[+\-*\/]/) || @roll_command.match?(/[+\-*\/]\z/)
@roll_command.match?(
/
\A(
(\d*d\d*) |
([+\-*\/]) |
(\d+) |
(self)
)*\z/xi,
)
end
private
def roll_delimiters
[ "+", "-", "*", "/" ]
end
def roll_dice(command)
parts = command.downcase.split("d").compact_blank
die_type = parts.last
dice_number = parts.length > 1 ? parts.first.to_i : 1
result = 0
dice_number.times do
roll = rand(1..die_type.to_i)
result += roll
dice << [ die_type, result ]
end
result
end
def parse_roll_command_references(roll_command)
roll_command = @roll_command.include?("self") ? @roll_command.gsub("self", @stat.to_s) : @roll_command
roll_command.split(Regexp.union(roll_delimiters)).each do |part|
next if part.match?(/\A\d*\z/)
next if part.match?(/\A\d*d\d*\z/)
value = CharacterSheetFeature.find_by(slug: part).featurable.value
raise "#{part} not found" if value.blank?
roll_command.gsub!(part, value.to_s)
end
roll_command
end
end