class BCDice::CommonCommand::Choice

チョイスコマンド

列挙された項目の中から一つを選んで出力する。

フォーマットは以下の通り choice choice(A,B,C,D) choice A B C D choice(新クトゥルフ神話TRPG, ソード・ワールド2.5, Dungeons & Dragons)

“choice”の次の文字によって区切り文字が変化する

"[" -> "," で区切る
"(" -> "," で区切る
" " -> /\s+/ にマッチする文字列で区切る

各項目の前後に空白文字があった場合は除去される

choice[A, B,  C , D   ] は choice[A,B,C,D] と等価

項目が空文字列である場合、その項目は無視する

choice[A,,C] は choice[A,C] と等価

フォーマットを選ぶことで、項目の文字列に()や,を含めることができる

choice A,B X,Y -> "A,B" と "X,Y" から選ぶ
choice(A[], B[], C[]) -> "A[]", "B[]", "C[]" から選ぶ
choice[A(), B(), C()] -> "A()", "B()", "C()" から選ぶ

“choise”の後に数を指定することで、列挙した要素から重複なしで複数個を選ぶ

choice2[A,B,C] -> "A", "B", "C" から重複なしで2個選ぶ

指定したい要素が「AからD」のように連続する項目の場合に「A-D」と省略して記述できる 略記の展開はアルファベット1文字もしくは数字の範囲に限り、略記1つを指定したときのみ展開される

choice[A-D] -> choice[A,B,C,D] と等価
choice[b-g] -> choice[b,c,d,e,f,g] と等価
choice[3-7] -> choice[3,4,5,6,7] と等価
choice[A-D,Z] -> 展開されない。 "A-D", "Z" から選ぶ
choice[D-A] -> 展開されない。

Constants

DELIMITER
DELIMITER_CHAR
PREFIX_PATTERN
SUFFIX
TERMINATION

Public Class Methods

eval(command, _game_system, randomizer) click to toggle source

@param command [String] @param _game_system [BCDice::Base] @param randomizer [Randomizer] @return [Result, nil]

# File lib/bcdice/common_command/choice.rb, line 81
def eval(command, _game_system, randomizer)
  cmd = parse(command)
  cmd&.roll(randomizer)
end
new(secret:, block_delimiter:, takes:, items:) click to toggle source

@param secret [Boolean] @param block_delimiter [BlockDelimiter::BRACKET, BlockDelimiter::PAREN, BlockDelimiter::SPACE] @param takes [Integer] 何個チョイスするか @param items [Array<String>]

# File lib/bcdice/common_command/choice.rb, line 185
def initialize(secret:, block_delimiter:, takes:, items:)
  @secret = secret
  @block_delimiter = block_delimiter
  @takes = takes
  @items = items
end

Private Class Methods

parse(command) click to toggle source
# File lib/bcdice/common_command/choice.rb, line 88
def parse(command)
  scanner = StringScanner.new(command)
  scanner.skip(/\s+/)

  secret = !scanner.scan(/S/i).nil?
  unless scanner.scan(/choice/i)
    return nil
  end

  takes = scanner.scan(/\d+/)&.to_i || 1
  if takes == 0
    return nil
  end

  type =
    case scanner.scan(/\(|\[|\s+/)
    when "["
      BlockDelimiter::BRACKET
    when "("
      BlockDelimiter::PAREN
    when String
      BlockDelimiter::SPACE
    else
      return nil
    end

  delimiter = DELIMITER[type]
  termination = TERMINATION[type]

  items = []
  while (item = scanner.scan_until(delimiter))
    items.push(item.delete_suffix(","))
  end

  last_item = scanner.scan_until(termination)
  unless last_item
    return nil
  end

  items.push(last_item.delete_suffix(SUFFIX[type]))

  items = items.map(&:strip).reject(&:empty?)
  if items.size == 1
    items = parse_multi_item_shorthand(items.first)
  end

  if items.empty? || items.size < takes
    return nil
  end

  new(
    secret: secret,
    block_delimiter: type,
    takes: takes,
    items: items
  )
end
parse_multi_chars_shorthand(str) click to toggle source
# File lib/bcdice/common_command/choice.rb, line 165
def parse_multi_chars_shorthand(str)
  m = /^([a-z])-([a-z])$/.match(str) || /^([A-Z])-([A-Z])$/.match(str)
  unless m
    return nil
  end

  first = m[1]
  last = m[2]
  if first > last
    return nil
  end

  return first.upto(last).to_a
end
parse_multi_item_shorthand(str) click to toggle source
# File lib/bcdice/common_command/choice.rb, line 146
def parse_multi_item_shorthand(str)
  parse_multi_nums_shorthand(str) || parse_multi_chars_shorthand(str) || []
end
parse_multi_nums_shorthand(str) click to toggle source
# File lib/bcdice/common_command/choice.rb, line 150
def parse_multi_nums_shorthand(str)
  m = /^(\d+)-(\d+)$/.match(str)
  unless m
    return nil
  end

  first = m[1].to_i
  last = m[2].to_i
  if first > last
    return nil
  end

  return first.upto(last).to_a
end

Public Instance Methods

expr() click to toggle source
# File lib/bcdice/common_command/choice.rb, line 210
def expr
  takes = @takes == 1 ? nil : @takes

  case @block_delimiter
  when BlockDelimiter::SPACE
    "choice#{takes} #{@items.join(' ')}"
  when BlockDelimiter::BRACKET
    "choice#{takes}[#{@items.join(',')}]"
  when BlockDelimiter::PAREN
    "choice#{takes}(#{@items.join(',')})"
  end
end
roll(randomizer) click to toggle source

@param randomizer [Randomizer] @return [Result]

# File lib/bcdice/common_command/choice.rb, line 194
def roll(randomizer)
  items = @items.dup
  chosens = []
  @takes.times do
    index = randomizer.roll_index(items.size)
    chosens << items.delete_at(index)
  end

  Result.new.tap do |r|
    chosen = chosens.join(DELIMITER_CHAR[@block_delimiter])

    r.secret = @secret
    r.text = "(#{expr()}) > #{chosen}"
  end
end