├── .gitignore ├── random-tables ├── example-primary-color.txt ├── example-paint-buckets.txt ├── example-monster.txt └── example-dragon.txt ├── test-decide.el ├── README.org └── decide.el /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.elc 3 | -------------------------------------------------------------------------------- /random-tables/example-primary-color.txt: -------------------------------------------------------------------------------- 1 | ;main 2 | red 3 | 2,green 4 | blue 5 | yellow 6 | -------------------------------------------------------------------------------- /random-tables/example-paint-buckets.txt: -------------------------------------------------------------------------------- 1 | ;main 2 | 4,[2-4] buckets of [example-primary-color] paint 3 | [1d3+2] empty paint buckets 4 | -------------------------------------------------------------------------------- /random-tables/example-monster.txt: -------------------------------------------------------------------------------- 1 | ;main 2 | [1d6+1] orcs 3 | [3d6+1] kobolds 4 | [2<<<20] goblins 5 | [2>>5] small goblins 6 | level [1--10] hero 7 | [example-dragon] 8 | [example-dragon.prefix]monster 9 | -------------------------------------------------------------------------------- /random-tables/example-dragon.txt: -------------------------------------------------------------------------------- 1 | ;main 2 | [type] 3 | 2,[adj] [type] 4 | [noun] [type] 5 | 3,[adj] [noun] [type] 6 | [prefix]-[type] 7 | [adj] [prefix][type] 8 | 9 | ;type 10 | 25,dragon 11 | wyrm 12 | wyvern 13 | 14 | ;adj 15 | [2d4]-headed 16 | [example-primary-color] 17 | ancient 18 | cunning 19 | cute 20 | epic 21 | huge 22 | old 23 | royal 24 | small 25 | undead 26 | wise 27 | young 28 | gargantuan 29 | gothic 30 | serpentine 31 | volcanic 32 | 33 | ;prefix 34 | lich 35 | semi 36 | shadow 37 | 38 | ;noun 39 | ice 40 | gold 41 | silver 42 | ethereal 43 | storm 44 | metal 45 | star 46 | -------------------------------------------------------------------------------- /test-decide.el: -------------------------------------------------------------------------------- 1 | ;;; test-decide.el --- unit tests for rolling dice and other random things 2 | ;; Copyright 2023-2024 Pelle Nilsson 3 | ;; 4 | ;; Author: Pelle Nilsson 5 | ;; Version: 0.10.0 6 | ;; 7 | ;; This program is free software: you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation, either version 3 of the License, or 10 | ;; (at your option) any later version. 11 | ;; 12 | ;; This program is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | ;; 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program. If not, see . 19 | ;; 20 | 21 | (ert-deftest decide-test-table-top-table-name () 22 | (should (equal (decide-table-top-table-name "foo.bar") "foo")) 23 | (should (equal (decide-table-top-table-name "foo") "foo")) 24 | ) 25 | 26 | (ert-deftest decide-test-table-normalize-name () 27 | (should (equal (decide-table-normalize-name "top" "aa.bb") "aa.bb")) 28 | (should (equal (decide-table-normalize-name "top" "aa") "top.aa")) 29 | ) 30 | 31 | (ert-deftest decide-test-string-to-number () 32 | (should (equal (decide-string-to-number "" 1) 1)) 33 | (should (equal (decide-string-to-number "" 2) 2)) 34 | (should (equal (decide-string-to-number "0" 1) 0)) 35 | (should (equal (decide-string-to-number "+" 1) 0)) 36 | (should (equal (decide-string-to-number "-" 1) 0)) 37 | (should (equal (decide-string-to-number "x" 1) "x")) 38 | (should (equal (decide-string-to-number "3" 1) 3)) 39 | (should (equal (decide-string-to-number "-1" 1) -1)) 40 | (should (equal (decide-string-to-number "A" 1) "A")) 41 | (should (equal (decide-string-to-number nil 1) 1)) 42 | ) 43 | 44 | (ert-deftest decide-test-make-dice-spec () 45 | (should (equal (decide-make-dice-spec "1d6") '(1 6 0))) 46 | (should (equal (decide-make-dice-spec "1d") '(1 6 0))) 47 | (should (equal (decide-make-dice-spec "d6") '(1 6 0))) 48 | (should (equal (decide-make-dice-spec "d") '(1 6 0))) 49 | (should (equal (decide-make-dice-spec "1d66") '(1 66 0))) 50 | (should (equal (decide-make-dice-spec "1d") '(1 6 0))) 51 | (should (equal (decide-make-dice-spec "1d8") '(1 8 0))) 52 | (should (equal (decide-make-dice-spec "2d6") '(2 6 0))) 53 | (should (equal (decide-make-dice-spec "dA") '(1 "A" 0))) 54 | (should (equal (decide-make-dice-spec "4dF") '(4 "F" 0))) 55 | (should (equal (decide-make-dice-spec "2d6+4") '(2 6 4))) 56 | (should (equal (decide-make-dice-spec "2d8+0") '(2 8 0))) 57 | (should (equal (decide-make-dice-spec "2d10+3") '(2 10 3))) 58 | (should (equal (decide-make-dice-spec "2d10-3") '(2 10 -3))) 59 | ) 60 | 61 | (ert-deftest decide-test-describe-dice-spec () 62 | (should (equal (decide-describe-dice-spec '(1 6 0)) "1d6")) 63 | (should (equal (decide-describe-dice-spec '(1 6 1)) "1d6+1")) 64 | (should (equal (decide-describe-dice-spec '(4 "f" 0)) "4dF")) 65 | (should (equal (decide-describe-dice-spec '(8 "B5" -1)) "8dB5-1")) 66 | ) 67 | 68 | (ert-deftest decide-test-sum-dice-rolled () 69 | (should (equal (decide-sum-dice-rolled '((1 "1")) 0) 1)) 70 | (should (equal (decide-sum-dice-rolled '((1 "1") (2 "2")) 0) 3)) 71 | (should (equal (decide-sum-dice-rolled '((1 "1")) 1) 2)) 72 | (should (equal (decide-sum-dice-rolled '((1 "1") (2 "2")) 1) 4)) 73 | ) 74 | 75 | (ert-deftest decide-test-describe-roll () 76 | (should (equal (decide-describe-roll '((1 "1"))) "1")) 77 | (should (equal (decide-describe-roll '((1 "1")(2 "2"))) "1 2")) 78 | (should (equal (decide-describe-roll '((1 "+")(-1 "-"))) "+ -")) 79 | ) 80 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * decide-mode.el 2 | Random decisions for Emacs, with functions and a minor mode. Roll dice and 3 | generate random numbers and text in a few other ways. Mostly inspired by tools 4 | and activities related to role-playing games, but may be useful beyond that. 5 | ** Examples (Quick Overview) 6 | There is a sequence of keys to press, starting with a question-mark, and some 7 | functions will prompt for more input. This is not a complete list of all 8 | functions. 9 | |-----------------------+-------+----------------+-----------------| 10 | | What to decide | Keys | Input | Example Output | 11 | |-----------------------+-------+----------------+-----------------| 12 | | Yes or no | ? ? | | YES | 13 | | ... probably yes | ? + | | YES | 14 | | ... probably no | ? - | | NO | 15 | | Roll 1d6 | ? 6 | | 3 | 16 | | Roll 2d6 | ? D | | (5 1) = 6 | 17 | | Roll 1d20 | ? 2 0 | | 20 | 18 | | Roll dice (general) | ? d | 2d6+1 | (5 3) +1 = 9 | 19 | | Roll four FATE dice | ? F | | (- 0 + +) = 1 | 20 | | Roll 5d6 and count 6s | ? d | 5dB6 | (6 3 2 6 4) = 2 | 21 | | Number from range | ? r | 1-10 | 5 | 22 | | ... low likely | ? r | 1<<10 | 4 | 23 | | ... low more likely | ? r | 1<<<<10 | 1 | 24 | | ... high likely | ? r | 1>>10 | 7 | 25 | | Choose from list | ? c | apple,orange | orange | 26 | | Random dir (of 4) | ? w 4 | | N | 27 | | Random dir (of 8) | ? w 8 | | NW | 28 | | Use random table | ? t | example-dragon | wise ice dragon | 29 | |-----------------------+-------+----------------+-----------------| 30 | ** Introduction 31 | Random results from /decide-mode/ are inserted at point in the current buffer if 32 | possible, otherwise only echoed in the mini-buffer. The default keyboard 33 | bindings for the minor-mode are intended to be convenient to trigger as part of 34 | typing regular text, by pressing ? followed by various other keys. 35 | 36 | A ? followed by space, enter, or a closing parenthesis will insert the ? itself 37 | and the next key as expected, so normally /decide-mode/ should not interfere 38 | with typing. 39 | 40 | The default binding of "? ?" (since version 0.7) is /decide-dwim-insert/, a function 41 | that looks in the buffer immediately before point and if it finds a dice 42 | or range specification there it will replace it with the result of rolling 43 | the dice or picking a random number from the range. If no dice or range 44 | is found it will fall back to the normal /decide-for-me-normal/ function that 45 | outputs a simple YES or NO result. 46 | 47 | See the decide.el file itself for some documentation and a list of all the 48 | keybindings in /decide-mode/. There is an overview of important features 49 | below, but all the details and more features can be found in the source. 50 | ** Yes or No 51 | The simplest function of /decide-mode/ is /decide-for-me-normal/ that will insert 52 | a /Yes/ or /No/ with equal probability. By default it is triggered by pressing 53 | "? ?", unless what is in the buffer just before point is a valid 54 | dice-specification or range (see descriptions of those below), in which case 55 | that will be used instead. 56 | 57 | To get a Yes only with 33% probability use "? -" (/decide-for-me-unlikely/) 58 | and for 67% probability "? +" (/decide-for-me-likely/). 59 | 60 | When inserting a Yes or No using these functions there will sometimes, randomly, 61 | be a + or - inserted as well. Those can be ignored or interpreted as for 62 | instance "and" and "or", for use in some RPG systems. The probability of a + 63 | or - appearing will depend on which of the functions is called (see the 64 | confusingly named function /decide-for-me-dice/ for the exact probabilities). 65 | ** Installation 66 | Load the file /decide.el/ (e.g. /(load-file "~/.emacs.d/lisp/decide.el")) and 67 | then enable it in a buffer by running /M-x decide-mode/. 68 | 69 | To always have it enabled for a specific major-mode, add it to the appropriate 70 | hook, e.g.: 71 | #+BEGIN_EXAMPLE 72 | (defun my-org-mode-hook () 73 | (decide-mode 1)) 74 | (add-hook 'org-mode-hook 'my-org-mode-hook)) 75 | #+END_EXAMPLE 76 | 77 | To only enable it for a specific file, check the documentation for [[https://www.gnu.org/software/emacs/manual/html_node/emacs/File-Variables.html][GNU Emacs 78 | File Variables]], and of course it can also be [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html][for an entire directory]]. 79 | ** Die Rolls 80 | Die-rolls are specified in common dice-notation, like 3d6 for rolling 3 81 | 6-sided dice, or 2d4+1 to roll 2 4-sided dice and add 1 to the result. 82 | If the number of dice is not given (e.g. d10) it is assumed to be a single die. 83 | If the number of sides is not given (e.g. 2d) a standard d6 is assumed, 84 | but that can be configured in the variable /decide-default-dice-faces/. 85 | 86 | There are several ways to roll dice using /decide-mode/. The interactive 87 | function /decide-roll-dice/ will ask for a dice specification and then 88 | immediately insert the results of rolling that. The default keybinding "? d" 89 | will call that function. There are also some bindings that are "?" followed by 90 | one or more digits that will roll a single die immediately, e.g. "? 8" to roll 91 | 1d8 or "? 2 0" to roll 1d20. See the list at the end of decide.el. A few special 92 | bindings are also included, like "? %" to 1d100 and "? f" to roll 4dF. Common 93 | 1d6 can be rolled with "? 6", or 2d6 with "? D". 94 | 95 | Dice are also rolled when pressing "? ?" (/decide-dwim-insert/) if point is 96 | immediately preceded by what can be parsed as a valid dice-specification. 97 | 98 | It is also possible to use custom named dice that can have any number of sides 99 | that has any text on. Each side must still be given a value, as the sum of all 100 | rolled dice is always printed. Custom dice are specified in the variable 101 | /decide-custom-dice/ Included by default are dice F (for Fudge/Fate dice), S 102 | (results in Fail or Success), B6 (for rolling a bucket of dice, counting 6s as 103 | hits), B5 (like B6 but hits on 5-6), B4 (you get the idea), B3, B2, and A 104 | (average dice, a 6-sided die labelled 2, 3, 3, 4, 4, 5). To roll a custom die 105 | just use its name where the number of sides of a die would normally be, e.g. 4dF 106 | to roll 4 Fudge/Fate dice. 107 | 108 | Custom dice can be given names of any length, but case is ignored, so a dF is 109 | the same as a df. It is thus not possible to specify dice that have the same 110 | names only differing in case. 111 | ** Random Ranges 112 | Ranges are a simpler way to specify a range of random numbers than to use dice. 113 | The default key-binding for ranges is "? r". The simplest range is just n-m, 114 | where n and m are integers (m must be positive) and m is greater than n. The 115 | inserted random number will be from n to m, inclusive. The range 1-6 is 116 | equivalent to rolling a standard 1d6. 117 | 118 | Ranges also support some special syntax to make some numbers in the range 119 | more likely. Including more dashes between the numbers makes results 120 | in the middle of the range more likely by effectively generating as many 121 | random values as the number of dashes and then picking the median of 122 | all those numbers. The range 10---20 for instance would insert a number 123 | from 10 to 20, but numbers close to 15 would be more likely. Using 124 | greater-than or lower-than characters instead between the numbers will 125 | tend towards higher or lower numbers, by generating as many numbers 126 | as indicated and then picking the highest or lowest, so 1>>6 is effectively 127 | like rolling 2d6 and picking the highest rolled. It makes no sense to use 128 | only a single < or >. 129 | 130 | The default binding for "? ?" will generate a random number from a range 131 | if point is immediately preceded by what can be parsed as a valid range 132 | specification. 133 | ** Random Choices 134 | The function /decide-random-choice/, by default bound to "? c", asks for 135 | a comma-separated list of things and then inserts one of those chosen 136 | at random. A random direction can for instance be requested by pressing 137 | "? c" and then "N,S,W,E" or a random color with "red,blue,green". 138 | 139 | Some lists of choices are available at default key-bindings that can be seen at 140 | the end of the decide.el file. For instance the random directions in the last 141 | paragraph are available by pressing "? w 4" and there are several other similar 142 | bindings included. 143 | 144 | See the Random Tables below for a more powerful way of asking for a random 145 | choice. A random choice using "? c" is good for a quick ad-hoc choice as it does 146 | not require setting up a table with choices. Choices are saved in mini-buffer 147 | history, and thus easily available to make new similar choices again until Emacs 148 | is restarted, but for more permanent lists of things to draw from it is better 149 | to create a table or set up a key-binding to call /decide-random-choice/ (see 150 | the functions at the end of decide.el for how to do that easily). 151 | ** Random Tables 152 | The function /decide-from-table/, by default bound to "? t" when /decide-mode/ is 153 | enabled, inserts random text generated from the table in variable /decide-table/. 154 | The table can be set up using elisp, but since version 0.8 there is a new, simpler, 155 | way of setting up the tables by using plain-text files. Text is generated by 156 | starting from a given table and picking a random phrase from that table. Phrases 157 | can be weighted to make some more likely to be chosen. A phrase can also contain 158 | references to other tables, which will be substituted by a random phrase from 159 | that table. It is also possible to insert die-rolls and random numbers from 160 | a given range. 161 | 162 | The functions /decide-table-load-file/ and /decide-table-load-dir/ can be used 163 | to load random tables from text files into the /decide-tables/ variable. The 164 | latter recursively loads all files in a directory, while the former only loads a 165 | single file. Each file contains a single table, with one phrase per line. 166 | Weights are set by prefixing a line with a number and a comma, with no 167 | white-space before or after. 168 | 169 | A line that begins with a semicolon begins a new table, and the name of the 170 | table is the text on the line after the semicolon. As a special case if 171 | the name is /main/ (case-insensitie match) instead the name of the file 172 | (sans extension) will be used for that table. 173 | 174 | To refer to another table from a phrase, include the name of that table 175 | in brackets. Die-rolls can similarly be inserted by putting it in brackets. 176 | To get a simple random number in a range from n to m use [n-m], but 177 | it is also possible to use more advanced range-specifiers as described 178 | 179 | References to other tables are first searched for in the same file. 180 | If no table matches in the file all file-names are searched instead. 181 | A name like /fff.ttt/ can be used for table /ttt/ that was declared in 182 | file /fff/. 183 | 184 | A simple table to generate a primary color, with green being twice 185 | as likely to be chosen could look like this (in file /primary-color.txt/): 186 | #+BEGIN_EXAMPLE 187 | ;main 188 | red 189 | 2,green 190 | blue 191 | #+END_EXAMPLE 192 | 193 | The following table in another file, /paint-buckets.txt/ 194 | refers to the previous table, and it also makes use of both 195 | a random range and a die-roll, where one of the 196 | phrases will be used four times as often as the other: 197 | #+BEGIN_EXAMPLE 198 | ;main 199 | 4,[2-4] buckets of [primary-color] paint 200 | [1d3+2] empty paint buckets 201 | #+END_EXAMPLE 202 | 203 | The *random-tables* sub-directory contains a few example tables. 204 | The /decides-tables/ variable by default includes tables "card", "card.suit", 205 | and "card.rank" that can be used to draw random cards from an infinite deck. To 206 | load the other examples (all with names prefixed "example-") use the 207 | /decide-table-load-dir/ function. It makes sense to gather all table files in a 208 | directory and put a single call like 209 | /(decide-table-load-dir "~/.emacs.d/random-generators")/ 210 | in emacs' init script to make sure all tables are always loaded. 211 | 212 | Blank lines in table files are ignored. If a blank phrase is needed (as in the 213 | /random-tables/example-dragon-prefix.txt/ file) a weight can be used alone on a 214 | line, just a number followed by a comma. Lines beginning with a # are comments 215 | and are ignored. 216 | 217 | The format of the table files is based on the format used by [[http://www.random-generator.com/][Abulafia (random-generator.com)]], 218 | that is a site containing thousands of random-generator tables, but lacking 219 | most of the advanced functions on that site. (NOTE: It seems like Abulafia 220 | has had issues for many years now, and currently (August 2024) the site seems 221 | broken with just error messages and almost empty pages). The format is 222 | also similar to the format used for [[https://campaignwiki.org/hex-describe][Hex Describe's rules]] (but lacking 223 | most of the features supported by that generator). 224 | ** Customizing Output 225 | Since version 0.10.0 there is a variable /decide-output-format/ that 226 | can be used to control what is inserted by decide-mode functions. 227 | 228 | If set to a string it is used as the format-string for a call to 229 | the standard emacs-lisp format function, that will also receive 230 | two strings: /what/ is being generated (e.g. a dice-specifier or 231 | name of a table) and the /result/ that was generated. The format-string 232 | can use %1$s and %2$s respectively to refer to the /what/ and /result/ 233 | strings (or use %s for both if used in that order). 234 | 235 | For example to only output the generated result (without describing 236 | what was generated) and add a single trailing space: 237 | #+BEGIN_EXAMPLE 238 | (setq decide-output-format "%2$s ") 239 | #+END_EXAMPLE 240 | 241 | This outputs the result only, and also makes it appear 242 | bold in org-mode: 243 | #+BEGIN_EXAMPLE 244 | (setq decide-output-format "**%2$s** ") 245 | #+END_EXAMPLE 246 | 247 | If set to a function that function will be called with the two 248 | string arguments /what/ and /result/ and is expected to return 249 | the string to be inserted. If nil is returned nothing is inserted 250 | (other than what the function itself may already have inserted). 251 | 252 | When creating custom functions for decide-mode, for instance to when binding a 253 | key to roll some special dice, note that /decide-output-format/ can be 254 | bound using /let/ around calls to /decide-mode/ functions to control the way the 255 | results are inserted without affecting the global (or buffer-local) 256 | configuration. 257 | ** Use in yasnippets 258 | Not a feature of /decide-mode/ itself, but it is easy to embed calls to 259 | various /decide-/ functions in yasnippet files. A random table 260 | set up to generate Victorian names (hint: you can easily make one based on [[http://www.random-generator.com/index.php?title=Victorian_Names&action=edit][the 261 | random Victorian names table on Abulafia]], but doing that is left as an exercise) 262 | can be used in a yasnippet snippet like this: 263 | #+BEGIN_EXAMPLE 264 | `(decide-choose-from-table "names-victorian")`. 265 | #+END_EXAMPLE 266 | ** Versions 267 | *** 0.10.0 2024-10-13 268 | - decide-output-format variable to control output format 269 | - Support +0 dice modifier bugfix (thanks wkmanire) 270 | - Bugfixed and cleaned up dice spec parser in general 271 | - ERT tests added 272 | - Added bucket-of-dice custom dice B2, B3, B4, B5, B6 273 | *** 0.9.1 2022-07-18 274 | - Updated README with changelog for 0.9.0. 275 | *** 0.9.0 2022-07-18 276 | - Multiple tables in the same file 277 | - Tables with name beginning with dash no longer hidden 278 | (but all tables other than the first one in each file is) 279 | - Fixes decide-roll-die-nonempty typo (thanks thezeroalpha) 280 | - New helper function decide-string-from-table 281 | *** 0.8 2021-11-28 282 | - Weighted decide-tables 283 | - Parse decide-tables from text-files 284 | - Default faces for dice (decide-default-dice-faces variable, default 6) 285 | - Success dice (S) to roll Success or Fail 286 | - Dice-specification parser more forgiving 287 | - Tables with new format only parsing parts in square brackets 288 | *** 0.7 2017-07-03 289 | - Do What I Mean (dice or range spec before ? ? in buffer) 290 | *** 0.6.2 2017-07-03 291 | - Fixed regression with parsing dice in tables. 292 | *** 0.6.1 2017-07-03 293 | - Cleaned up dice-parsing and improved error-handling. 294 | *** 0.6 2017-07-02 295 | - Custom dice can be added in decide-custom-dice variable. 296 | - Various minor fixes. 297 | *** 0.5 2016-03-06 298 | - First public version. 299 | ** Help 300 | Suggestions and bug-fixes are very much appreciated. This was 301 | my first attempt at writing a mode for Emacs, and I have very 302 | little experience with elisp (or any lisp) in general. 303 | ** License 304 | Copyright 2016, 2017, 2019, 2021-2024 Pelle Nilsson et al 305 | 306 | Author: Pelle Nilsson 307 | Version: 0.10.0 308 | 309 | This program is free software: you can redistribute it and/or modify 310 | it under the terms of the GNU General Public License as published by 311 | the Free Software Foundation, either version 3 of the License, or 312 | (at your option) any later version. 313 | 314 | This program is distributed in the hope that it will be useful, 315 | but WITHOUT ANY WARRANTY; without even the implied warranty of 316 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 317 | GNU General Public License for more details. 318 | 319 | You should have received a copy of the GNU General Public License 320 | along with this program. If not, see . 321 | -------------------------------------------------------------------------------- /decide.el: -------------------------------------------------------------------------------- 1 | ;;; decide.el --- rolling dice and other random things 2 | ;; Copyright 2016, 2017, 2019, 2021-2024 Pelle Nilsson et al 3 | ;; 4 | ;; Author: Pelle Nilsson 5 | ;; Version: 0.10.0 6 | ;; 7 | ;; This program is free software: you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation, either version 3 of the License, or 10 | ;; (at your option) any later version. 11 | ;; 12 | ;; This program is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | ;; 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with this program. If not, see . 19 | ;; 20 | ;;; Commentary: 21 | ;; 22 | ;; Use to make random decisions. Roll various types of dice, generate 23 | ;; random numbers from ranges, or generate random text from 24 | ;; tables. 25 | ;; 26 | ;; Enable decide-mode minor-mode. Pressing ? ? will insert a YES 27 | ;; or NO (possibly with a + or - modifier to be interpreted 28 | ;; any way you wish). 29 | ;; 30 | ;; If the answer is unlikely to be yes, press ? - 31 | ;; a more unlikely (difficult) query that has only a 33 % chance of being 32 | ;; yes. For a liklier/easier test press ? + (67 % chance). 33 | ;; 34 | ;; To roll generic dice, use the function decide-roll-dice. It will 35 | ;; ask for what roll to make, something like 2d6 or 3d10+2 or 2d12-1. 36 | ;; The default if nothing is input, or nothing that can be parsed 37 | ;; properly as a dice specification, 1d6 is rolled. 38 | ;; M-p and M-n can be used to navigate history to re-roll. 39 | ;; Rolling dice is bound to ? d when decide-mode is active. 40 | ;; Some common and less common die-rolls have their own key-bindings 41 | ;; enabled per default in decide-mode: 42 | ;; 43 | ;; ? 3 -> 1d3 44 | ;; ? 4 -> 1d4 45 | ;; ? 5 -> 1d5 46 | ;; ? 6 -> 1d6 47 | ;; ? 7 -> 1d7 48 | ;; ? 8 -> 1d8 49 | ;; ? 9 -> 1d9 50 | ;; ? 1 0 -> 1d10 51 | ;; ? 1 2 -> 1d12 52 | ;; ? 2 0 -> 1d20 53 | ;; ? % -> 1d100 54 | ;; ? D -> 2d6 55 | ;; 56 | ;; Custom dice can be defined in the decide-custom-dice alist. By default it 57 | ;; contains configuration for dA (average-dice, d6 numbered 2, 3, 3, 4, 4, 5) 58 | ;; and dF (Fudge/FATE dice, d6 labeled +, +, 0, 0, -, -). Custom dice names 59 | ;; are not case sensitive (avoid having dice with the same name only differing 60 | ;; in case). Each custom dice side has a string 61 | ;; label and an optional value that is used (if it exists) to calculate the sum 62 | ;; of rolling multiple dice of that type. There are some pre-defined 63 | ;; key-bindings in decide-mode for the included custom dice: 64 | ;; 65 | ;; ? f -> 4dF 66 | ;; ? a -> 1dA 67 | ;; ? A -> 2dA 68 | ;; 69 | ;; To pick a random number in any range press ? r (decide-random-range), 70 | ;; then input range to get number from, in one of the following formats: 71 | ;; 3-17 72 | ;; 3--17 73 | ;; 3---17 74 | ;; (even more dashes are allowed) 75 | ;; 3<<17 76 | ;; 3<<<17 77 | ;; 3>>17 78 | ;; 3>>>17 79 | ;; All ranges are inclusive (ie the two given numbers may be choosen). 80 | ;; The start of a range must be lower than the end of the range. 81 | ;; The end of a range can not be a negative number. 82 | ;; When having more than one dash between numbers, that means you will get 83 | ;; an average of that many random draws, meaning the result is more likely 84 | ;; to be close to the middle of the range. Adding more dashes makes it 85 | ;; increasingly unlikely to get results close to the extremes of the range. 86 | ;; Using << (or <<< etc) will instead result in the lowest of multiple 87 | ;; draws, tending towards the lower end of the range, 88 | ;; and the opposite is true when using >> (or >>> etc). 89 | ;; 90 | ;; To decide from a given list of possible choices press 91 | ;; ? c (decide-random-choice) 92 | ;; and input a comma-separated list of things to choose from. 93 | ;; There are also some pre-defined lists of choices that can be 94 | ;; accessed with the following shortcuts. 95 | ;; 96 | ;; ? w 4 -> decide-whereto-compass-4 (N,S,W,E) 97 | ;; ? w 6 -> decide-whereto-compass-6 (N,S,W,E,U,D) 98 | ;; ? w 8 -> decide-whereto-compass-8 (N,S,E,W,NE,NW,SE,SW) 99 | ;; ? w 1 0 -> decide-whereto-compass-10 (N,S,E,W,NE,NW,SE,SW,U,D) 100 | ;; ? W 2 -> decide-whereto-relative-2 (left,right) 101 | ;; ? W 3 -> decide-whereto-relative-3 (forward,left,right) 102 | ;; ? W 4 -> decide-whereto-relative-4 (forward,left,right,back) 103 | ;; ? W 6 -> decide-whereto-relative-6 (forward,left,right,back,up,down) 104 | ;; 105 | ;; It is also possible to pick random combinations of words taken from the 106 | ;; variable decide-tables using ? t (decide-from-table). decide-table is an 107 | ;; alist that gives lists of possible values for each 'table'. If the name of 108 | ;; another table in the alist is given in square brackets a random value from 109 | ;; that list will be substituted, and this is applied recursively (do not 110 | ;; include a reference to a table from a table referred to from that table!). A 111 | ;; valid dice-spec in square brackets is rolled with the result inserted. A 112 | ;; valid range in square brackets (as for decide-random-range) will result in a 113 | ;; random value from that range being inserted. If a word matches the name of a 114 | ;; table that table will be used to insert something at that position. A 115 | ;; possible expansion for a table name can have a weight added to make it more 116 | ;; likely to be choosen like ("dragon" . 3) (making it three times as likely to 117 | ;; be choosen as an expansion that has no weight given). The default-value for 118 | ;; decide-tables contains some examples to hopefully make all this a bit less 119 | ;; confusing. 120 | ;; 121 | ;; The functions decide-table-load-file and decide-table-load-dir 122 | ;; can be used to load random tables from text files into 123 | ;; the decide-tables variable. Each file contains one or more tables 124 | ;; with one possible substitution per line, in the same format 125 | ;; as is used in decide-tables. Weights are set by prefixing 126 | ;; a line with a number and a comma, with no whitespace before 127 | ;; or after. A line that begins with a semicolon marks 128 | ;; the beginning of a new table, and the text on that 129 | ;; line after the semicolon is the name of the table. 130 | ;; If a table is named MAIN (case-insensitive) it 131 | ;; will take its name from the file (sans extension). 132 | ;; The random-tables subdirectory in the git-repository 133 | ;; for decide-mode contains example tables. 134 | ;; 135 | ;; References (in brackets) to other tables will first match 136 | ;; tables defined in the same file. If that fails it will look 137 | ;; for other files that match the table-name. 138 | ;; It is possible to refer to tables inside of other files by 139 | ;; using table-name combining the file-name (sans extension) 140 | ;; with the name of the table within that file, joined by 141 | ;; a period (.) (e.g. example-dragon.prefix to refer to the 142 | ;; prefix table in random-tables/example-dragon.txt. 143 | ;; 144 | ;; 145 | ;; Example of globally binding a keyboard combination to roll dice: 146 | ;; (global-set-key (kbd "C-c r") 'decide-roll-dice) 147 | ;; 148 | ;; Results of decisions or dice will be input in current buffer at point, 149 | ;; or in the minibuffer if current buffer is read-only. 150 | ;; 151 | ;; To just type a question-mark (?) press ? immediately followed by 152 | ;; space or enter. (Or quote the ? key normally, ie C-q ?). 153 | ;; 154 | ;;; Code: 155 | 156 | (defvar decide-mode-map (make-sparse-keymap) 157 | "Keymap for decide minor mode.") 158 | 159 | ;;;###autoload 160 | (define-minor-mode decide-mode 161 | "Minor mode for making decisions. 162 | \\ %s\n" 209 | "How to format output from decide-mode functions. If this 210 | is a string it is used as first argument for the standard elisp 211 | format function, to format two strings (for what was generated 212 | and what the results were). If instead the value is a function 213 | that function will be called with those two strings and the 214 | returned value will be inserted. If the function returns 215 | nil nothing will be inserted." 216 | ) 217 | 218 | (setq decide-for-me-dice 219 | (let ((ya "YES+") 220 | (y "YES") 221 | (yb "YES-") 222 | (nb "NO-") 223 | (n "NO") 224 | (na "NO+") 225 | ) 226 | (list (cons :likely(list ya ya y yb nb n)) 227 | (cons :normal (list ya y yb nb n na)) 228 | (cons :unlikely (list y yb nb n na na) 229 | )))) 230 | 231 | (require 'cl-lib) 232 | 233 | (defun decide-for-me-get (difficulty) 234 | "Get random decision for difficulty :likely, :normal, or :unlikely." 235 | (let ((die (cdr (assoc difficulty decide-for-me-dice)))) 236 | (nth (random (length die)) die))) 237 | 238 | (defun decide-insert (what result) 239 | (let ((s (cond ((stringp decide-output-format) 240 | (format decide-output-format what result)) 241 | ((functionp decide-output-format) 242 | (funcall decide-output-format what result)) 243 | (t (error "decide-output-format must be string or function"))))) 244 | (when s (if buffer-read-only 245 | (minibuffer-message s) 246 | (insert s))))) 247 | 248 | (defun decide-for-me-likely () 249 | (interactive) 250 | (decide-insert "? [likely]" (decide-for-me-get :likely))) 251 | 252 | (defun decide-for-me-normal () 253 | (interactive) 254 | (decide-insert "?" (decide-for-me-get :normal))) 255 | 256 | (defun decide-for-me-unlikely () 257 | (interactive) 258 | (decide-insert "? [unlikely]" (decide-for-me-get :unlikely))) 259 | 260 | (defun decide-range-average (&rest results) 261 | (floor (+ 0.5 (/ (apply '+ (mapcar 'float results)) 262 | (length results))))) 263 | 264 | (defun decide-parse-range (s) 265 | (cond 266 | ((string-match "^\\(-?[1-9][0-9]*\\)\\(-+\\)\\([1-9][0-9]*\\)$" s) 267 | (list (string-to-number (match-string 1 s)) 268 | (string-to-number (match-string 3 s)) 269 | 'decide-range-average 270 | (length (match-string 2 s)))) 271 | ((string-match "^\\(-?[1-9][0-9]*\\)\\(<<+\\)\\([1-9][0-9]*\\)$" s) 272 | (list (string-to-number (match-string 1 s)) 273 | (string-to-number (match-string 3 s)) 274 | 'min 275 | (length (match-string 2 s)))) 276 | ((string-match "^\\(-?[1-9][0-9]*\\)\\(>>+\\)\\([1-9][0-9]*\\)$" s) 277 | (list (string-to-number (match-string 1 s)) 278 | (string-to-number (match-string 3 s)) 279 | 'max 280 | (length (match-string 2 s)))) 281 | (t nil))) 282 | 283 | (defun decide-describe-range (from to fn draws) 284 | (format "[%d-%d%s]" 285 | from 286 | to 287 | (if (> draws 1) 288 | (format " (%s of %d)" 289 | (cond 290 | ((eq fn 'decide-range-average) "average") 291 | ((eq fn 'min) "lowest") 292 | ((eq fn 'max) "highest")) 293 | draws) 294 | ""))) 295 | 296 | (defun decide-from-range-draw (from-to-pair) 297 | (let ((from (car from-to-pair)) 298 | (to (cdr from-to-pair))) 299 | (+ from (random (- to from -1))))) 300 | 301 | (defun decide-from-range-get (from to fn draws) 302 | (apply fn 303 | (mapcar 'decide-from-range-draw 304 | (make-list draws (cons from to))))) 305 | 306 | (defun decide-from-range (from to fn draws) 307 | (format "%d" (decide-from-range-get from to fn draws))) 308 | 309 | (defun decide-random-range (range-string) 310 | (interactive "sRange: ") 311 | (let ((range-spec (decide-parse-range range-string))) 312 | (decide-insert 313 | (apply 'decide-describe-range range-spec) 314 | (apply 'decide-from-range range-spec)))) 315 | 316 | (defun decide-random-choice (choices-string) 317 | (interactive "sRandom choice from (comma-separated choices): ") 318 | (let ((choices (mapcar 'string-trim (split-string choices-string ",")))) 319 | (decide-insert 320 | (format "(%s)" (mapconcat 'identity choices ", ")) 321 | (nth (random (length choices)) choices)))) 322 | 323 | (defun decide-table-normalize-name (top-table-name table-name) 324 | (let ((parts (split-string table-name "[.]"))) 325 | (if (> (length parts) 1) 326 | table-name 327 | (concat top-table-name "." table-name)))) 328 | 329 | (defun decide-table-top-table-name (table-name) 330 | (car (split-string table-name "[.]"))) 331 | 332 | (defun decide-table-assoc (top-table-name table-name) 333 | (or (assoc (decide-table-normalize-name top-table-name table-name) 334 | decide-tables) 335 | (assoc table-name decide-tables))) 336 | 337 | (defun decide-choose-for-table-list-part (top-table-name part) 338 | (let ((subparts (split-string part "\\]"))) 339 | (if (= (length subparts) 2) 340 | (concat (decide-choose-from-table top-table-name 341 | (nth 0 subparts)) 342 | (nth 1 subparts)) 343 | part))) 344 | 345 | (defun decide-choose-from-table-list (top-table-name choice) 346 | (mapconcat 'identity 347 | (mapcar (function (lambda (part) 348 | (decide-choose-for-table-list-part 349 | top-table-name part))) 350 | (split-string choice "\\[")) "")) 351 | 352 | (defun decide-weight-for-choice (choice) 353 | (if (listp choice) (cdr choice) 1)) 354 | 355 | (defun decide-entry-for-choice (choice) 356 | (if (listp choice) (car choice) choice)) 357 | 358 | (defun decide-table-sum (choices) 359 | (seq-reduce 360 | (lambda (s v) (+ s (decide-weight-for-choice v))) 361 | choices 0)) 362 | 363 | (defun decide-choose-from-table-choices (choices) 364 | (let* ((max (decide-table-sum choices)) 365 | (roll (decide-from-range-draw (cons 0 max))) 366 | (sum 0)) 367 | (cl-loop for c in choices 368 | for e = (decide-entry-for-choice c) 369 | for w = (decide-weight-for-choice c) 370 | sum w into sum 371 | until (< roll sum) 372 | finally return e))) 373 | 374 | (defun decide-choose-from-table (top-table-name table-name) 375 | (let ((found-table (decide-table-assoc top-table-name table-name))) 376 | (if found-table (decide-choose-from-table-list 377 | (decide-table-top-table-name (car found-table)) 378 | (decide-choose-from-table-choices 379 | (cdr found-table))) 380 | (let ((table-name-as-dice-spec (decide-make-dice-spec table-name)) 381 | (table-name-as-range-spec (decide-parse-range table-name))) 382 | (cond (table-name-as-dice-spec 383 | (format "%d" (decide-sum-dice-rolled 384 | (decide-roll-dice-result 385 | (nth 0 table-name-as-dice-spec) 386 | (nth 1 table-name-as-dice-spec)) 387 | (nth 2 table-name-as-dice-spec)))) 388 | (table-name-as-range-spec 389 | (format "%d" (apply 'decide-from-range-get 390 | table-name-as-range-spec))) 391 | (t table-name)))))) 392 | 393 | (defun decide-string-from-table (table-name) 394 | (decide-choose-from-table table-name table-name)) 395 | 396 | (defun decide-visible-tables () 397 | (cl-remove-if 398 | (lambda (x) 399 | (string-match "[.]" (car x))) 400 | decide-tables)) 401 | 402 | (defun decide-from-table (table-name) 403 | (interactive (list (completing-read "Table name: " 404 | (decide-visible-tables) 405 | nil 406 | 1))) 407 | (decide-insert 408 | (format "[<%s>]" table-name) 409 | (decide-choose-from-table table-name table-name))) 410 | 411 | (defun decide-table-parse-line (line) 412 | (cond 413 | ((string-match "^\s*#" line) nil) 414 | ((string-match "^\\([0-9]+\\),\\(.*\\)" line) 415 | (cons (match-string 2 line) 416 | (string-to-number (match-string 1 line)) 417 | )) 418 | (t line))) 419 | 420 | (defun decide-table-parse-lines (lines) 421 | (cl-remove-if-not 422 | (lambda (x) (or (consp x) (stringp x))) 423 | (mapcar 'decide-table-parse-line lines))) 424 | 425 | (defun decide-table-read-buffer () 426 | (save-excursion 427 | (goto-char (point-min)) 428 | (decide-table-parse-lines (split-string (buffer-string) "\n" t)))) 429 | 430 | (defun decide-make-table-name (file-name name) 431 | (let ((main-name (file-name-sans-extension 432 | (file-name-nondirectory file-name)))) 433 | (if (string= (downcase name) "main") 434 | main-name 435 | (concat main-name "." name)))) 436 | 437 | (defun decide-push-table (file-name lines) 438 | (let ((name-line (car lines))) 439 | (if (string-match "^\s*;" name-line) 440 | (let ((table-name (decide-make-table-name 441 | file-name 442 | (string-trim (nth 1 (split-string name-line ";"))))) 443 | (phrase-lines (cdr lines))) 444 | (push 445 | (cons table-name phrase-lines) 446 | decide-tables)) 447 | (error "First line in table must begin with ; `%s'" 448 | file-name)))) 449 | 450 | (defun decide-push-table-here (file-name begin end) 451 | (decide-push-table 452 | file-name 453 | (decide-table-parse-lines 454 | (split-string (buffer-substring-no-properties begin end) "\n" t)))) 455 | 456 | (defun decide-table-load-rest-of-file (file-name) 457 | (let ((begin (line-beginning-position))) 458 | (if (re-search-forward "^;" nil 1) 459 | (progn (decide-push-table-here file-name 460 | begin (line-beginning-position)) 461 | (decide-table-load-rest-of-file file-name)) 462 | (decide-push-table-here file-name 463 | begin (line-beginning-position))))) 464 | 465 | (defun decide-table-load-file (file-name) 466 | (interactive "f") 467 | (with-temp-buffer 468 | (insert-file-contents file-name) 469 | (if (re-search-forward "^;" nil 2) 470 | (decide-table-load-rest-of-file file-name) 471 | (error "Found no table in file %s" file-name)))) 472 | 473 | (defun decide-table-load-dir (dir) 474 | (interactive "D") 475 | (mapcar 'decide-table-load-file 476 | (directory-files-recursively dir ""))) 477 | 478 | (defun decide-whereto-compass-4 () 479 | (interactive) 480 | (decide-random-choice "N,S,E,W")) 481 | 482 | (defun decide-whereto-compass-6 () 483 | (interactive) 484 | (decide-random-choice "N,S,E,W,U,D")) 485 | 486 | (defun decide-whereto-compass-8 () 487 | (interactive) 488 | (decide-random-choice "N,S,E,W,NE,NW,SE,SW")) 489 | 490 | (defun decide-whereto-compass-10 () 491 | (interactive) 492 | (decide-random-choice "N,S,E,W,NE,NW,SE,SW,U,D")) 493 | 494 | (defun decide-whereto-relative-2 () 495 | (interactive) 496 | (decide-random-choice "left,right")) 497 | 498 | (defun decide-whereto-relative-3 () 499 | (interactive) 500 | (decide-random-choice "forward,left,right")) 501 | 502 | (defun decide-whereto-relative-4 () 503 | (interactive) 504 | (decide-random-choice "forward,left,right,back")) 505 | 506 | (defun decide-whereto-relative-6 () 507 | (interactive) 508 | (decide-random-choice "forward,left,right,back,up,down")) 509 | 510 | (defun decide-string-to-number (s default) 511 | "Convert s to number, in a way that makes sense for decide-make-dice-spec" 512 | (let ((n (if (stringp s) 513 | (string-to-number s) 514 | 0))) 515 | (cond ((null s) default) 516 | ((string= "" s) default) 517 | ((string= "0" s) 0) 518 | ((not (= n 0)) n) 519 | ((string= "+" s) 0) 520 | ((string= "-" s) 0) 521 | ((string= "+0" s) 0) 522 | ((string= "-0" s) 0) 523 | (t s)))) 524 | 525 | (defun decide-roll-custom-die (sides) 526 | (nth (random (length sides)) sides)) 527 | 528 | (defun decide-roll-number-die (faces) 529 | (let ((res (+ 1 (random faces)))) 530 | (list res (format "%d" res)))) 531 | 532 | (defun decide-roll-die (faces) 533 | (if (and (stringp faces) (= (length faces) 0)) 534 | (decide-roll-die-nonempty decide-default-dice-faces) 535 | (decide-roll-die-nonempty faces))) 536 | 537 | (defun decide-roll-die-nonempty (faces) 538 | (cond ((stringp faces) 539 | (let ((sides (cdr (assoc-string faces decide-custom-dice t)))) 540 | (if sides 541 | (decide-roll-custom-die sides) 542 | '(0 "?")))) 543 | ((numberp faces) 544 | (decide-roll-number-die faces)))) 545 | 546 | (defun decide-roll-dice-result (nr faces) 547 | (if (= 0 nr) 548 | '() 549 | (cons (decide-roll-die faces) 550 | (decide-roll-dice-result (- nr 1) faces)))) 551 | 552 | (defun decide-describe-roll (rolled) 553 | (let ((first-described (format "%s" (cadr (car rolled))))) 554 | (if (= 1 (length rolled)) 555 | first-described 556 | (format "%s %s" 557 | first-described 558 | (decide-describe-roll (cdr rolled)))))) 559 | 560 | (defun decide-sum-dice-rolled (rolled mod) 561 | (apply '+ (cons mod (mapcar 'car rolled)))) 562 | 563 | (defun decide-roll-dice-spec (nr faces mod) 564 | (let* ((rolled (decide-roll-dice-result nr faces)) 565 | (rolled-description (decide-describe-roll rolled)) 566 | (sum (decide-sum-dice-rolled rolled mod))) 567 | (if (= 0 mod) 568 | (if (> (length rolled) 1) 569 | (format "(%s) = %d" rolled-description sum) 570 | (format "%d" sum)) 571 | (format "(%s) %+d = %d" rolled-description mod sum)))) 572 | 573 | (defun decide-make-dice-spec (s) 574 | "eg \"1d6\" -> (1 6 0) or \"2d10+2\" -> (2 10 2) or \"4dF\" -> (4 \"f\" 0)" 575 | (when (string-match 576 | "^\\([0-9]*\\)d\\([0-9a-zA-Z]*\\)\\([+-][0-9]*\\)?" 577 | s) 578 | (list (decide-string-to-number (match-string 1 s) 1) 579 | (decide-string-to-number (match-string 2 s) 580 | decide-default-dice-faces) 581 | (decide-string-to-number (match-string 3 s) 0)))) 582 | 583 | (defun decide-describe-dice-spec (spec) 584 | (let* ((mod (car (last spec))) 585 | (faces (nth 1 spec)) 586 | (facesname (if (stringp faces) (upcase faces) (format "%d" faces)))) 587 | (if (= mod 0) 588 | (format "%dd%s" (nth 0 spec) facesname) 589 | (format "%dd%s%+d" (nth 0 spec) facesname (nth 2 spec))))) 590 | 591 | (defun decide-roll-dice (spec-string) 592 | "Roll some dice. Insert result in buffer, or in minibuffer if read-only." 593 | (interactive "sRoll: ") 594 | (let ((spec (decide-make-dice-spec spec-string))) 595 | (decide-roll-dice-insert (if spec spec '(1 6 0))))) 596 | 597 | (defun decide-roll-dice-insert (spec) 598 | (decide-insert 599 | (format "[%s]" (decide-describe-dice-spec spec)) 600 | (apply 'decide-roll-dice-spec spec))) 601 | 602 | (defun decide-roll-fate () 603 | "Roll four Fate/Fudge dice." 604 | (interactive) 605 | (decide-roll-dice "4df")) 606 | 607 | (defun decide-roll-1dA () 608 | (interactive) 609 | (decide-roll-dice "1dA")) 610 | 611 | (defun decide-roll-2dA () 612 | (interactive) 613 | (decide-roll-dice "2dA")) 614 | 615 | (defun decide-roll-1d6 () 616 | (interactive) 617 | (decide-roll-dice "1d6")) 618 | 619 | (defun decide-roll-2d6 () 620 | (interactive) 621 | (decide-roll-dice "2d6")) 622 | 623 | (defun decide-roll-1d3 () 624 | (interactive) 625 | (decide-roll-dice "1d3")) 626 | 627 | (defun decide-roll-1d4 () 628 | (interactive) 629 | (decide-roll-dice "1d4")) 630 | 631 | (defun decide-roll-1d5 () 632 | (interactive) 633 | (decide-roll-dice "1d5")) 634 | 635 | (defun decide-roll-1d7 () 636 | (interactive) 637 | (decide-roll-dice "1d7")) 638 | 639 | (defun decide-roll-1d8 () 640 | (interactive) 641 | (decide-roll-dice "1d8")) 642 | 643 | (defun decide-roll-1d9 () 644 | (interactive) 645 | (decide-roll-dice "1d9")) 646 | 647 | (defun decide-roll-1d10 () 648 | (interactive) 649 | (decide-roll-dice "1d10")) 650 | 651 | (defun decide-roll-1d12 () 652 | (interactive) 653 | (decide-roll-dice "1d12")) 654 | 655 | (defun decide-roll-1d20 () 656 | (interactive) 657 | (decide-roll-dice "1d20")) 658 | 659 | (defun decide-roll-1d100 () 660 | (interactive) 661 | (decide-roll-dice "1d100")) 662 | 663 | (defun decide-find-last-ws () 664 | (save-excursion 665 | (let ((p (search-backward-regexp "[\s\n(]" nil t))) 666 | (if p (+ p 1) (point-min))) 667 | ) 668 | ) 669 | 670 | (defun decide-get-from-last-ws () 671 | (buffer-substring-no-properties (decide-find-last-ws) (point)) 672 | ) 673 | 674 | (defun decide-dwim-insert () 675 | "Do what I mean with last word." 676 | (interactive) 677 | (let* ((s (decide-get-from-last-ws)) 678 | (dice-spec (decide-make-dice-spec s)) 679 | (range-spec (decide-parse-range s)) 680 | ) 681 | (cond (dice-spec (progn 682 | (delete-char (- (length s))) 683 | (decide-roll-dice-insert dice-spec))) 684 | (range-spec (progn 685 | (delete-char (- (length s))) 686 | (decide-random-range s))) 687 | (t (decide-for-me-normal))))) 688 | 689 | (defun decide-question-return () 690 | (interactive) 691 | (insert "?\n")) 692 | 693 | (defun decide-question-space () 694 | (interactive) 695 | (insert "? ")) 696 | 697 | (define-prefix-command 'decide-prefix-map) 698 | 699 | (define-key decide-mode-map (kbd "?") 'decide-prefix-map) 700 | 701 | (define-key decide-mode-map (kbd "? ?") 'decide-dwim-insert) 702 | (define-key decide-mode-map (kbd "? +") 'decide-for-me-likely) 703 | (define-key decide-mode-map (kbd "? -") 'decide-for-me-unlikely) 704 | 705 | (define-key decide-mode-map (kbd "? d") 'decide-roll-dice) 706 | (define-key decide-mode-map (kbd "? D") 'decide-roll-2d6) 707 | (define-key decide-mode-map (kbd "? 3") 'decide-roll-1d3) 708 | (define-key decide-mode-map (kbd "? 4") 'decide-roll-1d4) 709 | (define-key decide-mode-map (kbd "? 5") 'decide-roll-1d5) 710 | (define-key decide-mode-map (kbd "? 6") 'decide-roll-1d6) 711 | (define-key decide-mode-map (kbd "? 7") 'decide-roll-1d7) 712 | (define-key decide-mode-map (kbd "? 8") 'decide-roll-1d8) 713 | (define-key decide-mode-map (kbd "? 9") 'decide-roll-1d9) 714 | (define-key decide-mode-map (kbd "? 1 0") 'decide-roll-1d10) 715 | (define-key decide-mode-map (kbd "? 1 2") 'decide-roll-1d12) 716 | (define-key decide-mode-map (kbd "? 2 0") 'decide-roll-1d20) 717 | (define-key decide-mode-map (kbd "? %") 'decide-roll-1d100) 718 | (define-key decide-mode-map (kbd "? f") 'decide-roll-fate) 719 | (define-key decide-mode-map (kbd "? a") 'decide-roll-1dA) 720 | (define-key decide-mode-map (kbd "? A") 'decide-roll-2dA) 721 | 722 | (define-key decide-mode-map (kbd "? r") 'decide-random-range) 723 | 724 | (define-key decide-mode-map (kbd "? c") 'decide-random-choice) 725 | (define-key decide-mode-map (kbd "? t") 'decide-from-table) 726 | (define-key decide-mode-map (kbd "? w 4") 'decide-whereto-compass-4) 727 | (define-key decide-mode-map (kbd "? w 6") 'decide-whereto-compass-6) 728 | (define-key decide-mode-map (kbd "? w 8") 'decide-whereto-compass-8) 729 | (define-key decide-mode-map (kbd "? w 1 0") 'decide-whereto-compass-10) 730 | (define-key decide-mode-map (kbd "? W 2") 'decide-whereto-relative-2) 731 | (define-key decide-mode-map (kbd "? W 3") 'decide-whereto-relative-3) 732 | (define-key decide-mode-map (kbd "? W 4") 'decide-whereto-relative-4) 733 | (define-key decide-mode-map (kbd "? W 6") 'decide-whereto-relative-6) 734 | 735 | (define-key decide-mode-map (kbd "? RET") 'decide-question-return) 736 | (define-key decide-mode-map (kbd "? SPC") 'decide-question-space) 737 | (define-key decide-mode-map (kbd "? )") 'decide-question-right-paren) 738 | 739 | (provide 'decide) 740 | ;;; decide.el ends here 741 | --------------------------------------------------------------------------------