Re: do-random-weighted macro
- From: danb <sogwaldan@xxxxxxxxx>
- Date: Fri, 28 Mar 2008 01:30:57 -0700 (PDT)
I am in the process of learning lisp.
Welcome to c.l.l.
It all started with a function to calculate how many hits
a side gets in the Attack! combat system.
So presumably a player rolls the dice, and something happens
depending on the value.
I was just trying to learn lisp and see how simple
I could make this problem.
Good idea.
I ended up writing a function that, given an indefinite number of
pairs, where the first item in the pair is the weight, and the second
item is a function representing the action, select one of the items
randomly and do it.
(do-random-weighted (1 nil) (1 :s) (1 :a) (1 :t) (2 :p))
=> :p shows up 2/6 of the time, nil, :s, :a, and :t 1/6 each.
Separate the search from the randomization:
(defun weighted-nth (n-trigr pairs)
(let ((n-accum 0))
(dolist (pair pairs)
(if (>= n-accum n-trigr)
(return-from weighted-nth (cdr pair))
(incf n-accum (car pair))))))
(defmacro do-randoms ((n max num-vals) &body body)
(let ((_ (gensym)))
`(dotimes (,_ ,num-vals)
(let ((,n (random ,max))) ,@body))))
Since this function required me to write a lot of
lambda expressions, I thought maybe there was a better
way to do it. So I started to learn how to do macros.
(1 (print "Rolled soldier")
(when (plusp soldiers)
(print " ...hit")
(incf hits)
(decf soldiers)))
...
There's a lot of duplication here. You might as well
wrap the code in a function and the data in some
variables:
(defun process-roll (symbol string countable)
(cond ((plusp (symbol-value symbol))
(format t "You hit ~:[a~;~] ~A!~%" countable string)
(incf *hits*)
(decf (symbol-value symbol)))
(t
(if countable
(format t "Sorry, there are no ~As left.~%" string)
(format t "Sorry, there's no ~A left.~%" string)))))
(defvar *hits*)
(setf *hits* 0)
(defvar *weighted-forces*)
(setf *weighted-forces*
'((1 soldiers "soldier" t )
(1 tanks "tank" t )
(1 artillery "artillery" nil)
(2 planes "plane" t )))
(roll-hits 1 1 1 1)
(roll-hits 2 0 2 0)
Now you can have the program generate random numbers
for you, and feed them to the function that uses them:
(defun init-forces ()
(setf *hits* 0)
(set 'soldiers 10)
(set 'tanks 1)
(set 'artillery 2)
(set 'planes 1))
(defun total-weight (pairs) (reduce #'+ pairs :key #'car))
(defun multiroll (num-rolls pairs)
(do-randoms (n (total-weight pairs) num-rolls)
(let ((val (weighted-nth n pairs)))
(if val
(process-roll (car val) (cadr val) (caddr val))
(write-line "Sorry, no dice.")))))
And test the code:
(defun mr ()
(init-forces)
(multiroll 20 *weighted-forces*))
CL-USER> (mr)
You hit a tank!
You hit a plane!
You hit artillery!
Sorry, there are no tanks left.
You hit artillery!
Sorry, there's no artillery left.
Sorry, there are no tanks left.
Sorry, there's no artillery left.
Sorry, there are no planes left.
Sorry, there are no planes left.
Sorry, there are no planes left.
Sorry, there are no planes left.
Sorry, there are no planes left.
You hit a soldier!
You hit a soldier!
Sorry, there are no planes left.
Sorry, there are no tanks left.
You hit a soldier!
You hit a soldier!
Sorry, there are no tanks left.
Comments? Suggestions?
Don't use macros to operate on data. If the code you want
to call varies enough, and isn't selected until runtime, use
lambdas and funcall to call the right function. Macros are used
to hide code patterns that you would otherwise have to type
or paste repeatedly into your source file(s).
Note that I couldn't see a way around eval.
Is this a legitimate use of eval or do I need to do
something else?
Something else. EVAL is almost never necessary.
--Dan
------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
.
- Follow-Ups:
- Re: do-random-weighted macro
- From: Jonathan Gardner
- Re: do-random-weighted macro
- From: fvmarcoline
- Re: do-random-weighted macro
- References:
- do-random-weighted macro
- From: Jonathan Gardner
- do-random-weighted macro
- Prev by Date: Distributing Lisp applications.
- Next by Date: Re: do-random-weighted macro
- Previous by thread: do-random-weighted macro
- Next by thread: Re: do-random-weighted macro
- Index(es):
Relevant Pages
|