bcdice 3.0.0.pre.alpha.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.
Files changed (258) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +21 -0
  3. data/.github/.codecov.yaml +10 -0
  4. data/.github/workflows/coverage.yaml +21 -0
  5. data/.github/workflows/lint.yaml +13 -0
  6. data/.github/workflows/test.yml +20 -0
  7. data/.gitignore +14 -0
  8. data/.rubocop.yml +96 -0
  9. data/.rubocop_todo.yml +113 -0
  10. data/CHANGELOG.md +868 -0
  11. data/Gemfile +11 -0
  12. data/LICENSE +29 -0
  13. data/README.md +38 -0
  14. data/ROADMAP.md +30 -0
  15. data/Rakefile +118 -0
  16. data/bcdice.gemspec +27 -0
  17. data/bin/repl.rb +21 -0
  18. data/docs/README.txt +2028 -0
  19. data/docs/dicebot_sort_key.md +72 -0
  20. data/docs/how_to_make_dicebot.md +453 -0
  21. data/lib/bcdice.rb +8 -0
  22. data/lib/bcdice/arithmetic_evaluator.rb +167 -0
  23. data/lib/bcdice/base.rb +423 -0
  24. data/lib/bcdice/command_parser.rb +242 -0
  25. data/lib/bcdice/common_command.rb +23 -0
  26. data/lib/bcdice/common_command/add_dice.rb +54 -0
  27. data/lib/bcdice/common_command/add_dice/node.rb +488 -0
  28. data/lib/bcdice/common_command/add_dice/parser.rb +282 -0
  29. data/lib/bcdice/common_command/add_dice/randomizer.rb +48 -0
  30. data/lib/bcdice/common_command/barabara_dice.rb +57 -0
  31. data/lib/bcdice/common_command/calc.rb +39 -0
  32. data/lib/bcdice/common_command/choice.rb +35 -0
  33. data/lib/bcdice/common_command/d66_dice.rb +63 -0
  34. data/lib/bcdice/common_command/reroll_dice.rb +190 -0
  35. data/lib/bcdice/common_command/upper_dice.rb +165 -0
  36. data/lib/bcdice/common_command/version.rb +26 -0
  37. data/lib/bcdice/dice_table.rb +8 -0
  38. data/lib/bcdice/dice_table/chain_table.rb +33 -0
  39. data/lib/bcdice/dice_table/d66_grid_table.rb +26 -0
  40. data/lib/bcdice/dice_table/d66_range_table.rb +26 -0
  41. data/lib/bcdice/dice_table/d66_table.rb +34 -0
  42. data/lib/bcdice/dice_table/range_table.rb +267 -0
  43. data/lib/bcdice/dice_table/roll_result.rb +43 -0
  44. data/lib/bcdice/dice_table/sai_fic_skill_table.rb +32 -0
  45. data/lib/bcdice/dice_table/table.rb +35 -0
  46. data/lib/bcdice/enum.rb +15 -0
  47. data/lib/bcdice/format.rb +36 -0
  48. data/lib/bcdice/game_system.rb +187 -0
  49. data/lib/bcdice/game_system/AFF2e.rb +141 -0
  50. data/lib/bcdice/game_system/AceKillerGene.rb +55 -0
  51. data/lib/bcdice/game_system/Airgetlamh.rb +117 -0
  52. data/lib/bcdice/game_system/Alsetto.rb +124 -0
  53. data/lib/bcdice/game_system/Alshard.rb +27 -0
  54. data/lib/bcdice/game_system/AlterRaise.rb +282 -0
  55. data/lib/bcdice/game_system/Amadeus.rb +622 -0
  56. data/lib/bcdice/game_system/Amadeus_Korean.rb +501 -0
  57. data/lib/bcdice/game_system/AnimaAnimus.rb +96 -0
  58. data/lib/bcdice/game_system/Arianrhod.rb +50 -0
  59. data/lib/bcdice/game_system/ArsMagica.rb +149 -0
  60. data/lib/bcdice/game_system/Avandner.rb +101 -0
  61. data/lib/bcdice/game_system/BBN.rb +143 -0
  62. data/lib/bcdice/game_system/BadLife.rb +435 -0
  63. data/lib/bcdice/game_system/BarnaKronika.rb +188 -0
  64. data/lib/bcdice/game_system/BattleTech.rb +530 -0
  65. data/lib/bcdice/game_system/BeastBindTrinity.rb +348 -0
  66. data/lib/bcdice/game_system/BeginningIdol.rb +2527 -0
  67. data/lib/bcdice/game_system/BeginningIdol_Korean.rb +2378 -0
  68. data/lib/bcdice/game_system/BladeOfArcana.rb +252 -0
  69. data/lib/bcdice/game_system/BlindMythos.rb +460 -0
  70. data/lib/bcdice/game_system/BloodCrusade.rb +423 -0
  71. data/lib/bcdice/game_system/BloodMoon.rb +213 -0
  72. data/lib/bcdice/game_system/CardRanker.rb +249 -0
  73. data/lib/bcdice/game_system/ChaosFlare.rb +181 -0
  74. data/lib/bcdice/game_system/Chill.rb +163 -0
  75. data/lib/bcdice/game_system/Chill3.rb +52 -0
  76. data/lib/bcdice/game_system/CodeLayerd.rb +132 -0
  77. data/lib/bcdice/game_system/ColossalHunter.rb +569 -0
  78. data/lib/bcdice/game_system/CrashWorld.rb +81 -0
  79. data/lib/bcdice/game_system/Cthulhu.rb +252 -0
  80. data/lib/bcdice/game_system/Cthulhu7th.rb +767 -0
  81. data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +386 -0
  82. data/lib/bcdice/game_system/Cthulhu7th_Korean.rb +383 -0
  83. data/lib/bcdice/game_system/CthulhuTech.rb +263 -0
  84. data/lib/bcdice/game_system/Cthulhu_ChineseTraditional.rb +243 -0
  85. data/lib/bcdice/game_system/Cthulhu_Korean.rb +240 -0
  86. data/lib/bcdice/game_system/DarkBlaze.rb +207 -0
  87. data/lib/bcdice/game_system/DarkDaysDrive.rb +425 -0
  88. data/lib/bcdice/game_system/DarkSouls.rb +91 -0
  89. data/lib/bcdice/game_system/DeadlineHeroes.rb +583 -0
  90. data/lib/bcdice/game_system/DemonParasite.rb +524 -0
  91. data/lib/bcdice/game_system/DetatokoSaga.rb +310 -0
  92. data/lib/bcdice/game_system/DetatokoSaga_Korean.rb +310 -0
  93. data/lib/bcdice/game_system/DiceBot.rb +17 -0
  94. data/lib/bcdice/game_system/DiceOfTheDead.rb +111 -0
  95. data/lib/bcdice/game_system/DoubleCross.rb +373 -0
  96. data/lib/bcdice/game_system/DoubleCross_Korean.rb +371 -0
  97. data/lib/bcdice/game_system/Dracurouge.rb +1856 -0
  98. data/lib/bcdice/game_system/Dracurouge_Korean.rb +310 -0
  99. data/lib/bcdice/game_system/DungeonsAndDragons.rb +19 -0
  100. data/lib/bcdice/game_system/EarthDawn.rb +225 -0
  101. data/lib/bcdice/game_system/EarthDawn3.rb +275 -0
  102. data/lib/bcdice/game_system/EarthDawn4.rb +272 -0
  103. data/lib/bcdice/game_system/EclipsePhase.rb +51 -0
  104. data/lib/bcdice/game_system/Elric.rb +36 -0
  105. data/lib/bcdice/game_system/Elysion.rb +1213 -0
  106. data/lib/bcdice/game_system/EmbryoMachine.rb +227 -0
  107. data/lib/bcdice/game_system/EndBreaker.rb +132 -0
  108. data/lib/bcdice/game_system/EtrianOdysseySRS.rb +27 -0
  109. data/lib/bcdice/game_system/Fiasco.rb +123 -0
  110. data/lib/bcdice/game_system/Fiasco_Korean.rb +122 -0
  111. data/lib/bcdice/game_system/FilledWith.rb +1543 -0
  112. data/lib/bcdice/game_system/FullMetalPanic.rb +27 -0
  113. data/lib/bcdice/game_system/FutariSousa.rb +994 -0
  114. data/lib/bcdice/game_system/FutariSousa_Korean.rb +994 -0
  115. data/lib/bcdice/game_system/Garako.rb +559 -0
  116. data/lib/bcdice/game_system/GardenOrder.rb +451 -0
  117. data/lib/bcdice/game_system/GehennaAn.rb +136 -0
  118. data/lib/bcdice/game_system/GeishaGirlwithKatana.rb +120 -0
  119. data/lib/bcdice/game_system/GoblinSlayer.rb +168 -0
  120. data/lib/bcdice/game_system/GoldenSkyStories.rb +77 -0
  121. data/lib/bcdice/game_system/Gorilla.rb +44 -0
  122. data/lib/bcdice/game_system/GranCrest.rb +518 -0
  123. data/lib/bcdice/game_system/Gundog.rb +52 -0
  124. data/lib/bcdice/game_system/GundogRevised.rb +294 -0
  125. data/lib/bcdice/game_system/GundogZero.rb +261 -0
  126. data/lib/bcdice/game_system/Gurps.rb +403 -0
  127. data/lib/bcdice/game_system/GurpsFW.rb +2330 -0
  128. data/lib/bcdice/game_system/HarnMaster.rb +208 -0
  129. data/lib/bcdice/game_system/HatsuneMiku.rb +706 -0
  130. data/lib/bcdice/game_system/Hieizan.rb +48 -0
  131. data/lib/bcdice/game_system/HouraiGakuen.rb +270 -0
  132. data/lib/bcdice/game_system/HuntersMoon.rb +503 -0
  133. data/lib/bcdice/game_system/Illusio.rb +89 -0
  134. data/lib/bcdice/game_system/InfiniteFantasia.rb +51 -0
  135. data/lib/bcdice/game_system/Insane.rb +822 -0
  136. data/lib/bcdice/game_system/Insane_Korean.rb +773 -0
  137. data/lib/bcdice/game_system/IthaWenUa.rb +38 -0
  138. data/lib/bcdice/game_system/JamesBond.rb +45 -0
  139. data/lib/bcdice/game_system/Kamigakari.rb +394 -0
  140. data/lib/bcdice/game_system/Kamigakari_Korean.rb +428 -0
  141. data/lib/bcdice/game_system/KanColle.rb +938 -0
  142. data/lib/bcdice/game_system/KemonoNoMori.rb +258 -0
  143. data/lib/bcdice/game_system/KillDeathBusiness.rb +1420 -0
  144. data/lib/bcdice/game_system/KillDeathBusiness_Korean.rb +1510 -0
  145. data/lib/bcdice/game_system/KurayamiCrying.rb +49 -0
  146. data/lib/bcdice/game_system/LiveraDoll.rb +375 -0
  147. data/lib/bcdice/game_system/LogHorizon.rb +3567 -0
  148. data/lib/bcdice/game_system/LogHorizon_Korean.rb +1407 -0
  149. data/lib/bcdice/game_system/LostRecord.rb +30 -0
  150. data/lib/bcdice/game_system/LostRoyal.rb +242 -0
  151. data/lib/bcdice/game_system/MagicaLogia.rb +1148 -0
  152. data/lib/bcdice/game_system/MeikyuDays.rb +415 -0
  153. data/lib/bcdice/game_system/MeikyuKingdom.rb +466 -0
  154. data/lib/bcdice/game_system/MeikyuKingdomBasic.rb +269 -0
  155. data/lib/bcdice/game_system/MetalHead.rb +249 -0
  156. data/lib/bcdice/game_system/MetalHeadExtream.rb +917 -0
  157. data/lib/bcdice/game_system/MetallicGuardian.rb +31 -0
  158. data/lib/bcdice/game_system/MonotoneMuseum.rb +354 -0
  159. data/lib/bcdice/game_system/MonotoneMuseum_Korean.rb +304 -0
  160. data/lib/bcdice/game_system/Nechronica.rb +211 -0
  161. data/lib/bcdice/game_system/Nechronica_Korean.rb +160 -0
  162. data/lib/bcdice/game_system/NeverCloud.rb +74 -0
  163. data/lib/bcdice/game_system/NightWizard.rb +231 -0
  164. data/lib/bcdice/game_system/NightWizard3rd.rb +24 -0
  165. data/lib/bcdice/game_system/NightmareHunterDeep.rb +117 -0
  166. data/lib/bcdice/game_system/NinjaSlayer.rb +274 -0
  167. data/lib/bcdice/game_system/NjslyrBattle.rb +54 -0
  168. data/lib/bcdice/game_system/Nuekagami.rb +293 -0
  169. data/lib/bcdice/game_system/OneWayHeroics.rb +134 -0
  170. data/lib/bcdice/game_system/OracleEngine.rb +265 -0
  171. data/lib/bcdice/game_system/OrgaRain.rb +77 -0
  172. data/lib/bcdice/game_system/Oukahoushin3rd.rb +162 -0
  173. data/lib/bcdice/game_system/Paradiso.rb +323 -0
  174. data/lib/bcdice/game_system/Paranoia.rb +64 -0
  175. data/lib/bcdice/game_system/ParanoiaRebooted.rb +109 -0
  176. data/lib/bcdice/game_system/ParasiteBlood.rb +231 -0
  177. data/lib/bcdice/game_system/Pathfinder.rb +21 -0
  178. data/lib/bcdice/game_system/Peekaboo.rb +324 -0
  179. data/lib/bcdice/game_system/Pendragon.rb +39 -0
  180. data/lib/bcdice/game_system/PhantasmAdventure.rb +73 -0
  181. data/lib/bcdice/game_system/Postman.rb +185 -0
  182. data/lib/bcdice/game_system/PulpCthulhu.rb +184 -0
  183. data/lib/bcdice/game_system/Raisondetre.rb +171 -0
  184. data/lib/bcdice/game_system/RecordOfLodossWar.rb +58 -0
  185. data/lib/bcdice/game_system/RecordOfSteam.rb +149 -0
  186. data/lib/bcdice/game_system/RokumonSekai2.rb +110 -0
  187. data/lib/bcdice/game_system/RoleMaster.rb +24 -0
  188. data/lib/bcdice/game_system/RuneQuest.rb +47 -0
  189. data/lib/bcdice/game_system/RyuTuber.rb +266 -0
  190. data/lib/bcdice/game_system/Ryutama.rb +199 -0
  191. data/lib/bcdice/game_system/SRS.rb +348 -0
  192. data/lib/bcdice/game_system/SamsaraBallad.rb +111 -0
  193. data/lib/bcdice/game_system/Satasupe.rb +1173 -0
  194. data/lib/bcdice/game_system/ScreamHighSchool.rb +128 -0
  195. data/lib/bcdice/game_system/SevenFortressMobius.rb +34 -0
  196. data/lib/bcdice/game_system/ShadowRun.rb +26 -0
  197. data/lib/bcdice/game_system/ShadowRun4.rb +49 -0
  198. data/lib/bcdice/game_system/ShadowRun5.rb +80 -0
  199. data/lib/bcdice/game_system/SharedFantasia.rb +69 -0
  200. data/lib/bcdice/game_system/ShinMegamiTenseiKakuseihen.rb +85 -0
  201. data/lib/bcdice/game_system/ShinkuuGakuen.rb +506 -0
  202. data/lib/bcdice/game_system/ShinobiGami.rb +680 -0
  203. data/lib/bcdice/game_system/ShoujoTenrankai.rb +677 -0
  204. data/lib/bcdice/game_system/Skynauts.rb +324 -0
  205. data/lib/bcdice/game_system/SteamPunkers.rb +485 -0
  206. data/lib/bcdice/game_system/StellarKnights.rb +856 -0
  207. data/lib/bcdice/game_system/SterileLife.rb +452 -0
  208. data/lib/bcdice/game_system/StrangerOfSwordCity.rb +103 -0
  209. data/lib/bcdice/game_system/StratoShout.rb +316 -0
  210. data/lib/bcdice/game_system/StratoShout_Korean.rb +314 -0
  211. data/lib/bcdice/game_system/Strave.rb +223 -0
  212. data/lib/bcdice/game_system/SwordWorld.rb +491 -0
  213. data/lib/bcdice/game_system/SwordWorld2_0.rb +295 -0
  214. data/lib/bcdice/game_system/SwordWorld2_5.rb +121 -0
  215. data/lib/bcdice/game_system/TherapieSein.rb +93 -0
  216. data/lib/bcdice/game_system/TokumeiTenkousei.rb +96 -0
  217. data/lib/bcdice/game_system/TokyoGhostResearch.rb +122 -0
  218. data/lib/bcdice/game_system/TokyoNova.rb +19 -0
  219. data/lib/bcdice/game_system/Torg.rb +364 -0
  220. data/lib/bcdice/game_system/Torg1_5.rb +139 -0
  221. data/lib/bcdice/game_system/TorgEternity.rb +414 -0
  222. data/lib/bcdice/game_system/TrinitySeven.rb +364 -0
  223. data/lib/bcdice/game_system/TunnelsAndTrolls.rb +287 -0
  224. data/lib/bcdice/game_system/TwilightGunsmoke.rb +527 -0
  225. data/lib/bcdice/game_system/Utakaze.rb +153 -0
  226. data/lib/bcdice/game_system/VampireTheMasquerade5th.rb +132 -0
  227. data/lib/bcdice/game_system/Villaciel.rb +555 -0
  228. data/lib/bcdice/game_system/WARPS.rb +33 -0
  229. data/lib/bcdice/game_system/WaresBlade.rb +34 -0
  230. data/lib/bcdice/game_system/Warhammer.rb +336 -0
  231. data/lib/bcdice/game_system/WitchQuest.rb +275 -0
  232. data/lib/bcdice/game_system/WorldOfDarkness.rb +135 -0
  233. data/lib/bcdice/game_system/YankeeYogSothoth.rb +440 -0
  234. data/lib/bcdice/game_system/YearZeroEngine.rb +134 -0
  235. data/lib/bcdice/game_system/ZettaiReido.rb +142 -0
  236. data/lib/bcdice/game_system/meikyu_kingdom/item_table.rb +383 -0
  237. data/lib/bcdice/game_system/meikyu_kingdom/kingdom_name_table.rb +89 -0
  238. data/lib/bcdice/game_system/meikyu_kingdom/landscape_table.rb +196 -0
  239. data/lib/bcdice/game_system/meikyu_kingdom/name_tables.rb +223 -0
  240. data/lib/bcdice/game_system/meikyu_kingdom/placename_table.rb +214 -0
  241. data/lib/bcdice/game_system/meikyu_kingdom/tables.rb +610 -0
  242. data/lib/bcdice/game_system/meikyu_kingdom/word_table.rb +117 -0
  243. data/lib/bcdice/game_system/meikyu_kingdom_basic/item_table.rb +380 -0
  244. data/lib/bcdice/game_system/meikyu_kingdom_basic/kingdom_table.rb +184 -0
  245. data/lib/bcdice/game_system/meikyu_kingdom_basic/name_table.rb +417 -0
  246. data/lib/bcdice/game_system/meikyu_kingdom_basic/table.rb +912 -0
  247. data/lib/bcdice/game_system/meikyu_kingdom_basic/word_table.rb +121 -0
  248. data/lib/bcdice/game_system/one_way_heroics/dungeon_table.rb +67 -0
  249. data/lib/bcdice/game_system/one_way_heroics/random_event_table.rb +166 -0
  250. data/lib/bcdice/game_system/one_way_heroics/tables.rb +668 -0
  251. data/lib/bcdice/loader.rb +37 -0
  252. data/lib/bcdice/normalize.rb +38 -0
  253. data/lib/bcdice/preprocessor.rb +87 -0
  254. data/lib/bcdice/randomizer.rb +137 -0
  255. data/lib/bcdice/repl.rb +155 -0
  256. data/lib/bcdice/user_defined_dice_table.rb +153 -0
  257. data/lib/bcdice/version.rb +3 -0
  258. metadata +304 -0
@@ -0,0 +1,242 @@
1
+ require "bcdice/arithmetic_evaluator"
2
+ require "bcdice/normalize"
3
+ require "bcdice/format"
4
+
5
+ module BCDice
6
+ # よくある圢匏のコマンドのパヌスを補助するクラス
7
+ #
8
+ # @example Literal by String
9
+ # parser = CommandParser.new("MC")
10
+ # parsed = parser.parse("MC+2*3@30<=10/2-3") #=> <CommandParser::Parsed>
11
+ #
12
+ # parsed.command #=> "MC"
13
+ # parsed.modify_number #=> 6
14
+ # parsed.critical #=> 30
15
+ # parsed.cmp_op #=> #>=
16
+ # parsed.target_number #=> 2
17
+ #
18
+ # @example Literal by Regexp
19
+ # parser = CommandParser.new(/^RE\d+$/)
20
+ # parsed = parser.parse("RE44+20") #=> <CommandParser::Parsed>
21
+ #
22
+ # parsed.command #=> "RE44"
23
+ # parsed.modify_number #=> 20
24
+ class CommandParser < ArithmeticEvaluator
25
+ # @param literals [Array<String, Regexp>]
26
+ def initialize(*literals)
27
+ @literals = literals
28
+ @round_type = RoundType::FLOOR
29
+ @allowed_cmp_op = nil
30
+
31
+ @enabled_question_target = false
32
+ end
33
+
34
+ # パヌス結果
35
+ class Parsed
36
+ # @return [String]
37
+ attr_accessor :command
38
+
39
+ # @return [Integer, nil]
40
+ attr_accessor :critical
41
+
42
+ # @return [Integer, nil]
43
+ attr_accessor :fumble
44
+
45
+ # @return [Integer, nil]
46
+ attr_accessor :dollar
47
+
48
+ # @return [Integer]
49
+ attr_accessor :modify_number
50
+
51
+ # @return [Symbol, nil]
52
+ attr_accessor :cmp_op
53
+
54
+ # @return [Integer, nil]
55
+ attr_accessor :target_number
56
+
57
+ # @param value [Boolean]
58
+ # @return [Boolean]
59
+ attr_writer :question_target
60
+
61
+ def initialize
62
+ @critical = nil
63
+ @fumble = nil
64
+ @dollar = nil
65
+ @cmp_op = nil
66
+ @target_number = nil
67
+ @question_target = false
68
+ end
69
+
70
+ # @return [Boolean]
71
+ def question_target?
72
+ @question_target
73
+ end
74
+
75
+ # @param suffix_position [Symbol] クリティカルなどの衚瀺䜍眮
76
+ # @return [String]
77
+ def to_s(suffix_position = :after_command)
78
+ c = @critical ? "@#{@critical}" : nil
79
+ f = @fumble ? "##{@fumble}" : nil
80
+ d = @dollar ? "$#{@dollar}" : nil
81
+ m = Format.modifier(@modify_number)
82
+ target = @question_target ? "?" : @target_number
83
+
84
+ case suffix_position
85
+ when :after_command
86
+ [@command, c, f, d, m, @cmp_op, target].join()
87
+ when :after_modify_number
88
+ [@command, m, c, f, d, @cmp_op, target].join()
89
+ when :after_target_number
90
+ [@command, m, @cmp_op, target, c, f, d].join()
91
+ end
92
+ end
93
+ end
94
+
95
+ # 特定の比范挔算子のみ蚱可するようにする。
96
+ # 比范挔算子なしを蚱可する堎合には nil を指定しおください。
97
+ #
98
+ # @param cmp_op [Array<Symbol, nil>] 蚱可する比范挔算子の䞀芧
99
+ # @return [self]
100
+ def allow_cmp_op(*cmp_op)
101
+ @allowed_cmp_op = cmp_op
102
+ self
103
+ end
104
+
105
+ # 目暙倀 "?" を蚱容する
106
+ #
107
+ # @return [self]
108
+ def enable_question_target
109
+ @enabled_question_target = true
110
+ self
111
+ end
112
+
113
+ # 匏をパヌスする
114
+ #
115
+ # @param expr [String]
116
+ # @param round_type [Symbol]
117
+ # @return [CommandParser::Parsed, nil]
118
+ def parse(expr, round_type = RoundType::FLOOR)
119
+ @tokens = tokenize(expr)
120
+ @idx = 0
121
+ @error = false
122
+ @round_type = round_type
123
+
124
+ @parsed = Parsed.new()
125
+
126
+ lhs()
127
+ if @error
128
+ return nil
129
+ end
130
+
131
+ @parsed.cmp_op = take_cmp_op()
132
+ rhs() if @parsed.cmp_op
133
+
134
+ if @idx < @tokens.size || @error
135
+ return nil
136
+ end
137
+
138
+ return @parsed
139
+ end
140
+
141
+ private
142
+
143
+ # @return [Array<String>]
144
+ def tokenize(expr)
145
+ expr.gsub(%r{[\(\)\+\-*/@#\$]|[<>!=]+}) { |e| " #{e} " }.split(" ")
146
+ end
147
+
148
+ def lhs
149
+ command = take()
150
+ unless literal?(command)
151
+ @error = true
152
+ return
153
+ end
154
+
155
+ command_suffix()
156
+
157
+ ret = 0
158
+ loop do
159
+ if consume("+")
160
+ ret += mul()
161
+ elsif consume("-")
162
+ ret -= mul()
163
+ else
164
+ break
165
+ end
166
+ end
167
+
168
+ command_suffix()
169
+
170
+ @parsed.command = command
171
+ @parsed.modify_number = ret
172
+ end
173
+
174
+ def command_suffix
175
+ loop do
176
+ if consume("@")
177
+ if @parsed.critical
178
+ @error = true
179
+ end
180
+ @parsed.critical = unary()
181
+ elsif consume("#")
182
+ if @parsed.fumble
183
+ @error = true
184
+ end
185
+ @parsed.fumble = unary()
186
+ elsif consume("$")
187
+ if @parsed.dollar
188
+ @error = true
189
+ end
190
+ @parsed.dollar = unary()
191
+ else
192
+ break
193
+ end
194
+ end
195
+ end
196
+
197
+ def rhs
198
+ if @enabled_question_target && consume("?")
199
+ @parsed.question_target = true
200
+ @parsed.target_number = 0
201
+ else
202
+ @parsed.question_target = false
203
+ @parsed.target_number = expr()
204
+ end
205
+ end
206
+
207
+ def literal?(command)
208
+ @literals.each do |lit|
209
+ case lit
210
+ when String
211
+ return true if command == lit
212
+ when Regexp
213
+ return true if command =~ lit
214
+ end
215
+ end
216
+
217
+ return false
218
+ end
219
+
220
+ def take
221
+ ret = @tokens[@idx]
222
+ @idx += 1
223
+
224
+ return ret
225
+ end
226
+
227
+ def take_cmp_op
228
+ cmp_op = Normalize.comparison_operator(take())
229
+ @error ||= denied_cmp_op?(cmp_op)
230
+
231
+ return cmp_op
232
+ end
233
+
234
+ def denied_cmp_op?(cmp_op)
235
+ if @allowed_cmp_op.nil?
236
+ false
237
+ else
238
+ !@allowed_cmp_op.include?(cmp_op)
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,23 @@
1
+ require "bcdice/common_command/add_dice"
2
+ require "bcdice/common_command/barabara_dice"
3
+ require "bcdice/common_command/calc"
4
+ require "bcdice/common_command/choice"
5
+ require "bcdice/common_command/d66_dice"
6
+ require "bcdice/common_command/reroll_dice"
7
+ require "bcdice/common_command/upper_dice"
8
+ require "bcdice/common_command/version"
9
+
10
+ module BCDice
11
+ module CommonCommand
12
+ COMMANDS = [
13
+ AddDice,
14
+ BarabaraDice,
15
+ Calc,
16
+ Choice,
17
+ D66Dice,
18
+ RerollDice,
19
+ UpperDice,
20
+ Version,
21
+ ].freeze
22
+ end
23
+ end
@@ -0,0 +1,54 @@
1
+ require "bcdice/normalize"
2
+ require "bcdice/common_command/add_dice/parser"
3
+ require "bcdice/common_command/add_dice/randomizer"
4
+
5
+ module BCDice
6
+ module CommonCommand
7
+ class AddDice
8
+ PREFIX_PATTERN = /[\+\-\dD\(\[]+/.freeze
9
+
10
+ def initialize(command, randomizer, game_system)
11
+ @command = command
12
+ @bcdice = randomizer
13
+ @diceBot = game_system
14
+
15
+ @dice_list = []
16
+ @is_secret = false
17
+ end
18
+
19
+ def secret?
20
+ @is_secret
21
+ end
22
+
23
+ def eval()
24
+ parser = Parser.new(@command)
25
+
26
+ command = parser.parse()
27
+ if parser.error?
28
+ return nil
29
+ end
30
+
31
+ randomizer = Randomizer.new(@bcdice, @diceBot, command.cmp_op)
32
+ total = command.lhs.eval(randomizer)
33
+
34
+ output =
35
+ if randomizer.dice_list.size <= 1 && command.lhs.is_a?(Node::DiceRoll)
36
+ "(#{command})  #{total}"
37
+ else
38
+ "(#{command})  #{command.lhs.output}  #{total}"
39
+ end
40
+
41
+ dice_list = randomizer.dice_list
42
+
43
+ if command.cmp_op
44
+ dice_total = dice_list.inject(&:+)
45
+ output += @diceBot.check_result(total, dice_total, dice_list, randomizer.sides, command.cmp_op, command.rhs)
46
+ end
47
+
48
+ @is_secret = parser.secret?
49
+
50
+ return output
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,488 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module CommonCommand
5
+ class AddDice
6
+ # 加算ロヌルの構文解析朚のノヌドを栌玍するモゞュヌル
7
+ module Node
8
+ # 加算ロヌルコマンドのノヌド。
9
+ #
10
+ # 目暙倀が蚭定されおいない堎合は +lhs+ のみを䜿甚する。
11
+ # 目暙倀が蚭定されおいる堎合は、+lhs+、+cmp_op+、+rhs+ を䜿甚する。
12
+ class Command
13
+ # 巊蟺のノヌド
14
+ # @return [Object]
15
+ attr_reader :lhs
16
+ # 比范挔算子
17
+ # @return [Symbol]
18
+ attr_reader :cmp_op
19
+ # 右蟺のノヌド
20
+ # @return [Integer, String]
21
+ attr_reader :rhs
22
+
23
+ # ノヌドを初期化する
24
+ # @param [Object] lhs 巊蟺のノヌド
25
+ # @param [Symbol] cmp_op 比范挔算子
26
+ # @param [Integer, String] rhs 右蟺のノヌド
27
+ def initialize(lhs, cmp_op, rhs)
28
+ @lhs = lhs
29
+ @cmp_op = cmp_op
30
+ @rhs = rhs
31
+ end
32
+
33
+ # 文字列に倉換する
34
+ # @return [String]
35
+ def to_s
36
+ @lhs.to_s + cmp_op_text + @rhs.to_s
37
+ end
38
+
39
+ # ノヌドのS匏を返す
40
+ # @return [String]
41
+ def s_exp
42
+ if @cmp_op
43
+ "(Command (#{@cmp_op} #{@lhs.s_exp} #{@rhs}))"
44
+ else
45
+ "(Command #{@lhs.s_exp})"
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # メッセヌゞ䞭で比范挔算子をどのように衚瀺するかを返す
52
+ # @return [String]
53
+ def cmp_op_text
54
+ case @cmp_op
55
+ when :'!='
56
+ "<>"
57
+ when :==
58
+ "="
59
+ else
60
+ @cmp_op.to_s
61
+ end
62
+ end
63
+ end
64
+
65
+ # 二項挔算子のノヌド
66
+ class BinaryOp
67
+ # ノヌドを初期化する
68
+ # @param [Object] lhs 巊のオペランドのノヌド
69
+ # @param [Symbol] op 挔算子
70
+ # @param [Object] rhs 右のオペランドのノヌド
71
+ def initialize(lhs, op, rhs)
72
+ @lhs = lhs
73
+ @op = op
74
+ @rhs = rhs
75
+ end
76
+
77
+ # ノヌドを評䟡する
78
+ #
79
+ # 巊右のオペランドをそれぞれ再垰的に評䟡した埌で、挔算を行う。
80
+ #
81
+ # @param [Randomizer] randomizer ランダマむザ
82
+ # @return [Integer] 評䟡結果
83
+ def eval(randomizer)
84
+ lhs = @lhs.eval(randomizer)
85
+ rhs = @rhs.eval(randomizer)
86
+
87
+ return calc(lhs, rhs)
88
+ end
89
+
90
+ # 文字列に倉換する
91
+ # @return [String]
92
+ def to_s
93
+ "#{@lhs}#{@op}#{@rhs}"
94
+ end
95
+
96
+ # メッセヌゞぞの出力を返す
97
+ # @return [String]
98
+ def output
99
+ "#{@lhs.output}#{@op}#{@rhs.output}"
100
+ end
101
+
102
+ # ノヌドのS匏を返す
103
+ # @return [String]
104
+ def s_exp
105
+ "(#{op_for_s_exp} #{@lhs.s_exp} #{@rhs.s_exp})"
106
+ end
107
+
108
+ private
109
+
110
+ # 挔算を行う
111
+ # @param [Integer] lhs 巊のオペランド
112
+ # @param [Integer] rhs 右のオペランド
113
+ # @return [Integer] 挔算の結果
114
+ def calc(lhs, rhs)
115
+ lhs.send(@op, rhs)
116
+ end
117
+
118
+ # S匏で䜿う挔算子の衚珟を返す
119
+ # @return [String]
120
+ def op_for_s_exp
121
+ @op
122
+ end
123
+ end
124
+
125
+ # 陀算ノヌドの基底クラス
126
+ #
127
+ # 定数 +ROUNDING_METHOD+ で端数凊理方法を瀺す蚘号
128
+ # ( +'U'+, +'R'+, +''+ ) を定矩するこず。
129
+ # たた、陀算および端数凊理を行う +divide_and_round+ メ゜ッドを実装するこず。
130
+ class DivideBase < BinaryOp
131
+ # ノヌドを初期化する
132
+ # @param [Object] lhs 巊のオペランドのノヌド
133
+ # @param [Object] rhs 右のオペランドのノヌド
134
+ def initialize(lhs, rhs)
135
+ super(lhs, :/, rhs)
136
+ end
137
+
138
+ # 文字列に倉換する
139
+ #
140
+ # 通垞の結果の末尟に、端数凊理方法を瀺す蚘号を付加する。
141
+ #
142
+ # @return [String]
143
+ def to_s
144
+ "#{super}#{rounding_method}"
145
+ end
146
+
147
+ # メッセヌゞぞの出力を返す
148
+ #
149
+ # 通垞の結果の末尟に、端数凊理方法を瀺す蚘号を付加する。
150
+ #
151
+ # @return [String]
152
+ def output
153
+ "#{super}#{rounding_method}"
154
+ end
155
+
156
+ private
157
+
158
+ # 端数凊理方法を瀺す蚘号を返す
159
+ # @return [String]
160
+ def rounding_method
161
+ self.class::ROUNDING_METHOD
162
+ end
163
+
164
+ # S匏で䜿う挔算子の衚珟を返す
165
+ # @return [String]
166
+ def op_for_s_exp
167
+ "#{@op}#{rounding_method}"
168
+ end
169
+
170
+ # 挔算を行う
171
+ # @param [Integer] lhs 巊のオペランド
172
+ # @param [Integer] rhs 右のオペランド
173
+ # @return [Integer] 挔算の結果
174
+ def calc(lhs, rhs)
175
+ if rhs.zero?
176
+ return 1
177
+ end
178
+
179
+ return divide_and_round(lhs, rhs)
180
+ end
181
+
182
+ # 陀算および端数凊理を行う
183
+ # @param [Integer] _dividend 被陀数
184
+ # @param [Integer] _divisor 陀数0以倖
185
+ # @return [Integer]
186
+ def divide_and_round(_dividend, _divisor)
187
+ raise NotImplementedError
188
+ end
189
+ end
190
+
191
+ # 陀算切り䞊げのノヌド
192
+ class DivideWithRoundingUp < DivideBase
193
+ # 端数凊理方法を瀺す蚘号
194
+ ROUNDING_METHOD = "U"
195
+
196
+ private
197
+
198
+ # 陀算および端数凊理を行う
199
+ # @param [Integer] dividend 被陀数
200
+ # @param [Integer] divisor 陀数0以倖
201
+ # @return [Integer]
202
+ def divide_and_round(dividend, divisor)
203
+ (dividend.to_f / divisor).ceil
204
+ end
205
+ end
206
+
207
+ # 陀算四捚五入のノヌド
208
+ class DivideWithRoundingOff < DivideBase
209
+ # 端数凊理方法を瀺す蚘号
210
+ ROUNDING_METHOD = "R"
211
+
212
+ private
213
+
214
+ # 陀算および端数凊理を行う
215
+ # @param [Integer] dividend 被陀数
216
+ # @param [Integer] divisor 陀数0以倖
217
+ # @return [Integer]
218
+ def divide_and_round(dividend, divisor)
219
+ (dividend.to_f / divisor).round
220
+ end
221
+ end
222
+
223
+ # 陀算切り捚おのノヌド
224
+ class DivideWithRoundingDown < DivideBase
225
+ # 端数凊理方法を瀺す蚘号
226
+ ROUNDING_METHOD = ""
227
+
228
+ private
229
+
230
+ # 陀算および端数凊理を行う
231
+ # @param [Integer] dividend 被陀数
232
+ # @param [Integer] divisor 陀数0以倖
233
+ # @return [Integer]
234
+ def divide_and_round(dividend, divisor)
235
+ dividend / divisor
236
+ end
237
+ end
238
+
239
+ # 笊号反転のノヌド
240
+ class Negate
241
+ # 笊号反転の察象
242
+ # @return [Object]
243
+ attr_reader :body
244
+
245
+ # ノヌドを初期化する
246
+ # @param [Object] body 笊号反転の察象
247
+ def initialize(body)
248
+ @body = body
249
+ end
250
+
251
+ # ノヌドを評䟡する
252
+ #
253
+ # 察象オペランドを再垰的に評䟡した埌、評䟡結果の笊号を反転する。
254
+ #
255
+ # @param [Randomizer] randomizer ランダマむザ
256
+ # @return [Integer] 評䟡結果
257
+ def eval(randomizer)
258
+ -@body.eval(randomizer)
259
+ end
260
+
261
+ # 文字列に倉換する
262
+ # @return [String]
263
+ def to_s
264
+ "-#{@body}"
265
+ end
266
+
267
+ # メッセヌゞぞの出力を返す
268
+ # @return [String]
269
+ def output
270
+ "-#{@body.output}"
271
+ end
272
+
273
+ # ノヌドのS匏を返す
274
+ # @return [String]
275
+ def s_exp
276
+ "(- #{@body.s_exp})"
277
+ end
278
+ end
279
+
280
+ # ダむスロヌルのノヌド
281
+ class DiceRoll
282
+ # ノヌドを初期化する
283
+ # @param [Number] times ダむスを振る回数のノヌド
284
+ # @param [Number] sides ダむスの面数のノヌド
285
+ def initialize(times, sides)
286
+ @times = times.literal
287
+ @sides = sides.literal
288
+
289
+ # ダむスを振った結果の出力
290
+ @text = nil
291
+ end
292
+
293
+ # ノヌドを評䟡するダむスを振る
294
+ #
295
+ # 評䟡結果は出目の合蚈倀になる。
296
+ # 出目はランダマむザに蚘録される。
297
+ #
298
+ # @param [Randomizer] randomizer ランダマむザ
299
+ # @return [Integer] 評䟡結果出目の合蚈倀
300
+ def eval(randomizer)
301
+ dice_groups = randomizer.roll(@times, @sides)
302
+
303
+ # TODO: Ruby 2.4以降では Array#sum が䜿える
304
+ total = dice_groups.flatten.reduce(0, &:+)
305
+
306
+ dice_str = dice_groups
307
+ .map { |dice_list| "[#{dice_list.join(',')}]" }
308
+ .join
309
+ @text = "#{total}#{dice_str}"
310
+
311
+ return total
312
+ end
313
+
314
+ # 文字列に倉換する
315
+ # @return [String]
316
+ def to_s
317
+ "#{@times}D#{@sides}"
318
+ end
319
+
320
+ # メッセヌゞぞの出力を返す
321
+ # @return [String]
322
+ def output
323
+ @text
324
+ end
325
+
326
+ # ノヌドのS匏を返す
327
+ # @return [String]
328
+ def s_exp
329
+ "(DiceRoll #{@times} #{@sides})"
330
+ end
331
+ end
332
+
333
+ # フィルタ凊理付きダむスロヌルのノヌド。
334
+ #
335
+ # ダむスロヌル埌、条件に埓っお出目を遞択し、和を求める。
336
+ class DiceRollWithFilter
337
+ # フィルタの構造䜓
338
+ #
339
+ # 各フィルタには、あらかじめ゜ヌトされた出目の配列が枡される。
340
+ #
341
+ # @!attribute abbr
342
+ # @return [Symbol] フィルタの略称
343
+ # @!attribute apply
344
+ # @return [Proc] フィルタ凊理の内容
345
+ Filter = Struct.new(:abbr, :apply)
346
+
347
+ # 倧きな出目から耇数個取る
348
+ KEEP_HIGHEST = Filter.new(
349
+ :KH,
350
+ lambda { |sorted_values, n| sorted_values.reverse.take(n) }
351
+ ).freeze
352
+
353
+ # 小さな出目から耇数個取る
354
+ KEEP_LOWEST = Filter.new(
355
+ :KL,
356
+ lambda { |sorted_values, n| sorted_values.take(n) }
357
+ ).freeze
358
+
359
+ # 倧きな出目から耇数個陀く
360
+ DROP_HIGHEST = Filter.new(
361
+ :DH,
362
+ lambda { |sorted_values, n| sorted_values.reverse.drop(n) }
363
+ ).freeze
364
+
365
+ # 小さな出目から耇数個陀く
366
+ DROP_LOWEST = Filter.new(
367
+ :DL,
368
+ lambda { |sorted_values, n| sorted_values.drop(n) }
369
+ ).freeze
370
+
371
+ # ノヌドを初期化する
372
+ # @param [Number] times ダむスを振る回数のノヌド
373
+ # @param [Number] sides ダむスの面数のノヌド
374
+ # @param [Number] n_filtering ダむスを残す/枛らす個数のノヌド
375
+ # @param [Filter] filter フィルタ
376
+ def initialize(times, sides, n_filtering, filter)
377
+ @times = times.literal
378
+ @sides = sides.literal
379
+ @n_filtering = n_filtering.literal
380
+ @filter = filter
381
+
382
+ # ダむスを振った結果の出力
383
+ @text = nil
384
+ end
385
+
386
+ # ノヌドを評䟡するダむスを振り、出目を遞択しお和を求める
387
+ #
388
+ # 評䟡結果は出目の合蚈倀になる。
389
+ # 出目はランダマむザに蚘録される。
390
+ #
391
+ # @param [Randomizer] randomizer ランダマむザ
392
+ # @return [Integer] 評䟡結果出目の合蚈倀
393
+ def eval(randomizer)
394
+ sorted_values = randomizer.roll_once(@times, @sides).sort
395
+ total = @filter
396
+ .apply[sorted_values, @n_filtering]
397
+ .reduce(0, &:+)
398
+
399
+ @text = "#{total}[#{sorted_values.join(',')}]"
400
+
401
+ return total
402
+ end
403
+
404
+ # 文字列に倉換する
405
+ # @return [String]
406
+ def to_s
407
+ "#{@times}D#{@sides}#{@filter.abbr}#{@n_filtering}"
408
+ end
409
+
410
+ # メッセヌゞぞの出力を返す
411
+ # @return [String]
412
+ def output
413
+ @text
414
+ end
415
+
416
+ # ノヌドのS匏を返す
417
+ # @return [String]
418
+ def s_exp
419
+ "(DiceRollWithFilter #{@times} #{@sides} #{@filter.abbr.inspect} #{@n_filtering})"
420
+ end
421
+ end
422
+
423
+ # カッコで匏をたずめるノヌド
424
+ class Parenthesis
425
+ # @param expr [Object] カッコ内のノヌド
426
+ def initialize(expr)
427
+ @expr = expr
428
+ end
429
+
430
+ # @param randomizer [Randomizer]
431
+ # @return [integer]
432
+ def eval(randomizer)
433
+ @expr.eval(randomizer)
434
+ end
435
+
436
+ # @return [String]
437
+ def to_s
438
+ "(#{@expr})"
439
+ end
440
+
441
+ # @return [String]
442
+ def output
443
+ "(#{@expr.output})"
444
+ end
445
+
446
+ # @return [String] S匏
447
+ def s_exp
448
+ "(Parenthesis #{@expr.s_exp})"
449
+ end
450
+ end
451
+
452
+ # 数倀のノヌド
453
+ class Number
454
+ # 倀
455
+ # @return [Integer]
456
+ attr_reader :literal
457
+
458
+ # ノヌドを初期化する
459
+ # @param [Integer] literal 倀
460
+ def initialize(literal)
461
+ @literal = literal
462
+ end
463
+
464
+ # 笊号を反転した結果の数倀ノヌドを返す
465
+ # @return [Number]
466
+ def negate
467
+ Number.new(-@literal)
468
+ end
469
+
470
+ # ノヌドを評䟡する
471
+ # @return [Integer] 栌玍しおいる倀
472
+ def eval(_randomizer)
473
+ @literal
474
+ end
475
+
476
+ # 文字列に倉換する
477
+ # @return [String]
478
+ def to_s
479
+ @literal.to_s
480
+ end
481
+
482
+ alias output to_s
483
+ alias s_exp to_s
484
+ end
485
+ end
486
+ end
487
+ end
488
+ end