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,33 @@
1
+ module BCDice
2
+ module DiceTable
3
+ class ChainTable
4
+ # @param [String] name 表の名前
5
+ # @param [String] type 項目を選ぶときのダイスロールの方法 '1D6'など
6
+ # @param [Array<String, #roll>] items 表の項目の配列
7
+ def initialize(name, type, items)
8
+ @name = name
9
+ @items = items.freeze
10
+
11
+ m = /(\d+)D(\d+)/i.match(type)
12
+ unless m
13
+ raise ArgumentError, "Unexpected table type: #{type}"
14
+ end
15
+
16
+ @times = m[1].to_i
17
+ @sides = m[2].to_i
18
+ end
19
+
20
+ # 表を振る
21
+ # @param randomizer [#roll_sum] ランダマイザ
22
+ # @return [String] 結果
23
+ def roll(randomizer)
24
+ value = randomizer.roll_sum(@times, @sides)
25
+ index = value - @times
26
+ chosen = @items[index]
27
+ chosen = chosen.roll(randomizer) if chosen.respond_to?(:roll)
28
+
29
+ return RollResult.new(@name, value, chosen)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ module BCDice
2
+ module DiceTable
3
+ # D66を振って6x6マスの表を参照する
4
+ class D66GridTable
5
+ # @param [String] name 表の名前
6
+ # @param [Array<Array<String>>] items 表の項目の配列
7
+ def initialize(name, items)
8
+ @name = name
9
+ @items = items.freeze
10
+ end
11
+
12
+ # 表を振る
13
+ # @param randomizer [#roll_once] ランダマイザ
14
+ # @return [String] 結果
15
+ def roll(randomizer)
16
+ dice1 = randomizer.roll_once(6)
17
+ dice2 = randomizer.roll_once(6)
18
+ value = dice1 * 10 + dice2
19
+
20
+ index1 = dice1 - 1
21
+ index2 = dice2 - 1
22
+ return RollResult.new(@name, value, @items[index1][index2])
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module BCDice
2
+ module DiceTable
3
+ # 項目をRangeを用いて参照するD66表
4
+ class D66RangeTable
5
+ # @param name [String] 表の名前
6
+ # @param items [Array<(Range, String)>] 表の項目の配列
7
+ def initialize(name, items)
8
+ @name = name
9
+ @items = items.freeze
10
+ end
11
+
12
+ # 表を振る
13
+ # @param randomizer [#roll_once] ランダマイザ
14
+ # @return [String] 結果
15
+ def roll(randomizer)
16
+ dice1 = randomizer.roll_once(6)
17
+ dice2 = randomizer.roll_once(6)
18
+
19
+ key = dice1 * 10 + dice2
20
+
21
+ chosen = @items.find { |row| row[0].include?(key) }
22
+ return RollResult.new(@name, key, chosen[1])
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ module BCDice
2
+ module DiceTable
3
+ # D66を振って出目を昇順/降順にして表を参照する
4
+ class D66Table
5
+ # @param [String] name 表の名前
6
+ # @param [Symbol] sort_type 出目入れ替えの方式 BCDice::D66SortType
7
+ # @param [Hash] items 表の項目 Key は数値
8
+ def initialize(name, sort_type, items)
9
+ @name = name
10
+ @sort_type = sort_type
11
+ @items = items.freeze
12
+ end
13
+
14
+ # 表を振る
15
+ # @param randomizer [#roll_barabara] ランダマイザ
16
+ # @return [String] 結果
17
+ def roll(randomizer)
18
+ dice = randomizer.roll_barabara(2, 6)
19
+
20
+ case @sort_type
21
+ when D66SortType::ASC
22
+ dice.sort!
23
+ when D66SortType::DESC
24
+ dice.sort!.reverse!
25
+ end
26
+
27
+ key = dice[0] * 10 + dice[1]
28
+ chosen = @items[key]
29
+ chosen = chosen.roll(randomizer) if chosen.respond_to?(:roll)
30
+ return RollResult.new(@name, key, chosen)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BCDice
4
+ module DiceTable
5
+ # 各項目について、Rangeを用いて出目の合計の範囲を指定する、表のクラス。
6
+ #
7
+ # このクラスを使うと、表の定義を短く書ける。
8
+ # このクラスを使って表を定義するときは、各項目を以下の形で書く。
9
+ #
10
+ # [出目の合計の範囲, 内容]
11
+ #
12
+ # 「出目の合計の範囲」には、Integerを要素とするRangeか、Integerを置ける。
13
+ #
14
+ # roll メソッドで表を振ると、出目の合計値と対応する項目が選ばれる。
15
+ #
16
+ # @example 表の定義(バトルテックの致命的命中表)
17
+ # CRITICAL_TABLE = RangeTable.new(
18
+ # '致命的命中表',
19
+ # '2D6',
20
+ # [
21
+ # [2..7, '致命的命中はなかった'],
22
+ # [8..9, '1箇所の致命的命中'],
23
+ # [10..11, '2箇所の致命的命中'],
24
+ # [12, 'その部位が吹き飛ぶ(腕、脚、頭)または3箇所の致命的命中(胴)']
25
+ # ]
26
+ # )
27
+ #
28
+ # @example 表を振った結果
29
+ # CRITICAL_TABLE.roll(bcdice).formatted
30
+ # # 出目の合計が7の場合 :"致命的命中表(7) > 致命的命中はなかった"
31
+ # # 出目の合計が8の場合 :"致命的命中表(8) > 1箇所の致命的命中"
32
+ # # 出目の合計が9の場合 :"致命的命中表(9) > 1箇所の致命的命中"
33
+ # # 出目の合計が10の場合:"致命的命中表(10) > 2箇所の致命的命中"
34
+ class RangeTable
35
+ # 表を振った結果を表す構造体
36
+ # @!attribute [rw] sum
37
+ # @return [Integer] 出目の合計
38
+ # @!attribute [rw] values
39
+ # @return [Array<Integer>] 出目の配列
40
+ # @!attribute [rw] content
41
+ # @return [Object] 選ばれた項目の内容
42
+ # @!attribute [rw] formatted
43
+ # @return [String] 整形された結果
44
+ RollResult = Struct.new(:sum, :values, :content, :formatted) do
45
+ alias_method :to_s, :formatted
46
+ end
47
+
48
+ # 表の項目を表す構造体
49
+ # @!attribute [rw] range
50
+ # @return [Range] 出目の合計の範囲
51
+ # @!attribute [rw] content
52
+ # @return [Object] 内容
53
+ Item = Struct.new(:range, :content)
54
+
55
+ # 項目を選ぶときのダイスロールの方法を表す正規表現
56
+ DICE_ROLL_METHOD_RE = /\A(\d+)D(\d+)\z/i.freeze
57
+
58
+ # 表を振った結果の整形処理(既定の処理)
59
+ DEFAULT_FORMATTER = lambda do |table, result|
60
+ "#{table.name}(#{result.sum}) > #{result.content}"
61
+ end
62
+
63
+ # @return [String] 表の名前
64
+ attr_reader :name
65
+ # @return [Integer] 振るダイスの個数
66
+ attr_reader :num_of_dice
67
+ # @return [Integer] 振るダイスの面数
68
+ attr_reader :num_of_sides
69
+
70
+ # 表を初期化する
71
+ #
72
+ # ブロックを与えると、独自の結果整形処理を指定できる。
73
+ # ブロックは振った表(+table+)と振った結果(+result+)を引数として受け取る。
74
+ #
75
+ # @param [String] name 表の名前
76
+ # @param [String] dice_roll_method
77
+ # 項目を選ぶときのダイスロールの方法(+'1D6'+ など)
78
+ # @param [Array<(Range, Object)>, Array<(Integer, Object)>] items
79
+ # 表の項目の配列。[出目の合計の範囲, 内容]
80
+ # @yieldparam [RangeTable] table 振った表
81
+ # @yieldparam [RollResult] result 表を振った結果
82
+ # @raise [ArgumentError] ダイスロール方法が正しい書式で指定されていなかった場合
83
+ # @raise [TypeError] 範囲の型が正しくなかった場合
84
+ # @raise [RangeError] 出目の合計の最小値がカバーされていなかった場合
85
+ # @raise [RangeError] 出目の合計の最大値がカバーされていなかった場合
86
+ # @raise [RangeError] 出目の合計の範囲にずれや重なりがあった場合
87
+ #
88
+ # @example 表の定義(バトルテックの致命的命中表)
89
+ # CRITICAL_TABLE = RangeTable.new(
90
+ # '致命的命中表',
91
+ # '2D6',
92
+ # [
93
+ # [2..7, '致命的命中はなかった'],
94
+ # [8..9, '1箇所の致命的命中'],
95
+ # [10..11, '2箇所の致命的命中'],
96
+ # [12, 'その部位が吹き飛ぶ(腕、脚、頭)または3箇所の致命的命中(胴)']
97
+ # ]
98
+ # )
99
+ #
100
+ # @example 独自の結果整形処理を指定する場合
101
+ # CRITICAL_TABLE_WITH_FORMATTER = RangeTable.new(
102
+ # '致命的命中表',
103
+ # '2D6',
104
+ # [
105
+ # [2..7, '致命的命中はなかった'],
106
+ # [8..9, '1箇所の致命的命中'],
107
+ # [10..11, '2箇所の致命的命中'],
108
+ # [12, 'その部位が吹き飛ぶ(腕、脚、頭)または3箇所の致命的命中(胴)']
109
+ # ]
110
+ # ) do |table, result|
111
+ # "致命的命中発生? > #{result.sum}[#{result.values}] > #{result.content}"
112
+ # end
113
+ #
114
+ # CRITICAL_TABLE_WITH_FORMATTER.roll(bcdice).formatted
115
+ # #=> "致命的命中発生? > 11[5,6] > 2箇所の致命的命中"
116
+ def initialize(name, dice_roll_method, items, &formatter)
117
+ @name = name.freeze
118
+ @formatter = formatter || DEFAULT_FORMATTER
119
+
120
+ m = DICE_ROLL_METHOD_RE.match(dice_roll_method)
121
+ unless m
122
+ raise(
123
+ ArgumentError,
124
+ "#{@name}: invalid dice roll method: #{dice_roll_method}"
125
+ )
126
+ end
127
+
128
+ @num_of_dice = m[1].to_i
129
+ @num_of_sides = m[2].to_i
130
+
131
+ store(items)
132
+ end
133
+
134
+ # 指定された値に対応する項目を返す
135
+ # @param [Integer] value 値(出目の合計)
136
+ # @return [Item] 指定された値に対応する項目
137
+ # @raise [RangeError] 範囲外の値が指定された場合
138
+ def fetch(value)
139
+ item = @items.find { |i| i.range.include?(value) }
140
+ unless item
141
+ raise RangeError, "#{@name}: value is out of range: #{value}"
142
+ end
143
+
144
+ return item
145
+ end
146
+
147
+ # 表を振る
148
+ # @param randomizer [#roll_barabara] ランダマイザ
149
+ # @return [RollResult] 表を振った結果
150
+ def roll(randomizer)
151
+ values = randomizer.roll_barabara(@num_of_dice, @num_of_sides)
152
+ sum = values.sum()
153
+
154
+ result = RollResult.new(sum, values, fetch(sum).content)
155
+ result.formatted = @formatter[self, result]
156
+
157
+ return result
158
+ end
159
+
160
+ private
161
+
162
+ # 表の項目を格納する
163
+ # @param [Array<(Range, Object)>, Array<(Integer, Object)>] items
164
+ # 表の項目の配列。[出目の合計の範囲, 内容]
165
+ # @return [self]
166
+ # @raise [TypeError] 範囲の型が正しくなかった場合
167
+ # @raise [RangeError] 出目の合計の最小値がカバーされていなかった場合
168
+ # @raise [RangeError] 出目の合計の最大値がカバーされていなかった場合
169
+ # @raise [RangeError] 出目の合計の範囲にずれや重なりがあった場合
170
+ def store(items)
171
+ items_with_range = items.map { |r, c| [coerce_to_int_range(r), c] }
172
+ sorted_items = items_with_range.sort_by { |r, _| r.min }
173
+
174
+ assert_min_sum_is_covered(sorted_items)
175
+ assert_max_sum_is_covered(sorted_items)
176
+ assert_no_gap_or_overlap_in_ranges(sorted_items)
177
+
178
+ @items = sorted_items
179
+ .map { |range, content| Item.new(range, content.freeze).freeze }
180
+ .freeze
181
+
182
+ self
183
+ end
184
+
185
+ # 引数を強制的に整数を要素とするRangeに変換する
186
+ # @param [Range, Integer] x 変換対象
187
+ # @return [Range] 整数を要素とするRange
188
+ # @raise [TypeError] xの型に対応していなかった場合
189
+ def coerce_to_int_range(x)
190
+ case x
191
+ when Integer
192
+ return Range.new(x, x)
193
+ when Range
194
+ if x.begin.is_a?(Integer) && x.end.is_a?(Integer)
195
+ return x
196
+ end
197
+ end
198
+
199
+ raise(
200
+ TypeError,
201
+ "#{@name}: #{x} (#{x.class}) must be an Integer or a Range with Integers "
202
+ )
203
+ end
204
+
205
+ # 出目の合計の最小値がカバーされていることを確認する
206
+ # @param [Array<(Range, Object)>] sorted_items
207
+ # ソートされた、項目の配列
208
+ # @return [self]
209
+ # @raise [RangeError] 出目の合計の最小値がカバーされていなかった場合
210
+ def assert_min_sum_is_covered(sorted_items)
211
+ min_sum = @num_of_dice
212
+ range = sorted_items.first[0]
213
+ unless range.include?(min_sum)
214
+ raise(
215
+ RangeError,
216
+ "#{@name}: min value (#{min_sum}) is not covered: #{range}"
217
+ )
218
+ end
219
+
220
+ self
221
+ end
222
+
223
+ # 出目の合計の最大値がカバーされていることを確認する
224
+ # @param [Array<(Range, Object)>] sorted_items
225
+ # ソートされた、項目の配列
226
+ # @return [self]
227
+ # @raise [RangeError] 出目の合計の最大値がカバーされていなかった場合
228
+ def assert_max_sum_is_covered(sorted_items)
229
+ max_sum = @num_of_dice * @num_of_sides
230
+ range = sorted_items.last[0]
231
+ unless range.include?(max_sum)
232
+ raise(
233
+ RangeError,
234
+ "#{@name}: max value (#{max_sum}) is not covered: #{range}"
235
+ )
236
+ end
237
+
238
+ self
239
+ end
240
+
241
+ # 出目の合計の範囲にずれや重なりがないことを確認する
242
+ # @param [Array<(Range, Object)>] sorted_items
243
+ # ソートされた、項目の配列
244
+ # @return [self]
245
+ # @raise [RangeError] 出目の合計の範囲にずれや重なりがあった場合
246
+ def assert_no_gap_or_overlap_in_ranges(sorted_items)
247
+ sorted_items.each_cons(2) do |i1, i2|
248
+ r1 = i1[0]
249
+ r2 = i2[0]
250
+
251
+ max1 = r1.max
252
+ next_of_max1 = max1 + 1
253
+
254
+ if r2.include?(max1)
255
+ raise RangeError, "#{@name}: Range overlap: #{r1} and #{r2}"
256
+ end
257
+
258
+ unless r2.include?(next_of_max1)
259
+ raise RangeError, "#{@name}: Range gap: #{r1} and #{r2}"
260
+ end
261
+ end
262
+
263
+ self
264
+ end
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,43 @@
1
+ module BCDice
2
+ module DiceTable
3
+ class RollResult
4
+ # @param table_name [String]
5
+ # @param value [Integer]
6
+ # @param body [String, RollResult]
7
+ def initialize(table_name, value, body)
8
+ @table_name = table_name
9
+ @value = value
10
+ @body = body
11
+ end
12
+
13
+ # @return [String]
14
+ attr_reader :table_name
15
+
16
+ # @return [Integer]
17
+ attr_reader :value
18
+
19
+ # @return [String, RollResult]
20
+ attr_reader :body
21
+
22
+ # @return [String]
23
+ def to_s
24
+ "#{@table_name}(#{@value}) > #{@body}"
25
+ end
26
+
27
+ # @return [String]
28
+ def last_body
29
+ if @body.is_a?(RollResult)
30
+ @body.last_body
31
+ else
32
+ @body
33
+ end
34
+ end
35
+
36
+ # 一部のゲームシステムが String#empty? を想定してチェックしているため
37
+ # @return [false]
38
+ def empty?
39
+ false
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ module BCDice
2
+ module DiceTable
3
+ class SaiFicSkillTable
4
+ def initialize(items, peekaboo_style: false)
5
+ @items = items
6
+ @peekaboo_style = peekaboo_style
7
+ end
8
+
9
+ def roll(randomizer)
10
+ category_dice = randomizer.roll_once(6)
11
+ cindex = category_dice - 1
12
+ category, skill_list = @items[cindex]
13
+
14
+ skill_dice = randomizer.roll_sum(2, 6)
15
+ sindex = skill_dice - 2
16
+ skill = skill_list[sindex]
17
+
18
+ return "ランダム指定特技表(#{category_dice},#{skill_dice}) > #{result(category, skill, skill_dice)}"
19
+ end
20
+
21
+ private
22
+
23
+ def result(category, skill, skill_dice)
24
+ if @peekaboo_style
25
+ "《#{skill}/#{category}#{skill_dice}》"
26
+ else
27
+ "#{category}《#{skill}》"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end