[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"