Here, we explore the code offered in http://code.google.com/p/iccle2/source/browse/trunk/lib/game.
Two useful idioms is to store constants in one box and things that change in another.
The working memory is stuff you zap before the next run. And by resetting the working memory, everything goes back to some initial useful initial state.
(defstruct wm "runtime working memory" (toolong 1000) board) (defparameter *w* (make-wm)) (defun zap () (setf *w* (make-wm)))
Working memory should be kept separate to the things that never change. For GAMELAND, these are the size of infinity and a bunch of characters that map to types in our system:
(defstruct fixed "fixed stuff" (inf most-positive-fixnum) (ninf (* -1 most-positive-fixnum)) (version 0.01) (width 21) ; counting from zero (height 10) ; counting from zero (chars '((#\a . axe) (#\k . key) (#\d . dynamite) (#\g . gold) (#\B . bush) (#\- . door) (#\. . blank) (#\* . wall) (#\ . blank) (#\~ . water) (#\^ . player) (#\> . player) (#\v . player) (#\< . player ))) (steps '( ;deltax deltay (#\^ 0 -1) (#\> 1 0) (#\v 0 1) (#\< -1 0)))) (defparameter *f* (make-fixed))
Note the "steps". This shows how we can more from the current position.
After the glabls, the next best thing to look at are the data types.
A "point" has slots "x" and "y".
(defstruct point "why isn't this a listp build in?" (x 0) (y 0))
A "board" contains the state of the board, where we can find the current player and the the gold.
(defstruct board g h gold ; position of gold at ; position of player width height (contents (make-grid)) ; an array height * width )
"Make-grid" is short-hand for "return an array of the height and width of the board".
(defun make-grid () (make-array (list (fixed-height *f*) (fixed-width *f*))))
thing player #\^ #\< #\> #\v has direction, holding piece has cost=0 blanks #\. #\ blocks has cost=inf, fixedp bush #\B door #\- wall #\* water #\~ has fixedp=t tool axe #\a key #\k dynamite #\d gold #\g
Note that the characters make to types via the "chars" of "*f". There are two use functions for chars:
Contains code to read strings like:
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~ B B k ~~ ~~ *** *** ~~ ~~*-* v *-*~~ ~~ ** ** ~~ ~~ g ** d ** a ~~ ~~ BB BB ~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
and output "board"s:
(read1) #S(BOARD :G NIL :H NIL :WIDTH 20 :HEIGHT 9 :GOLD #S(POINT :X 3 :Y 6) :AT #S(POINT :X 10 :Y 4) :CONTENTS #2A((#S(WATER :COST 536870911 :FIXEDP T) #S(WATER :COST 536870911 :FIXEDP T) #S(WATER :COST 536870911 :FIXEDP T) ... #S(WATER :COST 536870911 :FIXEDP T)) (#S(WATER :COST 536870911 :FIXEDP T) #S(WATER :COST 536870911 :FIXEDP T) ... S(WATER :COST 536870911 :FIXEDP T)) (#S(WATER :COST 536870911 :FIXEDP T) #S(WATER :COST 536870911 :FIXEDP T) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BUSH :COST 536870911 :FIXEDP NIL) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BUSH :COST 536870911 :FIXEDP NIL) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(BLANK :COST 0) #S(KEY :COST 0) #S(BLANK :COST 0) #S(WATER :COST 536870911 :FIXEDP T) #S(WATER :COST 536870911 :FIXEDP T)) ... )
This file also contains a "board-ok-p" function that checks that the generated board makes sense.
A recally common task is walk around the board. This is simplified by the "walk" macro
(walk (i k cell endp board) ... )which calls the body for each "cell" at position "i" and "j" in "board". The boolean "endp" is true if "cell" is the last cell in the row.
The code in this file pretty-prints a board. It is a nice example of polymorphism in LISP so we show it in its entirity.
(defun onstring (x) (with-output-to-string (str) (onstream x str))) (defmethod onstream ((x blank) &optional (str *standard-output*)) (princ " " str)) (defmethod onstream ((x piece) &optional (str *standard-output*)) (princ (piece->char x) str)) (defmethod onstream ((x board) &optional (str *standard-output*)) (let (sep (max (1- (fixed-width *f*)))) (format str "~%") (walk (i j cell endrowp x) (princ (or sep "") str) (onstream cell str) (if endrowp (princ #\Newline str))))) (defmethod onstream ((x player) &optional (str *standard-output*)) (princ (player-direction x) str))
Shows code for finding the neighbors and empty neighbors of a cell on the board.
Stagger shows code for picking empty neighbors at random, then moving to them. Note that, at each step, "onstream" is called to pretty print the currrent state of the board.