赤城の備忘録。

どうでもいいこと

ヒュムノス・第二紀成語

 

基本的な文法定義

  1. 想音。Em と表す。
  2. 主語。S と表す。
  3. 動詞。V と表す。
  4. 名詞。N と表す。
  5. 形容詞。Adj と表す。
  6. 副詞。Adv. と表す。

単語律

それぞれの単語には、「律」という属性が定まっている。
律とは、その単語がどこに由来するものか示すものである。ここでは由来については説明しない。

律は以下の6種類がある。

  1. 中央正純律(共通語)
  2. クルトシエール律(Ⅰ紀前古代語)
  3. 古メタファルス律(Ⅰ紀神聖語)
  4. クラスタ律(クラスタ地方語)
  5. アルファ律(オリジンスペル:XXX属)
  6. 新約パスタリエ(パスタリア成語)

第一紀成語では、1-3と4-6は詩サーバなどによっては使用の可否が別れたが、第二紀成語では全て共存可能である。

基本的な文

Em1 - Em2 - Em3 文...

Em1 は第一想音。感情の程度を表す。

Em2 は第二想音。具体的な感情を表す。

Em3 は第三想音。将来的にどうなりたいかを表す。

想音

第二紀成語(ヒュムノス)において、感情を表すものである。

文の先頭につける想音(第一~第三想音)、単語につける想音があるがここでは前者について示す。

第一想音
 想音   単語律   意味 
 Rrha 中央正純律 トランス状態
 Was 中央正純律 強烈に
 Wee 中央正純律 とても強く
 Fei 中央正純律 力を込めて
 Ma 中央正純律 平常心
 Fou 中央正純律 少し
 Nil 中央正純律 弱く
 Nn 中央正純律 無 
第二想音
 想音  単語律  意味
 apea   クルトシエール律  幸せでぽわー
 granme  中央正純律  勇気・守護の気持ち
 guwo  中央正純律  怒り
 i  中央正純律  焦り
 jyel  中央正純律  寂しさ
 ki  中央正純律  真面目・本気
 num  中央正純律  無
 nyasri  クルトシエール律   哀しみ
 paks  中央正純律  ココロどきどき
 quel  中央正純律  必死に
 touwaka  中央正純律  希望を持って
 waa  古メタファルス律  嬉しい
 wol  中央正純律  熱血
 yant  中央正純律  混乱
 yea  中央正純律  嬉しい
 zweie  中央正純律  真摯に
 zaag  中央正純律   傲慢・尊大な
 tajac  クルトシエール律   人生を賭けてでも
 jaq  クルトシエール律   無知・とんちんかん
 gzal  中央正純律  絶望
 stia  古メタファルス律  冷静に
 axin  中央正純律  どうでもいい・ふざけて
 au  アルファ律  悲しい
第三想音
想音  単語律  意味
 ra  中央正純律  このままでいたい
 erra  中央正純律  永遠にこのままでいたい
 ga  中央正純律  この状態から脱したい
 gaya  中央正純律  二度とこうなりたくない
 gagis  古メタファルス律  自分はどうなってもいい
 wa  中央正純律  この状態を受け入れる

diceR 技術的な解説

github.com

diceRの解説です。 正しくない記述も含まれるかもしれません。

前書き

「どどんとふ」のダイスボットに関するアプリケーション、BCDiceは高機能です。
3d61d100<=50といった単純なものから、CoCなどといったTRPGシステムに特化したものまで使いやすいように網羅されています。

BCDiceは確かに単純なダイス(e.g. 1d100)から、複雑な処理をするダイスまで網羅しています。
しかし、その内「単純なダイス」に対しては、私はBCDiceでは機能過剰だと感じました。

そのため、私は「単純なダイス」、つまりBCDiceを簡略化したdiceRを開発しました。
diceRはSinatra上で動き、APIとして機能します。 これは外部からの参照を意図したものです。

また、diceRを開発するにあたり、NKMR6194氏のbcdice-apiを参考にさせていただきました。
勝手ながら、この場を借りて感謝申し上げます。

ダイス表現

1d1003d6のようなダイスを、私は引っくるめて「ダイス表現(Dice Expression)」と名付けました*1
diceRのダイスロールは、すべてこのダイス表現を元に行います。

diceR上では、このダイス表現を分解し、以下のようにします。

  • 振る回数*2 - 1d1001
  • *3 - 1d100100

また、ダイス表現自体も「ダイス表現」とします。

更に、1d100<=30のように、条件を指定したい場合があります。
その際は、更に以下のように分解します。

  • 条件*4 - 1d100<=30<=
  • 条件のパラメータ(数字)*5 - 1d100<=3030

説明が下手ですみません。まとめるとこうなります。
1d100<=30 なら:

  • ― ダイスを振る回数は1回。
  • ― ダイスの面は100面。
  • ― ダイスの条件は<=
  • ― ダイスの条件のパラメータは30

文章に起こすと:100面ダイスを1回振る。条件は、結果が30以下なら成功、さもなくば失敗。

diceRの性質上(= 1d100などしか扱わない)、ダイス表現という考えは重要です。
これはダイスを一単語の、抽象的な言葉で表せることを意味します。

diceRの実際の処理

diceRにおいての、具体的処理に入ります。
Sinatraのルーティング*6API バージョン1のコード*7の説明は割愛します。

ダイス表現処理

難しい処理は行っていません。上の文章(「100面ダイスを1回振る。条件は、結果が30以下なら成功、さもなくば失敗。」)をソースコードに起こしているだけです。
具体的なソースコードは以下のとおりです。

def roll_dice
  if @dice_number.zero? || @dice_faces.zero?
    init_val

    @error   = true
    @message = 'Specified expression has illegal characters.'

    return
  end

  @result = Array.new(@dice_number) { { faces: @dice_faces, value: [*1..@dice_faces].sample } }

  @result_val = @result.map { |element| element[:value] }
  @result_sum = @result_val.inject(:+)

  @result_formatted = format_result

  @message = 'OK'
  @error   = false
end

大体の処理は不正なダイス表現に対してのものと、APIとしての出力のための処理のものです。
純粋に「ダイスを振る」というソースコードは以下になります。

@result = Array.new(@dice_number) { { faces: @dice_faces, value: [*1..@dice_faces].sample } }

…短いですね。最初のコードが冗長的すぎるだけ

更に、見やすく改行などを加えるとこうなります。

# @result      はダイスを振った結果の配列。
# @dice_number はダイスを振る回数。
# @dice_faces  はダイスの面。
@result = Array.new(@dice_number) { { 
   faces: @dice_faces,
   value: [*1..@dice_faces].sample
} }

究極的に単純化すると、Array#new(size) {|idx| block}という処理になります。
newの引数分、ブロック内の処理を実行しろ、ということです。

ブロック内では、ダイスを振った結果を一つ一つハッシュとし、それを配列をとして一つにまとめています。

ダイス表現の分解

def analyze_dice(r)
  dice_args = r.split(/d|(<=|<=)|(>=|>=)|(<|<)|(>|>)/)

  @dice_expr = r
  @dice_number = dice_args[0].to_i
  @dice_faces  = dice_args[1].to_i

  @cond_val = dice_args[2]
  @cond_dig = dice_args[3].to_i
end

String#splitでダイス表現を配列に分解します。正規表現を使っています。
そして配列内の要素を、各変数に代入しています*8

各変数の説明は、変数名を見ればわかると思うので省略します。

条件指定時の、成否のチェック

def check_success
  return '成功' if @cond_val == '<=' && @result_sum <= @cond_dig
  return '成功' if @cond_val == '>=' && @result_sum >= @cond_dig
  return '成功' if @cond_val == '<'  && @result_sum < @cond_dig
  return '成功' if @cond_val == '>'  && @result_sum > @cond_dig

  '失敗'
end

…ややこしいですね。いかにもウンコードです。実際Rubocopでcyclomatic complexity*9が高いと言われてます。

それぞれの文は、比較する文字列が違うだけなので一つだけ見れば問題ありません。 @cond_val = (<=|<|>=|>) AND @result_sum(ダイスを振った結果の総計) <=|<|>=|> @cond_dig なら成功、さもなくば失敗を返します。

ダイスを振った結果のフォーマット

def format_result
  # '(%s) > %s[%s] > %d > %d' % [@dice_expr, @result_sum, @result_val, @result_sum, check_success]
  if @cond_val
    return format('(%s) > %s[%s] > %d > %s',
                  @dice_expr,
                  @result_sum,
                  @result_val.join(', '),
                  @result_sum,
                  check_success)
  end

  format('(%s) > %s[%s] > %d',
         @dice_expr, @result_sum, @result_val.join(', '), @result_sum)
end

一つ上の成否チェックと同じような理由(こちらは@cond_valがTRUE → 条件が指定されているか, 指令されていたら成否を付け足す *10で、片方のformatを見れば問題ないです。

結果は、(3d6<=10) > 6[2, 2, 2] > 6 > 成功といった具合になります。
formatでそれぞれの変数を、一つの文字列にまとめています。

その他

attr_reader :message, :error, :result, :result_formatted

def initialize(r)
  init_val
  analyze_dice(r)
end

def init_val
  # dice
  @dice_expr   = ''
  @dice_number = 0
  @dice_faces  = 0

  # Condition
  @cond_val = ''
  @cond_dig = 0

  # roll
  @result        = []
  @result_val    = []
  @result_sum    = 0

  # format
  @result_formatted = ''

  # meta
  @error   = false
  @message = ''
end

initialize(r)はコンストラクタ。init_valはエラー発生時に、変数類をリセットするための関数。
DiceV2.new("1d100")でほとんどの工程を処理します。

以下のようにすることで、一通りの操作が可能です。

r = '1d100<=30'
dice = DiceV2.new(r)

dice.message # エラーメッセージ
dice.error   # エラーの有無
dice.result  # ダイスの結果の配列
dice.result_formatted # ダイスの結果のフォーマット版

参考: API出力

{
    "message": "OK",
    "error": false,
    "dices": [
        {"faces": 6, "value": 2 },
        {"faces": 6, "value": 2},
        {"faces": 6, "value": 2}
    ],
    "result": "(3d6<=10) > 6[2, 2, 2] > 6 > 成功"
}

終わりに

私は、プログラマだの自称している割には、大規模なアプリケーションや、人の役に立つようなプロジェクトに参加したことはありません。
しかし、きりだるま氏のOnset!の開発に一部参加させてもらい、下向きだった意識が多少変わったと思います。その成れの果て(!)がこのdiceRです。

また、こうやってちゃんとした、技術的な解説をしてみるのもはじめてかもしれません。
Qiitaでは色々書いていましたが、拙いものです。

diceRはSinatra上で動きます。OSSを活用しています。
無論、diceRもOSSです。MITライセンス上でGitHubに公開しています。

diceRの開発はちょっぴり、少しずつですが、今後も進めていきます。
この記事が、将来diceRの開発に加わってくれる方の助けに、または色々プログラミングしてみたいなー、という方の助けになれば幸いです。

謝礼

NKMR6194氏のbcdice-apiを参考にさせていただきました。前書きでも延べましたが、再度感謝申し上げます。

参考: 利用した技術

*1:ちゃんとした名前、あるのでしょうか?

*2:number, diceR変数名 @dice_number

*3:faces, @dice_faces

*4:condition, @cond_val

*5:condition digit, @cond_dig

*6:server.rb

*7:/lib/dice_v1.rb

*8:一つのオブジェクトにまとめればいいと思うので、近いうち書き換えます。

*9:循環的複雑度

*10:「成否を付け足す」とここに書いて、処理を簡潔化出来るじゃん! と思いつきました。DRYみたいですね。近いうち書き換えます。

METHOD_HYMME_ADVANCE/.

yUzt vEtA sev wIseh/.
与えられたときの中で輝いていたい。

mUam sev ttu jAzt Earhou/.
私の夢をかなえるために、今という時は在る。
naave wErUl yeal wAf v.s.k/.
だから私は、自ら選んだ道を歩んでいきたい。
xN rre sev zz ayulsa ag jEzIt iem/.
この時は永遠ではないから、今できることをやってみよう。

xE rre revm iwl yEzEteh/.
夢はきっと叶うから。

METHOD_PAJA/.#Akagi extacting.

hLYOmLYOmLYOrLYO l.n.c.a. naflansee dn vega keg/.
(この手で人々の繋がりを破壊する いざ戦え)

xN rre sachel wLYNnLYEseh zz ouvyu ut mLYNtEyIye ag p.s.w.t.
(その都市はかつて再び荒廃した廃墟から立ち直った)

xN rre qejyu vLYAaLYA/.
(ある日、一人の人間が生まれた)
xN rre qejyu wLYErOleh sarr ut ceku/.
(家族から、仲間から、同胞さえも「人間」に期待を寄せていた)

xN rre qejyu_20 qym, kLYNtn hegta/.
(「人間」は20歳になった 時も過ぎた)

xN rre qejyu rLYUfLYOrOmU gral Adaedu_LYAsphaela/.
(「人間」は世界の醜きすべての真実を知った)
xN rre qejyu zNzNxN/.
(絶望した)
xN rre qejyu nLYUmLYUlLYU/.
(悪意に染まる)

xN rre qejyu hLYOmLYOmLYOrLYO ag wLYOwLYOjLYUnOcO eje/.
(それぞれの「人間」に背を向け謳う 光のない心と共鳴する 怒りと悲しみの下に)
xN rre qejyu wLYNsLYNeh zess balduo/.
(「人間」は暗黒のような期待を背負って謳う)

EXEC_PAJA/.#Akagi extracting.

Was ki ra paul dius iem en manafeeze.
(尊い今を感じて、そして生きよう)

Fou ki ra crushue hymmne eazas.
(互いに波動を紡ごう)
Ma i ra parge ides rol getrra.
(醜い過去は切り捨てよう)

Was ki erra zaffur lamenza yeeel.
(遥か彼方の嘆きに耐えよう)

Ma ki ra zahha uteu syec der flip chyet diasee pauwel.
(選ばれた神の子の力を引き換えにして深い微睡みの奥へ突き進む)

Ma apea gagis wis salavec, wis nha manac der papana quivale.
(我は生け贄にして、冬の雨よりその真の名を召喚するものなり)
Ma apea gagis yehar shellan, gyen maya enerel.
(揺り籠より生まれ、永遠の魔法を紡ぐ)

Ma apea gagis noce wis enclone der foul ammue.
(自然は不思議な波動で包み込まれている)
Ma apea gagis flip pauwel, gyen maya.
(我は力を引き換えにして、魔法を紡ぐ)

Ma apea gagis pat uteu, en deata omnis der alroetsue crudea.
(微睡みから詩を開放し、償うべき苦しみをもってすべてを断罪する)
Ma apea gagis xest pauwel revatail, gyen HYMME_PAJA.
(レーヴァテイルの力を変換し、HYMME_PAJAを紡ぐ)

EXEC_FLIP_ENEETOR/.

「花は咲く」ヒュムノス訳。

Ma ki wa rre frawrle fhyu ferda ciel der vinan papana.
春風は雪で覆われた大地の上を舞う。
Ma ki ra ene idesy memora oz garlden.
私はあの街での古い記憶を想う。

Ma ki wa irs revm sos exec, irs mea chsee.
叶えたい夢があった、変わりたい自分もいた。
Ma num ga iem la ene whou irs innna mea.
今はただ、私の心の中に居るあの人を思い出す。

Fou paks ra kiafa whou hymme.
誰かの詩が聴こえる。
Fou paks wa sorr rasse whou.
それは誰かを励ましている。

Ma zweie ga vit fernia oz whou.
誰かの笑顔が垣間見える。
Ma ki ra hierle eetor.
哀しみの向こう側に。

Was ki wa rre frawr exec, grruw, chsee, gran.
そして花は咲き、花びらを大きく開き、天の恵みを受け、再び育つ。
Was ki ra sos manaf whou manafaln.
いつか生まれる命のために。

Was ki wa rre frawr exec, grruw, chsee, gran.
花は咲き、花びらを大きく開き、天の恵みを受け、再び育つ。
Was touwaka gaya ene teyys.
私は何を残しただろうか。

METHOD_DEL/.

(「アンインストール」ヒュムノス訳)

 

xA rre Amoy_glansee wInsYA eetor/.
向こうから偉大な時が訪れたのは、

xA merra aIuNk li Ityui ag xA yorr kYEfYEkUkI/.
私たちは小さき者だと笑いに来た。

jOljO cest ag wYAwNjIncA Adje/.
真実に困惑して、身体は恐怖に震える。

xA merra hIppA pauwel aEukI iwl/.
力をどこに注いで立ち続ければいい?

jLYNztA/.
「実行する」

jLYNztA/.
「実行する」

xA merra zz hIpIp merra auNkA Aqejyu_tyui LYAcia/.
この世界の小さな人であると私たちは理解できない。 

jLYNztA/.
「実行する」

jLYNztA/.
「実行する」

xA merra la aukA zz rAfArAm Aeje_balduo/.
私たちは恐怖を知らないように生きるしかない。 

jLYNztA/.
「実行する」