[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

(TFT) Tables for the rolling...



When I was young(er, like 30 years ago), I started in with that gateway game, D&D, of the 3 pamphlet and paste dice in a box variety. I very much liked having various tables to roll on. When I began running my own games, I also liked having tables to roll on. Being that I was also writing prodigious amounts of code, I often thought about automating the rolling of various tables. In fact, I thought about writing a language whose sole purpose was to make die rolls on tables. But I also wanted features that were difficult to use with paper tables. For examples, I wanted the chances of some result happening on a table to be modifiable by some input to the table. the prime example was the 2nd edition DMG encounter tables. I though it would be nice to have the encounter table change how often one ran into a monster based on the hour of the day, and the terrain, and such other factors. Though many of you have seen my venerable gem and jewelry program (the oldest bit of my code still in regular use), I never really got around to writing my random table language. I didn't because it was a big task in the languages I knew at the time. In C, I'd have to write a lexer and a parser, and some way of evaluating mathematical expressions, and a whole lot of other stuff. It was a pain, so I stuck to writing various tables as programs in themselves, which worked well enough. So a couple of years ago, I began writing in Lisp. Yes, that weird language with all the parentheses (that all other languages appear to want to be). One of the things regularly done in lisp is to write mini-languages -- just like my table language! So I figured I'd give it a try in Lisp... A half hour (and 15 lines of code) later, I had finished what I'd avoided doing for at least 25 years.
Here it is:

(defmacro table (name args &rest rest)
  `(defun ,name (,@args)
     (declare (special ,@args))
     (let* ((base ',(loop for x in rest collect (car x)))
            (results ',(loop for x in rest collect (cadr x)))
            (chances (loop for x in base collect (eval x)))
            (total (loop for x in chances sum x))
            (roll (+ 1 (random total))))
;(format t "Base:~{~a~^, ~}~%Chances:~{~a~^, ~}~%Results:~{~a~^, ~}~%total:~A roll:~A~%"
       ;  base chances results total roll)
       (do ((n chances (cdr n))
            (cnt 0)
            (item 0 (+ item 1)))
           ((eq n nil) nil)
         (setf cnt (+ cnt (car n)))
         (when (<= roll cnt)
           (return (eval (nth item results))))))))

And that's it. And here's how to use it (a small, incomplete example):

; This table takes the name, st, dx, ad iq for a character. It then
; figures out what to do with the extra attribute points. ; It prints the name and stats for the character. ; Format is how Lisp does output. (table generate-human (name st dx iq ex)
  (1 (format t "~A ST:~A DX:~A IQ:~A~%" name (+ st ex) dx iq))
  (1 (format t "~A ST:~A DX:~A IQ:~A~%" name st (+ dx ex) iq))
  (1 (format t "~A ST:~A DX:~A IQ:~A~%" name st dx (+ iq ex)))
(1 (format t "~A ST:~A DX:~A IQ:~A~%" name (+ st (/ ex 2)) (+ dx (/ ex 2)) iq)))

; This table generates characters. When calling the table, making more-wizards
; bigger makes it more likely that a wizard will be generated. Ex is passed on, and ; is the number of extra attribute points to add to the character. (table generate-character (more-wizards ex)
  (2 (generate-human "Human Tank" 14 10 8 ex))
  (2 (generate-human "Archer" 10 14 8 ex))
  (2 (generate-human "Blademaster" 11 11 10 ex))
  (2 (generate-human "Peculiar Weapons Guy" 11 11 10 ex))
  ((+ 1 more-wizards) (generate-human "Offensive Wizard" 10 12 10 ex))
  ((+ 1 more-wizards) (generate-human "Tank Wizard" 12 14 8 ex))
  ((+ 1 more-wizards) (generate-human "Hedge Wizard" 11 11 10 ex)))

You should see my old coat of arms generating program now (now that I know more about heraldry, and computer graphics). I might try a character-generating set of tables when I finish with the heraldry stuff. Anyway, if anyone wants to use it, go ahead. But you'll have to get Lisp installed and suchlike (which isn't all that hard). I suppose it could be ported to Emacs Lisp, in which case it could be made to output directly into the text of whatever you're writing (and args wouldn't have to be declared special, as elisp uses dynamic scoping by default).
Neil Gilmore
raito@raito.com
=====
Post to the entire list by writing to tft@brainiac.com.
Unsubscribe by mailing to majordomo@brainiac.com with the message body
"unsubscribe tft"