bcdice 3.0.0.pre.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
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