[the-feebs-war-cvs] r15 - definitions documentation graphics

gmilare at common-lisp.net gmilare at common-lisp.net
Sat Feb 16 20:01:44 UTC 2008


Author: gmilare
Date: Sat Feb 16 15:01:42 2008
New Revision: 15

Added:
   definitions/brains.lisp   (contents, props changed)
   definitions/extra.lisp   (contents, props changed)
   definitions/mazes.lisp   (contents, props changed)
   definitions/rules.lisp   (contents, props changed)
   definitions/utils.lisp
   documentation/feebs.tex   (contents, props changed)
   graphics/graphics.lisp   (contents, props changed)
Modified:
   /   (props changed)
   definitions/   (props changed)
Log:


Added: definitions/brains.lisp
==============================================================================
--- (empty file)
+++ definitions/brains.lisp	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,65 @@
+;;; -*- Common Lisp -*-
+
+#|  Copyright (c) 2007,2008 Gustavo Henrique Milar�
+
+    This file is part of The Feebs War.
+
+    The Feebs War is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    The Feebs War is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+|#
+
+
+(in-package :the-feebs-war)
+
+
+;;; Modified from "cautious-brain"
+
+(defun auto-brain (status proximity vision vision-left vision-right)
+  (declare (ignore vision-left vision-right))
+  (let ((stuff (my-square proximity)))
+    (cond ((and (member :mushroom stuff :test #'eq)
+		(< (energy-reserve status)
+		   (- (get-feeb-parm 'maximum-energy) 20)))
+	   :eat-mushroom)
+	  ((member :carcass stuff :test #'eq)
+	   :eat-carcass)
+	  ((and (ready-to-fire status)
+		(> (energy-reserve status) 30)
+		(dotimes (index (min (line-of-sight status) 5))
+		  (let ((feeb (find-if #'feeb-image-p (svref vision index))))
+		    (if (and feeb
+			     (not (eq (feeb-image-facing feeb)
+				      (facing status))))
+			(return t)))))
+	   :flame)
+	  ((and (not (wallp (left-square proximity)))
+		(or (member :mushroom (left-square proximity))
+		    (> 3 (random 10))))
+	   :turn-left)
+	  ((and (not (wallp (right-square proximity)))
+		(or (member :mushroom (right-square proximity))
+		    (> 3 (random 10))))
+	   :turn-right)
+	  ((and (> (line-of-sight status) 0)
+		(not (dotimes (index (min (line-of-sight status) 7))
+		       (if (find #'fireball-image-p (svref vision index))
+			   (return t)))))
+	   :move-forward)
+	  (t
+	   :turn-around))))
+
+(defun make-auto-feebs (n)
+  (dotimes (i n)
+    (define-feeb
+	(format nil "System Feeb # ~d" i)
+	#'auto-brain)))

Added: definitions/extra.lisp
==============================================================================
--- (empty file)
+++ definitions/extra.lisp	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,128 @@
+;;; -*- Common Lisp -*-
+
+#|  Copyright (c) 2007,2008 Gustavo Henrique Milar�
+
+    This file is part of The Feebs War.
+
+    The Feebs War is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    The Feebs War is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+|#
+
+
+;;; Usefull for creating a feeb
+;;; These are optimized so someone can use them without
+;;; complaining that they slow down your feeb !!!
+
+(in-package :the-feebs-war)
+
+(declaim (optimize (speed 3) (safety 0))
+
+	 (inline left-of right-of behind
+		 forward-dx forward-dy
+  		 left-dx left-dy
+  		 right-dx right-dy
+  		 behind-dx behind-dy
+		 
+	 	 relative-facing
+		 
+		 wallp chance)
+
+	 ((function ((integer 0 3)) (integer 0 3))
+	  left-of right-of behind
+	  relative-facing)
+	 
+	 ((function ((integer 0 3)) (integer -1 1)) 
+	  forward-dx forward-dy
+	  left-dx left-dy
+	  right-dx right-dy
+	  behind-dx behind-dy)
+
+	 ((function (rational) boolean)
+	  chance))
+
+;;; Directional arithmetic.
+
+(defun right-of (facing)
+  (mod (+ facing 3) 4))
+
+(defun left-of (facing)
+  (mod (+ facing 1) 4))
+
+(defun behind (facing)
+  (mod (+ facing 2) 4))
+
+(defun relative-facing (my-facing other-facing)
+  (mod (- my-facing other-facing) 4))
+
+(defun forward-dy (facing)
+  (if (oddp facing)
+      0
+      (rem (1- facing) 4)))
+
+(defun forward-dx (facing)
+  (if (oddp facing)
+      (rem (- 2 facing) 4)
+      0))
+
+(defun left-dy (facing)
+  (forward-dy (left-of facing)))
+
+(defun left-dx (facing)
+  (forward-dx (left-of facing)))
+
+(defun right-dy (facing)
+  (forward-dy (right-of facing)))
+
+(defun right-dx (facing)
+  (forward-dx (right-of facing)))
+
+(defun behind-dy (facing)
+  (forward-dy (behind facing)))
+
+(defun behind-dx (facing)
+  (forward-dx (behind facing)))
+
+;;; Tests
+
+(defun wallp (thing)
+  (the boolean
+    (eq :rock (car thing))))
+
+(defun chance (ratio)
+  (< (random (denominator ratio)) (numerator ratio)))
+
+#|
+;;; Handling the vision, vision-left and vision-right objects
+ (defmacro with-visible-elements ((count line-of-sight)
+                                 ((vis   vision)       &body vis-body)
+				 ((vis-l vision-left)  &body vis-l-body)
+				 ((vis-r vision-right) &body vis-r-body)
+				 &body finalize)
+  (let ((v (gensym))
+	(vl (gensym))
+	(vr (gensym)))
+    `(do* ((,count 1 (1+ ,count))
+	   (,v  (svref ,vision ,count))
+	   (,vl (svref ,vision ,count))
+	   (,vr (svref ,vision ,count)))
+	  ((= ,count line-of-sight)
+	   , at finalize)
+       (declare (list ,v ,vl ,vr)
+	        (fixnum ,count))
+       (dolist (,vis   ,v)
+	 , at vis-body)
+       (dolist (,vis-l ,vl)
+	 , at vis-l-body)
+       (dolist (,vis-r ,vr)
+	 , at vis-r-body))))
+|#
\ No newline at end of file

Added: definitions/mazes.lisp
==============================================================================
--- (empty file)
+++ definitions/mazes.lisp	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,359 @@
+;;; -*- Common Lisp -*-
+
+#|  Copyright (c) 2007,2008 Gustavo Henrique Milar�
+
+    This file is part of The Feebs War.
+
+    The Feebs War is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    The Feebs War is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+|#
+
+;;; The mazes were
+;;; Created by Jim Healy, July 1987.
+;;;
+;;; **************************************************
+;;;  Maze guidelines:
+;;;    X represents a wall.
+;;;    * represents a mushroom patch.
+;;;    e is a feeb entry point.
+;;;
+;;;  The maze should be a rectangle bounded by walls
+;;;  in each side.
+;;;  These mazes are all 32x32, but you may build
+;;;  a maze of any size you wish.
+;;; **************************************************
+
+;;; Maze1 has a good number of dead ends and little nooks.
+
+(in-package :the-feebs-war)
+
+(defparameter *maze-1*
+  '("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+    "XXX     *eXXXX      *e      ** X"
+    "XXX XXXXX XXXX XXXXXXXXXXXX XX X"
+    "XXX   XXX      XXXXXX       X  X"
+    "XXXXX XXXXXXX XXXXXXX XXXXXXX XX"
+    "X *       XXX XX *    XeXX    XX"
+    "X XXXXXXX XXX XXXXXXX X XXX XXXX"
+    "X  XXXXXX XXX XX      X      *XX"
+    "X XXXXXXX XXX XXXXXXX XXXXXXXXXX"
+    "X XXXXXXX XXX*     e  XXXXXX XXX"
+    "X         XXXXX XXXXXXXXX  * XXX"
+    "X XXXXX XXXXXX     XXXXXX XX  XX"
+    "X eXXXX XXXXXX XXX  XXXXX XX XXX"
+    "X XXXXX*       XXXXe XXXX XX  XX"
+    "X XXXXX XXXXXX XXXXX  XXX XXX XX"
+    "X eXXXX    e   XXXXXX *XX XX  XX"
+    "X XXXXX XXXXXX XXXXXXX  X XXeXXX"
+    "X   XXX        XXXXXXXX   XX  XX"
+    "X XXXXX XXXXXX XXXXXXXXXX XXXXXX"
+    "X XXXXX      * XXXXX          XX"
+    "X*  XXX XXXXXX XXXXX XXXXXX X XX"
+    "X XXXXX e      XXXXX X  e   X XX"
+    "X XX XX XXXXXX XXXXX X XXXXXX XX"
+    "X         *XXX XXXXX       *  XX"
+    "X XX XX XXXXXX XXXXXXXXXX XXXXXX"
+    "X XXXXX XXXXXX   *   *        XX"
+    "X   XXX XXXXXXXXXXXXXXXXXXXXX XX"
+    "X XXXX    X    X   eX    X    XX"
+    "X  XXX XX X XX X XX X XX X XX XX"
+    "X XXXX XX X XX X XX X XX*X XX XX"
+    "X e *  XX   XX * XX   XX   XXeXX"
+    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
+
+;;; Maze2 doesn't have any really long corridors.
+
+(defparameter *maze-2*
+  '("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+    "X  eXXXXX * X   XXXXXX X e XXXXX"
+    "X XXXX    X X XXXX   X   X XXXXX"
+    "X  XX  XXXX   XXXX X*  X X XXXXX"
+    "XX XX XXX   XXXX   XX XX X     X"
+    "XX e  XXX XXXXXXX XX  XXXXXXXX*X"
+    "XXXX XXX    XXXXX X e XXX   XX X"
+    "XXXX XXX XXXXXXXX   XXXX  X    X"
+    "XX * XX   XXe  XXXXXXXXXX XX XXX"
+    "XX XXXX X XX X   XXX XXXXX   XXX"
+    "XX        XX XXX X   XX    XXXXX"
+    "XXXXX XXX   *XXX   X    XXXXXXXX"
+    "XX*   XXXXXX  XXXX XXXX XXXXXXXX"
+    "XXXXX XX XXXX XXXXXXXXX XXXXXXXX"
+    "XXXXX  e XXXX   *XXXXXX   eXXXXX"
+    "XXXXXXXX XXXXXXX XXXXXXXXX XXXXX"
+    "XXXXXX     XXXXX  eXXXXX   XXXXX"
+    "XXXXXX XXX XXXXXXX XXXXX XXXXXXX"
+    "XX     XXX X   XXX XX    X    XX"
+    "XX XXX   XXXXX XX   XX XXX XX XX"
+    "XX XXXXX  *X   XX X XX XXXXXX*XX"
+    "X    XXXXX   XXXX X      XX   XX"
+    "X XX XXXXXXX   XXXXX*X X Xe XXXX"
+    "X    XXXX  e X XXXXX*XX  XX XXXX"
+    "X XX XXXXXX XX   XXX*XXX     XXX"
+    "XXXX  eXXX  XXXX XX  XXXXX X   X"
+    "XXXXXX XXXXXXXXX XX XXXX   XXX X"
+    "XXX *  X X    XX    XXXX XXX X X"
+    "XX  XXXX X XX XXXX XXX   X  e  X"
+    "XX XX  *   X *   X XXXX XX XXX*X"
+    "XX    XXX XXX XX  eXXX     XXX*X"
+    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
+
+;;; Maze3 has the minimum number of mushroom sites, most
+;;; of which are between a rock and a hard place.  Those
+;;; poor feebs!
+
+(defparameter *maze-3*
+  '("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+    "X    e  XXXXXX XXXXXXX*XXXXXXXXX"
+    "X X XXX XXXXX e   XXXX XXX e   X"
+    "X   XXX XXXXXX XX XX   XXX XXX X"
+    "XXX XXX XXXXXX XX XX X X   e   X"
+    "Xe  XXX*XXXX*  XX XXeX X XXXXX X"
+    "X X XXX XXXXXX XX XX X   XXXXX X"
+    "X   XXX XXXXXX XX*   XXXXXXXXX X"
+    "X XXXXX XX e   XX XXXXXXXX XXX X"
+    "X X XXX XXX XXXXX XXXXXX   XXX X"
+    "Xe  XXX      XXXX XXXX   X X   X"
+    "XXX XXXX XXXXXXXX  X   XXX   XXX"
+    "XXX  eXX XXXXXXXXX   XXXXX XXXXX"
+    "XXXXX XXXXXXXXXXXX XXXXXX    XXX"
+    "XXXXX     *    XX eXX XXX XX XXX"
+    "XX*XXXX XXXXXX XX XXX XXX XX XXX"
+    "XX X    XXXXX  X  XXX    eXX XXX"
+    "X    XXXXXXXX XX XXXX XXX XX XXX"
+    "X XXXXeXXXXXX    XXXX XXX XX XXX"
+    "X      XX*XXXXX XXXXXXXXX    XXX"
+    "XXXXXX    XXX   XXXX  XXXXXX XXX"
+    "XXXXXXXXX XXX XXXXXX XXXXXX  XXX"
+    "XXX       XX  e          eX XXXX"
+    "XX  XXXXX    XXXX XXXX XXXX XXXX"
+    "XX XXXXX  XX XXXX XXXX XXXX   XX"
+    "XX eXXXX XX  XXXX XXXX XXXXXX XX"
+    "XXX XX   XXX     *        XXX XX"
+    "XX  XX XXXX* XXXX XXXX XXXXXX XX"
+    "XXX  X XXXXX XXXX XXXX X      XX"
+    "XXXX    e    XXXX XXXX X XX X  X"
+    "XXXXXXXXXXXX     *e       X e XX"
+    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
+
+
+;;; Maze4 is symmetric about the vertical axis.  (Wow...)
+
+(defparameter *maze-4*
+  '("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+    "X*        eXXXXXXXXXXe        *X"
+    "X XXXXXXXX            XXXXXXXX X"
+    "X XX   XXXXXXX XX XXXXXXX   XX X"
+    "X    XeXXXXXXX XX XXXXXXXeX    X"
+    "XX X XXXXXXX  eXXe  XXXXXXX X XX"
+    "XX X XXXXXXX XXXXXX XXXXXXX X XX"
+    "XX * XXXXXXX XXXXXX XXXXXXX * XX"
+    "XX X XXXe              eXXX X XX"
+    "XX X XXX XXXXXXXXXXXXXX XXX X XX"
+    "XX e XXX    XXXXXXXX    XXX e XX"
+    "XX X XXXXXX XXXXXXXX XXXXXX X XX"
+    "XX X XXXX   XXXXXXXX   XXXX X XX"
+    "XX   XXXX XXXe   eXXXX XXXX   XX"
+    "XXX XXXXX XXX XXX XXXX XXXXX XXX"
+    "XXX XXXXX XXX      XXX XXXXX XXX"
+    "X*  XXXXX     XXXX     XXXXX  *X"
+    "X XXXXX XX XX  **  XX XX XXXXX X"
+    "X XXXXX XX XX XXXX XX XX XXXXX X"
+    "X XXX e XX XX XXXX XX XX e XXX X"
+    "X XXXXX XX    XXXX    XX XXXXX X"
+    "X XXXXX XXXXX XXXX XXXXX XXXXX X"
+    "X     X XXXXX XXXX XXXXX X     X"
+    "XXXXX  *                *  XXXXX"
+    "XXXXX XXXXXXXX XX XXXXXXXX XXXXX"
+    "XXXXX XXXXXXXX XX XXXXXXXX XXXXX"
+    "XXXXX    XXXXX XX XXXXX    XXXXX"
+    "XXXX  XX  XXXXeXXeXXXX  XX  XXXX"
+    "XXX  XXXX  XXX XX XXX  XXXX  XXX"
+    "XXX XXXXXX XXX XX XXX XXXXXX XXX"
+    "XX*       e    XX    e       *XX"
+    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
+
+;;; Maze5 has a lot of long corridors good for feeb showdowns.
+;;; Furthermore, all the feeb entry-points are in the corridors.
+;;; You can run but you can't hide!
+
+(defparameter *maze-5*
+  '("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+    "X              e           e   X"
+    "X XXXXXXX*XXXXXXXXXXXX XXXXXXX X"
+    "X               e              X"
+    "X X XXXXX XXXXXXXXXXXX XXXXX X X"
+    "X              *               X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XXeXX X X"
+    "X X XX XX    *         XX XX X X"
+    "XeX XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX          e   XX XXeX X"
+    "X X XXeXX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XX XX XeX"
+    "X*X XX XX              XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X XeXX XX XXXXXXXXXXXX*XX XX X X"
+    "X X XX XX  *           XX XX*X X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX  e           XX XX*X X"
+    "X X XX*XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX              XX XXeX X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X X XX XX XXXXXXXXXXXX XX XX X X"
+    "X             e                X"
+    "X*X XXXXX XXXXXXXXXXXX XXXXX X*X"
+    "X             e       *        X"
+    "X XXXXXXX XXXXXXXXXXXX XXXXXXX X"
+    "X     e        *               X"
+    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
+
+;;; Use this function to create new mazes
+;;; of any size.
+
+(defun make-template (x-size y-size)
+  "Prints map template of the requested size.
+Use this to create new mazes."
+  (loop repeat y-size collect
+	(make-string x-size :initial-element #\X)))
+
+(defun density (maze xs ys)
+  (let ((sum 0))
+    (dotimes (x xs)
+      (dotimes (y ys)
+	(if (not (aref maze x y))
+	    (incf sum))))
+    (float (/ sum (* xs ys)))))
+
+(defun horiz-corridor (map y x1 x2)
+  (do ((x x1 (if (< x1 x2) (1+ x) (1- x))))
+      ((= x x2))
+    ;; we need to guarantee that everything in map is
+    ;; corridors, that is, can't have something like
+    ;; XXXXXXXX
+    ;; XXX    X
+    ;; X    XXX
+    ;; XXXXXXXX
+    ;; that big blank square isn't good due
+    ;; to the limited vision of the feebs
+    (and (not (aref map x (1- y))) ; blank square up
+	 (or (and (not (aref map (1+ x) y)) ; blank square to the right
+		  (not (aref map (1+ x) (1- y)))) ; blank square up-right
+	     (and (not (aref map (1- x) (1- y))) ; blank square up-left
+		  (not (aref map (1- x) y)))) ; blank square to the left
+	 (return)) ; can't make a blank square here, stop
+    (and (not (aref map x (1+ y))) ; blank square down
+	 (or (and (not (aref map (1+ x) y)) ; blank square to the right
+		  (not (aref map (1+ x) (1+ y)))) ; blank square down-right
+	     (and (not (aref map (1- x) (1+ y))) ; blank square down-left
+		  (not (aref map (1- x) y)))) ; blank square to the left
+	 (return)) ; can't make a blank square here, stop
+    (setf (aref map x y) nil))
+  map)
+
+(defun vert-corridor (map x y1 y2)
+  (do ((y y1 (if (< y1 y2) (1+ y) (1- y))))
+      ((= y y2))
+    (and (not (aref map (1- x) y))
+	 (or (and (not (aref map x (1+ y)))
+		  (not (aref map (1- x) (1+ y))))
+	     (and (not (aref map (1- x) (1- y)))
+		  (not (aref map x (1- y)))))
+	 (return))
+    (and (not (aref map (1+ x) y))
+	 (if (or (and (not (aref map x (1+ y)))
+		      (not (aref map (1+ x) (1+ y))))
+		 (and (not (aref map (1+ x) (1- y)))
+		      (not (aref map x (1- y)))))
+	     (return)))
+    (setf (aref map x y) nil))
+  map)
+
+(defun translate (map xs ys)
+  (loop for y from (1- ys) downto 0 collect
+	(let ((str (make-string xs)))
+	  (dotimes (x xs str)
+	    (setf (aref str x)
+		  (if (aref map x y)
+		      #\X
+		    #\Space))))))
+
+;;; This one generates an almost ready-to-use map
+
+(defun generate-maze (x-size y-size
+		      &key (density 0.4)
+		      (corridor-x-min 1)
+		      (corridor-x-max (- x-size 2))
+		      (corridor-x-avg (floor x-size 4))
+		      (corridor-y-min 1)
+		      (corridor-y-max (- y-size 2))
+		      (corridor-y-avg (floor y-size 4)))
+  "Generates a maze of size X-SIZE x Y-SIZE (at least 10x10)
+with no entry points and no mushroom sites.
+DENSITY decides aproximatelly the ratio
+ (blank squares) / (total squares)
+recomended to be between 0.25 and 0.45.
+The horizontal corridors will be between CORRIDOR-X-MIN
+and CORRIDOR-X-MAX around CORRIDOR-X-AVG, when
+possible; similarly for vertical corridors.
+It returns two values, a layout like *maze-0* and its density."
+  (if (or (< x-size 10) (< y-size 10))
+      (error "Too small - should be at least 10x10."))
+  ;; Certifying the values to be acceptable
+  (ensure-bound corridor-x-avg
+		(ensure-bound corridor-x-min 1 (- x-size 2))
+		(ensure-bound corridor-x-max 3 (- x-size 2)))
+  (ensure-bound corridor-y-avg
+		(ensure-bound corridor-y-min 1 (- y-size 2))
+		(ensure-bound corridor-y-max 3 (- y-size 2)))
+  ;; Beginning with an array of walls
+  (let ((map (make-array (list x-size y-size)
+			 :initial-element t
+			 :element-type 'boolean)))
+    (do* ((i 1 (1+ i))
+	  (y 1 y*) ; position of horizontal corridor
+	  (y* (- y-size 2) (1+ (random (- y-size 2))))
+	  (x1 (1+ (random (- x-size 2))) ; start position of horiz corridor
+	      x1*)
+	  (x1* (1+ (random (- x-size 2)))
+	       (random-elt
+		(loop for x from 1 to (- x-size 2) ; any blank space
+		      if (not (aref map x y)) collect x))) ; in line
+	  (x2 (if x1 (bound-random x1 corridor-x-min
+				   corridor-x-avg corridor-x-max))
+	      (if x1 (bound-random x1 corridor-x-min
+				   corridor-x-avg corridor-x-max)))
+	  (x 1 x*) ; position of vertical corridor
+	  (x* (- x-size 2) (1+ (random (- x-size 2))))
+	  (y1 (1+ (random (- y-size 2)))
+	      y1*)
+	  (y1* (1+ (random (- y-size 2)))
+	       (random-elt
+		(loop for y from 1 to (- y-size 2)
+		      if (not (aref map x y)) collect y)))
+	  (y2 (if y1 (bound-random y1 corridor-y-min
+				   corridor-y-avg corridor-y-max))
+	      (if y1 (bound-random y1 corridor-y-min
+				   corridor-y-avg corridor-y-max)))
+	  (real-dens (density map x-size y-size)))
+	((or (>= real-dens density)
+	     (> i (* density x-size y-size))) ; quits after trying TOO MUCH
+	 (values (translate map x-size y-size) real-dens))
+      (if x1
+	  (setf map (horiz-corridor map y x1
+				    (bound x2 1 (- x-size 2)))))
+      (if y1
+	  (setf map (vert-corridor map x y1
+				    (bound y2 1 (- x-size 2))))))))

Added: definitions/rules.lisp
==============================================================================
--- (empty file)
+++ definitions/rules.lisp	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,247 @@
+;;; -*- Common Lisp -*-
+
+#|  Copyright (c) 2007,2008 Gustavo Henrique Milar�
+
+    This file is part of The Feebs War.
+
+    The Feebs War is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    The Feebs War is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+|#
+
+
+(in-package :the-feebs-war)
+
+
+
+;;; -*- General Rules -*-
+
+(def-feeb-parm 'game-length 320
+  "Number of turns the game will last.")
+
+(def-feeb-parm 'number-of-mushrooms 3
+  "Maximum number of mushrooms created each turn.")
+
+(let (turn-number total-time)
+
+  (defun start-round ()
+    (setf turn-number 0))
+
+  (defun start-turn ()
+    (incf turn-number)
+    (setf total-time 0)
+    (number-of-mushrooms
+     (random (1+ (get-feeb-parm 'number-of-mushrooms)))))
+  
+  (defun finish-game-p ()
+    (= (get-feeb-parm 'game-length) turn-number))
+
+  (defun inc-total-time (time)
+    (incf time total-time))
+
+  (defun total-time ()
+    total-time))
+
+;;; Detecting if feeb is playing
+
+(def-feeb-parm 'sense-location-p t
+  "If nil, x-position and y-position will return nil when
+   someone tries to invoke it. Otherwise return the position.")
+
+(defmethod x-position :around ((fb feeb))
+  (if (get-feeb-parm 'sense-location-p)
+      (call-next-method)))
+
+(defmethod y-position :around ((fb feeb))
+  (if (get-feeb-parm 'sense-location-p)
+      (call-next-method)))
+
+
+
+;;; -*- Being Born and Dying -*-
+
+;;; Being Born / Reincarnating
+
+(def-feeb-parm 'starting-energy 50
+  "Amount of energy a feeb will start with.")
+
+(defmethod create-object :before ((feeb feeb) x y)
+  (setf (feeb-energy-reserve feeb)
+	 (get-feeb-parm 'starting-energy)
+	(feeb-ready-to-fire feeb) t))
+
+;;; Dying and Killing
+
+(def-feeb-parm 'points-for-dying -3
+  "How many points some feeb earn for dying (usually negative).")
+
+(defmethod destroy-object :before ((feeb feeb) cause)
+  (incf (feeb-score feeb) (get-feeb-parm 'points-for-dying)))
+
+(def-feeb-parm 'points-for-killing 5
+  "How many points some feeb earn for killing someone.")
+
+(defmethod destroy-object :before ((feeb feeb) (fireball fireball))
+  (let ((owner (fireball-owner fireball)))
+    (unless (eq owner feeb)
+      (incf (feeb-score owner) (get-feeb-parm 'points-for-killing))
+      (incf (feeb-kill-counter owner)))))
+
+;;; Carcasses:
+
+(def-feeb-parm 'carcass-guaranteed-lifetime 5
+  "Number of turns that a carcass will surely not rot.
+After these turns, it can rot, depending on probabilities.")
+
+(def-feeb-parm 'carcass-rot-probability 1/3
+  "Probability of the carcass to rot, after the apropriate time.")
+
+(defun rot-carcass-p (time)
+  (and (> time (get-feeb-parm 'carcass-guaranteed-lifetime))
+       (chance (get-feeb-parm 'carcass-rot-probability))))
+
+
+
+;;; -*- Movement Choice -*-
+
+;;; Fireballs:
+
+(def-feeb-parm 'fireball-dissipation-probability 1/5
+  "Probability of the flame to dissipate each turn after the
+apropriate time.")
+
+(def-feeb-parm 'fireball-reflection-probability 2/3
+  "Probability of the flame to reflect when encountering a wall.")
+
+(defmethod make-move-choice ((fireball fireball))
+  (cond
+   ((wallp (get-forward-pos fireball))
+    (if (chance (get-feeb-parm 'fireball-reflection-probability))
+	:turn-around
+      :dissipate))
+   ((chance (get-feeb-parm 'fireball-dissipation-probability))
+    :dissipate)
+   (t :move-forward)))
+
+
+;;; Feebs
+
+(def-feeb-parm 'flame-no-recovery-time 2
+  "Probability
+of the feeb to recover the hability to throw a flame, after the apropriate
+time.")
+
+(def-feeb-parm 'flame-recovery-probability 1/3
+  "Probability of the feeb to recover the hability to throw a flame,
+after the apropriate time.")
+
+(defmethod make-move-choice :around ((feeb feeb))
+  (unless (feeb-ready-to-fire feeb)
+    (and (> (feeb-turns-since-flamed feeb)
+	    (get-feeb-parm 'flame-no-recovery-time))
+	 (chance (get-feeb-parm 'flame-recovery-probability))
+	 (setf (feeb-ready-to-fire feeb) t)))
+  (let (choice)
+    (inc-total-time
+     (setf (feeb-time feeb)
+	   (+ (- (get-internal-real-time))
+	      (progn
+		(setf choice (call-next-method))
+		(get-internal-real-time)))))
+    choice))
+
+
+
+;;; -*- Moving -*-
+
+;;; Fireball
+
+(defmethod make-move :before ((fireball fireball) (move (eql :move-forward)))
+  (multiple-value-bind (stuff x-pos y-pos)
+      (get-forward-pos fireball)
+    (dolist (thing stuff)
+      (typecase thing
+	(feeb (destroy-object thing fireball))
+	((eql :mushroom)
+	 (delete-object thing x-pos y-pos))))))
+
+
+;;; Feebs
+
+(def-feeb-parm 'slow-feeb-noop-switch nil
+  "If is non-nil, there is a possibility that the move
+of a feeb is aborted according to its function evaluation
+time.")
+
+(def-feeb-parm 'slow-feeb-noop-factor 1/4
+  "The probability of the feeb to abort will be this factor
+times the amount of time the feeb takes to have a decision,
+divided by the total time taken by all the feebs in the
+current turn, or divided by a reference time.")
+
+(def-feeb-parm 'reference-time nil
+  "Time taken by reference if non-nil. See slow-feeb-noop-factor.")
+
+(def-feeb-parm 'points-for-slow-down -1
+  "Points earned when a feeb's move is aborted due to slowness.")
+
+(defmethod make-move :around ((feeb feeb) move)
+  (if (get-feeb-parm 'slow-feeb-noop-switch)
+      (if (chance (* (get-feeb-parm 'slow-feeb-noop-factor)
+			 (/ (feeb-time feeb)
+			    (or (get-feeb-parm 'reference-time)
+				(total-time)))))
+	  (prog1 nil ; in case that the move was eating something
+	    (incf (feeb-score feeb) (get-feeb-parm 'points-for-slow-down)))
+	(call-next-method))
+    (call-next-method)))
+
+(defmethod make-move :around ((feeb feeb) (move (eql :move-forward)))
+  (let ((thing (find-if #'fireball-p (get-forward-pos feeb))))
+    (if thing
+	(destroy-object feeb thing)
+      (call-next-method))))
+
+
+;;; Eating
+
+(def-feeb-parm 'maximum-energy 100
+  "The most energy a feeb can accumulate.")
+
+(def-feeb-parm 'mushroom-energy 50
+  "Amount of energy recovered when the feeb eats a mushroom.")
+
+(defmethod make-move :around ((feeb feeb) (move (eql :eat-mushroom)))
+  (when (call-next-method) ; was eating successfull?
+    (setf (feeb-energy-reserve feeb)
+	  (min (+ (feeb-energy-reserve feeb)
+		  (get-feeb-parm 'mushroom-energy))
+	       (get-feeb-parm 'maximum-energy)))))
+
+(def-feeb-parm 'carcass-energy 30
+  "Amount of energy recovered each turn that the feeb
+eats a carcass.")
+
+(defmethod make-move :around ((feeb feeb) (move (eql :eat-carcass)))
+  (when (call-next-method)
+    (setf (feeb-energy-reserve feeb)
+	  (min (+ (feeb-energy-reserve feeb)
+		  (get-feeb-parm 'carcass-energy))
+	       (get-feeb-parm 'maximum-energy)))))
+
+(def-feeb-parm 'flame-energy 10
+  "Amount of energy lost after throwing a flame.")
+
+(defmethod make-move :around ((feeb feeb) (move (eql :flame)))
+  (when (>= (feeb-energy-reserve feeb) (get-feeb-parm 'flame-energy))
+    (decf (feeb-energy-reserve feeb) (get-feeb-parm 'flame-energy))
+    (call-next-method)))

Added: definitions/utils.lisp
==============================================================================
--- (empty file)
+++ definitions/utils.lisp	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,49 @@
+;;; -*- Common Lisp -*-
+
+#|  Copyright (c) 2007,2008 Gustavo Henrique Milar�
+
+    This file is part of The Feebs War.
+
+    The Feebs War is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    The Feebs War is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+|#
+
+
+(in-package :the-feebs-war)
+
+(defun bound-random (start min avg max)
+  (+ start
+     (* (expt -1 (random 2))
+	(let ((sort (random 2.0)))
+	  (round
+	   (if (< sort 1.0)
+	       (+ min (* sort (- avg min)))
+	     (+ avg (* (1- sort) (- max avg)))))))))
+
+(defun random-elt (seq)
+  (if seq
+      (elt seq (random (length seq)))))
+
+(defmacro ensure-bound (elt min max)
+  `(setf ,elt (bound ,elt ,min ,max)))
+
+(defun bound (elt min max)
+  (max min (min max elt)))
+
+(defmacro aif (test then &optional else)
+  `(let ((it ,test))
+     (if it ,then ,else)))
+
+(defmacro awhen (test &rest body)
+  `(let ((it ,test))
+     (when it , at body)))

Added: documentation/feebs.tex
==============================================================================
--- (empty file)
+++ documentation/feebs.tex	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,593 @@
+
+%   Copyright (c) 2007,2008 Gustavo Henrique Milaré
+%
+%   This file is part of The Feebs War.
+%
+%   The Feebs War is free software; you can redistribute it and/or modify
+%   it under the terms of the GNU General Public License as published by
+%   the Free Software Foundation; either version 3 of the License, or
+%   (at your option) any later version.
+%
+%   The Feebs War is distributed in the hope that it will be useful,
+%   but WITHOUT ANY WARRANTY; without even the implied warranty of
+%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%   GNU General Public License for more details.
+%
+%   You should have received a copy of the GNU General Public License
+%   along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+
+\documentclass[english]{article}
+\usepackage[T1]{fontenc}
+\usepackage[latin1]{inputenc}
+\IfFileExists{url.sty}{\usepackage{url}}
+                      {\newcommand{\url}{\texttt}}
+
+\makeatletter
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
+\newenvironment{lyxlist}[1]
+{\begin{list}{}
+{\settowidth{\labelwidth}{#1}
+ \setlength{\leftmargin}{\labelwidth}
+ \addtolength{\leftmargin}{\labelsep}
+ \renewcommand{\makelabel}[1]{##1\hfil}}}
+{\end{list}}
+
+
+\IfFileExists{url.sty}{\usepackage{url}
+}
+                      {\newcommand{\url}{\texttt}}
+
+\makeatletter
+
+
+
+
+\makeatother
+
+\usepackage{babel}
+\makeatother
+
+\begin{document}
+
+\title{\textbf{\huge The Feebs War}}
+
+
+\author{Gustavo Henrique Milaré}
+
+\maketitle
+\begin{abstract}
+\textit{The Feebs War} is a modified version of Planet of the Feebs
+\url{http://www.cliki.net/}, a game made for people learn and improve
+their lisp and code manipulation tecniques. The graphics are now displayed
+using Lispbuilder \url{http://lispbuilder.sourceforge.net}'s libraries,
+so the problems with portability from CMUCL and X Window Sistem do
+not exist anymore. Also the code is cleaner and more extensible. 
+\end{abstract}
+\tableofcontents{}
+
+
+\section{Introduction}
+
+The Feebs are intelligent and hostile creatures that live inside maze
+tunnels. They also have no mercy with each other, so they frequently
+throw a letal flame from through their mouth, getting rid of their
+opponent and eatting the carcass left. But throwing flames have an
+energy cost, so they must keep tracking for food.
+
+This game is intended to help lisp newbies (or maybe a little more
+advanced lispers) to learn lisp. A player must create a function that
+receives what his/her feeb is seeing and feeling, and returns what
+it will do next. To create the better feeb, one can create variables
+to store data from previous moves (or not), and can also use all the
+power of lisp to improve his/her creature. But the most important
+is to make good choices and be aware of danger!
+
+
+\subsection{Changes from \emph{Planet of the Feebs}}
+
+Many changes were made from the original game, but, if you have any
+feeb definition and you want to use it, it should be easy to adapt
+the brain function to the new rules.
+
+The main reason of this project is that \textit{Planet of the Feebs}
+is really interesting for (not just) newbies to learn lisp, but the
+difficulties to install, unportability and the ausence of competitors
+make it difficult to someone to be interested in making a feeb. So,
+I hope that making these adjustments and maybe creating some contests
+over the web make people be more interested in learning lisp.
+
+So, these are (some of) the changes:
+
+\begin{itemize}
+\item The graphics are not based on X Window Sistem anymore, but on \textit{Lispbuilder},
+and there are no CMUCL's event handler. This way, the code is more
+portable and graphics can be improved. Just creating some image
+files of a feeb and your feeb is much more personalized! 
+\item Every element of the map (including walls) is a list, so the brain of
+a feeb doesn't need to test all the time if the element is an atom
+or a list (wich, in my opinion, is really boring, unlispy and unnecessary
+in this case). That was only a reason to duplicate code and work,
+adding no results at all...
+\item Many functions and variables are changed and others were added
+\item Documentation is more objective than the one provided with \textit{Planet
+of the Feebs}, and is fully compatible with the code. This way it
+is easier to understand the game.
+\item Security is improved. Now it the behavior functions are allowed to store
+and change structures and vectors passed to it.
+The parameters can't be change by those functions while inside the game.
+\item It is possible now to extend the rules: the code is object oriented and
+new rules, special moves, change the behavior of flames, etc, can be done
+by adding new classes and/or methods. This manual is just the beginning!
+end{itemize}
+
+\section{The Game}
+
+
+\subsection{Overview}
+
+Your feeb's objective is to survive and kill other feebs. It is inside
+a maze of tunnels. Every turn, all feebs lose one unit of energy,
+and maybe starves. Your feeb is able to move forward, turn left, right
+or around, flame, peek around a corner, eat something or just wait.
+After all feebs move, the flames thrown before also move (or dissipate),
+carcasses may rot and mushrooms may grow, accordingly to some rules.
+
+The game rules are defined by parameters. These parameters can be read
+by the command  \textsf{\textbf{(get-feeb-parm~}'parameter\textbf{)}}
+To see all parameters, values and also all the documentation, one can use
+\textsf{\textbf{(list-parameter-settings)}}. Using
+\textsf{\textbf{(change-feeb-parm}'parameter~value\textbf{)}}
+gives the possibility to change them (but not during the game) and
+\textsf{\textbf{(documentation~}'parameter~'feeb-parm\textbf{)}}
+can be used to know them. Just remember that every probability
+must be a rational number (like 1/2).
+
+But don't panic! These parameters are just for one to know how
+the game is going to be, but in the begining there is no need
+to explicitly use them when creating the brain of a feeb.
+The best way to create a feeb is watching a game (among system feebs),
+improving it (it is defined in file brains.lisp) a little more,
+testing the changes...
+
+These are some global parameters:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{'game-length}}}] Number of turns the game
+will last.
+\item [{\textsf{\textbf{'points-for-killing}}}] How many points some
+feeb earn for killing someone. 
+\item [{\textsf{\textbf{'points-for-dying}}}] How many points some
+feeb earn for dying (usually negative). 
+\item [{\textsf{\textbf{'maze-x-size}}}] Horizontal size of the maze. 
+\item [{\textsf{\textbf{'maze-y-size}}}] Vertical size of the maze.
+\end{lyxlist}
+
+\subsection{Throwing flame}
+
+If a feeb decides to throw a flame, if it is prepared to and has
+enough energy, the next turn there will be a flame in the square
+in front of the feeb, and it will see it, so the feeb shouldn't move
+forward. For a few turns, the feeb will not be able to
+throw flames. Each turn, the flame moves forward destroing mushrooms and
+killing feebs it encounters, transforming them into carcass. If there
+is a wall, the flame can reflect, and, if so, it will turn 180 degrees.
+
+Once a feeb is killed (or starves), in it's place in the maze there will appear
+a carcass. The feeb goes to the end of the dead feebs line. When the
+carcass rots, the first feeb in line reincarnates. So, dying is not so terrible.
+
+These are the parameters related to flames:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{'flame-energy}}}] Amount of energy lost after
+throwing a flame. 
+\item [{\textsf{\textbf{'fireball-guaranteed-lifetime}}}] Number of
+turns that a fireball is guaranteed not to dissipate, unless it encounters
+a wall.
+\item [{\textsf{\textbf{'fireball-dissipation-probability}}}] Probability
+of the flame to dissipate each turn after the apropriate time.
+\item [{\textsf{\textbf{'fireball-reflection-probability}}}] Probability
+of the flame to reflect when encountering a wall. 
+\item [{\textsf{\textbf{'flame-no-recovery-time}}}] Number of turns
+that a feeb cannot fire.
+\item [{\textsf{\textbf{'flame-recovery-probability}}}] Probability
+of the feeb to recover the hability to throw a flame, after the apropriate
+time.
+\end{lyxlist}
+
+\subsection{Eating food}
+
+There are two kinds of food, carcasses and mushrooms. Carcasses usually
+give less energy than mushrooms, and may rot, but, while it does not
+rot, a feeb can feed as long as it wishes. Mushrooms disapear after
+being eaten. By eating food, the feeb
+will be able to recover energy, wich is important because, if a feeb
+stays with 0 or less units of energy, it starves.
+
+These are the quantities:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{'mushroom-energy}}}] Amount of energy recovered
+when the feeb eats a mushroom. 
+\item [{\textsf{\textbf{'carcass-energy}}}] Amount of energy recovered
+each turn that the feeb eats a carcass. 
+\item [{\textsf{\textbf{'carcass-guaranteed-lifetime}}}] Number of
+turns that a carcass will surely not rot. After these turns, it
+can rot, depending on probabilities. 
+\item [{\textsf{\textbf{'carcass-rot-probability}}}] Probability of
+the carcass to rot, after the apropriate time. 
+\item [{\textsf{\textbf{'maximum-energy}}}] Maximum amount of energy
+that a feeb can have eating. 
+\item [{\textsf{\textbf{'starting-energy}}}] Amount of energy a feeb
+has when it reincarnates. 
+\item [{\textsf{\textbf{'number-of-mushrooms}}}] Quantity of mushrooms
+that exist in the maze. 
+\end{lyxlist}
+
+\section{The Feeb}
+
+A feeb needs four things: a name, a brain and a set of graphics (optional).
+
+\begin{itemize}
+\item The name, a string. 
+\item The brain is a function that decides what the feeb will do next, based
+on what it is seeing and feeling.
+\item The set of graphics is an image file (of format BMP, JPEG, PNG, and
+any others that supported by SDL\_image). 
+\end{itemize}
+One can create a feeb calling
+\textsf{\textbf{(define-feeb}~name~brain~\textbf{:graphics}~graphics\textbf{)}}.
+If name is already used, a warning will be signaled, and the old feeb
+will be substituted. Calling \textsf{\textbf{(list-of-feebs)}} will
+return the list of the feebs (names only) that will be defined when
+the game begins. \textsf{\textbf{(delete-feeb}}\textsf{~name}\textsf{\textbf{)}}
+will delete the feeb with this name from this list, and \textsf{\textbf{(delete-all-feebs)}}
+will clear it.
+
+
+\subsection{Possible decisions}
+
+After processing the information available, the brain will take a
+decision. If this decision is not one of the decisions listed down,
+a warning will be signaled, and the result will be like \textsf{\textbf{:wait}}.
+Then, if someone are testing a brain function, he or she will be able
+to know if something goes wrong.
+
+The possible values that the brain function can return are these:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{:move-forward}}}] Move one square forward, unless
+there is a wall in front of the feeb. 
+\item [{\textsf{\textbf{:turn-left}}}] Turn 90 degrees to the left. 
+\item [{\textsf{\textbf{:turn-right}}}] Turn 90 degrees to the right. 
+\item [{\textsf{\textbf{:turn-around}}}] Turn 180 degrees. 
+\item [{\textsf{\textbf{:flame}}}] Throw a flame. The flame will be created
+next turn in the front square of the feeb. 
+\item [{\textsf{\textbf{:wait}}}] Do nothing in this turn. 
+\item [{\textsf{\textbf{:peek-left}}}] Peek to the left around a corner.
+The creature does note actually move, but, in the next turn, the creature
+will have the same vision that it would have if he had moved one step
+foward and turned left (and one step back because the feeb needs to see
+what is actually in front of it). Peeking used so a feeb can analize a corridor
+before trespassing it. 
+\item [{\textsf{\textbf{:peek-right}}}] Peek to the right around a corner,
+analogous to \textsf{\textbf{:peek-left}}. 
+\item [{\textsf{\textbf{:eat-carcass}}}] Eat a carcass if there is any
+available in the feeb's square. The amount of the parameter
+\textsf{\textbf{'carcass-energy}} is restored to the feeb's energy. 
+\item [{\textsf{\textbf{:eat-mushroom}}}] Eat a mushroom if there is any
+available in the feeb's square. The amount of the parameter
+\textsf{\textbf{'mushroom-energy}} is restored to the feeb's energy.
+\end{lyxlist}
+
+\subsection{Information available}
+
+The brain of a feeb must take five arguments; I'll call them \textsf{\emph{status}},
+\textsf{\emph{proximity}}, \textsf{\emph{vision}}, \textsf{\emph{vision-left}}
+and \textsf{\emph{vision-right}}.
+
+
+\subsubsection{Status}
+
+Every time the brain is called, it receives some useful information
+through \textsf{\textbf{status}}.
+
+The structure \textsf{\textbf{status}} keeps information about the
+feeb itself. Also it has information of the previous movement of the
+feeb. To access them, one must call:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{(name}}\textsf{\emph{~status}}\textsf{\textbf{)}}}] \begin{flushleft}
+The name of the feeb.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(facing}}\textsf{\emph{~status}}\textsf{\textbf{)}}}] \begin{flushleft}
+Where the feeb is facing to, one of the constants provided: \textsf{\textbf{north}}, 
+\textsf{\textbf{east}}, \textsf{\textbf{south}} or \textsf{\textbf{west}},
+wich are 0, 1, 2 and 3 respectivelly.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(x-position}\emph{~status}\textbf{)}}}] \begin{flushleft}
+The horizontal position of the feeb, starting with 0 and increasing to east.
+If \textsf{\textbf{'sense-location-p}} is nil, it returns nil instead.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(y-position}\emph{~status}\textbf{)}}}] \begin{flushleft}
+The vertical position of the feeb, starting with 0 and increasing to south. 
+If \textsf{\textbf{'sense-location-p}} is nil, it returns nil instead.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(peeking}\emph{~status}\textbf{)}}}] \begin{flushleft}
+If it is \textsf{\textbf{:peek-left}} or \textsf{\textbf{:peek-right}}, it means
+that the current \textsf{\emph{vision}} provided is result of a previous
+\textsf{\textbf{:peek-left}} or \textsf{\textbf{:peek-right}} command
+of the same feeb. Otherwise, it is \textsf{\textbf{nil}}. Note that
+\textsf{\emph{proximity}} is \emph{not} affected.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(line-of-sight}\emph{~status}\textbf{)}}}] \begin{flushleft}
+Indicates the amount of valid entries in \textsf{\emph{vision}}. It actually
+means that \textsf{\textbf{(aref}\emph{~vision~}\textbf{(line-of-sight}\emph{~status}\textbf{))}}
+will return \textsf{\textbf{'(:rock)}}.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(ready-to-fire}\emph{~status}\textbf{)}}}] \begin{flushleft}
+If \textsf{\textbf{T}} indicates that the feeb is ready to fire.
+If \textsf{\textbf{Nil}} indicates it is not.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(aborted}\emph{~status}\textbf{)}}}] \begin{flushleft}
+Related with timing. Returns \textsf{\textbf{T}} if the last move of feeb
+was aborted because of timing issues.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(last-move}\emph{~status}\textbf{)}}}] \begin{flushleft}
+The feeb's previous move, or \textsf{\textbf{:dead}} if it has just reincarnated.
+\par\end{flushleft}
+\end{lyxlist}
+
+\subsubsection{Proximity and vision}
+
+The brain receives also information about what is near the feeb and
+what the feeb sees. We note that, contrary to \emph{Planet of the Feebs},
+it is safe to change anything inside these structures, so you are
+alowed to keep them stored and to modify them as you wish.
+
+The structure \textsf{\emph{proximity}} has the contents of the squares
+near the feeb (not affected by peeking) with these fields:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{(my-square}}\textsf{\emph{~proximity}}\textsf{\textbf{)}}}] \begin{flushleft}
+Contents of the feeb's current square.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(left-square}}\textsf{\emph{~proximity}}\textsf{\textbf{)}}}] \begin{flushleft}
+Contents of the right square of the feeb.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(right-square}}\textsf{\emph{~proximity}}\textsf{\textbf{)}}}] \begin{flushleft}
+Contents of the left square of the feeb.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(rear-square}}\textsf{\emph{~proximity}}\textsf{\textbf{)}}}] \begin{flushleft}
+Contents of the square behind the feeb.
+\par\end{flushleft}
+\item [{The}] vector \textsf{\emph{vision}} has the contents of the squares
+that are in front of the feeb. For example, 
+\textsf{\textbf{(aref}\emph{~vision~}0\textbf{)}}
+will return the contents of the square in front of the feeb,
+\textsf{\textbf{(aref}\emph{~vision~}1\textbf{)}}
+will return the contents of the next square, and so on. As said before,
+\textsf{\textbf{(aref}\emph{~vision~}\textbf{(line-of-sight}\emph{~status}\textbf{))}}
+will be the first \textsf{\textbf{'(:rock)}} encountered. All subsequents square, like
+\textsf{\textbf{(aref}\emph{~vision~}\textbf{(+}~1~\textbf{(line-of-sight}\emph{~status}\textbf{)))}},
+will be garbage and should not be used.
+\end{lyxlist}
+The contents of one square returned by any of these calls is either
+a list of elements, a wall \textsf{\textbf{'(:rock)}} (i.e. a list with
+one element, a \textsf{\textbf{:rock}}) or \textsf{\textbf{()}} if the
+square is empty. Each element of the square is one of these:
+
+\begin{itemize}
+\item \textbf{Feeb image.} One can call \textsf{\textbf{(feeb-image-p}~element\textbf{)}}
+to see if element is a feeb image. 
+\item \textbf{Fireball image.} One can call \textsf{\textbf{(fireball-image-p}~element\textbf{)}}
+to check if element is a fireball image. 
+\item \textsf{\textbf{:carcass}}. If there is a \textsf{\textbf{:carcass}}
+in the square of the feeb (i.e. in \textsf{\textbf{(my-square}}\textsf{\emph{~proximity}}\textsf{\textbf{)}}),
+the call \textsf{\textbf{:eat-carcass}} will make the feeb eat it. 
+\item \textsf{\textbf{:mushroom}}. Analogous to \textsf{\textbf{:carcass}}.
+A mushroom appears randomly in places previously marked in the map.
+\end{itemize}
+
+\subsubsection{Feebs and fireballs images}
+
+Both fireballs and feebs that are given to the brain function are
+not the real ones, but just images with contents that the brain function
+can access. It is allowed to keep and change its contents because they
+won't be used internally.
+
+These are the accessors available (they read and change the fiels):
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{(feeb-image-name}~feeb-image\textbf{)}}}] \begin{flushleft}
+The name of the feeb. (Maybe you know it's weakpoints?)
+\par\end{flushleft}
+\item [{\textsf{\textbf{(feeb-image-facing}~feeb-image\textbf{)}}}] \begin{flushleft}
+The facing of the feeb. This way the brain function can 
+see if the feeb-image either sees the feeb which is playing or not.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(feeb-image-peeking}~feeb-image\textbf{)}}}] \begin{flushleft}
+Returns \textsf{\textbf{:peek-left}} or \textsf{\textbf{:peek-right}} if the
+feeb is peeking to (its) left or right, or \textsf{\textbf{nil}} if
+not.
+\par\end{flushleft}
+\item [{\textsf{\textbf{(fireball-image-direction}~fireball-image\textbf{)}}}] \begin{flushleft}
+The direction where the fireball image is going to.
+\par\end{flushleft}
+\end{lyxlist}
+
+\subsubsection{Vision-left and vision-right}
+
+\textsf{\emph{vision-left}} and \textsf{\emph{vision-right}} are vectors
+similar to vision, but they are less precise in the contents. Also
+their valid contents are limited by \textsf{\textbf{(line-of-sight}~\emph{status}\textbf{)}},
+so \textsf{\textbf{(aref}~\emph{vision-left}~\textbf{(line-of-sight}~\emph{status}\textbf{))}},
+for example, will return \textsf{\textbf{:unknown}}.
+
+Note that feebs that are not peeking, mushrooms and carcasses are
+\emph{not} be detected by these vectors. Also, if there is a feeb
+peeking to the opposite side, it won't be detected either. The
+elements in \textsf{\textbf{vision-left}} and \textsf{\textbf{vision-right}}
+are lists containing these elements:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{:peek-letf}}}] This means that in that square there
+is a feeb peeking to (its) left. 
+\item [{\textsf{\textbf{:peek-right}}}] This means that in that square
+there is a feeb peeking to (its) right. 
+\item [{\textsf{\textbf{:rock}}}] This square is just a wall. In this case,
+this is the only element in the square.
+\end{lyxlist}
+
+\subsection{Extra functions provided}
+
+Before making the brain of your feeb, you might want to take a look
+at the Extra functions that are available at the end of the feebs.lisp
+file. The only thing you need so you can use them is to see their
+code and know what they do.
+
+
+\subsection{Changing the map layout}
+
+It is possible to change the layout of the map by calling
+\textsf{\textbf{(change-layout}~new-layout\textbf{)}}.
+There are a few predefined mazes that are in variables \textsf{\textbf{{*}maze-0{*}}}
+(which is set by default) throw \textsf{\textbf{{*}maze-5{*}}}.
+In a layout, `X' represents a wall, `e' represents a feeb entry point
+(there will be as many entry points as feebs in the maze at the same time),
+`m' represents a mushroom site and ` ' is a blank space.
+
+If you want to create a new map, you can start by an empty template
+of any size that is provided by \textsf{\textbf{(make-template}~x-size~y-size\textbf{)}},
+or you can get a reandom map calling
+\textsf{\textbf{(generate-maze}~x-size~y-size~\textbf{:density}~density\textbf{)}}
+The density is a number, recomended to be between 0.25 and 0.45,
+which tells the portion of the maze should be blank spaces.
+The function quits after a while if it doesn't meet this portion. See
+its documentation for more details and options.
+
+
+\subsection{Graphics}
+
+With this version of the game, it's possible to choose the graphics
+of a feeb when creating it, so your feeb will be more personalized.
+
+The graphic of a feeb is defined by an image file, which should have
+three colunms by eight lines of pictures of the same size. The four
+first lines must be the animations of the feeb walking up, left, down
+and right, respectively. The next four lines must be the pictures
+of the feeb flaming up, left, down and right, respectively. To see
+an example, see {}``default-feeb.png''.
+
+After creating the image file, you must call \textsf{\textbf{(create-graphics}}\textsf{~path-to-image-file}\textsf{\textbf{)}}.
+If you now how to work with sdl surfaces in lispbuilder, you may use
+the function with a surface instead of a image file; or you can call
+\textsf{\textbf{(create-graphics}~path-to-image-file~nil\textbf{)}}
+if the surface should not be freed after the call. The result must
+be the third argument given to define-feeb.
+
+
+\subsection{Starting the game}
+
+The game loop is started by calling \textsf{\textbf{(simple-play)}}.
+
+
+
+\section{Contests}
+
+I sugest that you see this chapter only after you have created at
+least a basic brain feeb, which is better than the (simple) provided
+brain, or if you want to participate of a contest or a game with
+your friends.
+
+
+\subsection{\label{sub:Map}Map}
+
+It is possible to get the maze map during the game, but with only
+the corridors. Note that the function that gets the map is purposely
+a little slow, so, invoking it too many times in a contest that uses
+timing atributes is not a good idea; anyway, it is possible to invoke
+this function before defining the feeb, and store its value somewhere.
+Also note that the map returned does not have any information about
+what is really in the maze, but only the possible ways.
+
+To get the map, one can call \textsf{\textbf{(get-maze-map)}}. This
+function will return \textsf{\textbf{nil}} if parameter
+\textsf{\textbf{'may-get-maze-map-p}} is also \textsf{\textbf{nil}}.
+Otherwise, the map returned is an array, so that calling
+\textsf{\textbf{(aref}~map~x~y\textbf{)}} will get the contents
+in the position (x,y) (like euclidean but inverting the y axis).
+The contents of a cell could be one of these:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{:mushroom-place}}}] A mushroom patch, i.e. when
+a mushroom is reincarnate, it could reincarnate here. 
+\item [{\textsf{\textbf{:feeb-entry-place}}}] A feeb entry, i.e. if a carcass
+rots a feeb can appear here. 
+\item [{\textsf{\textbf{:rock}}}] A wall. Feebs cannot come to this place. 
+\item [{\textsf{\textbf{nil}}}] An {}``empty'' place, i.e. neither of
+the previous. 
+\end{lyxlist}
+
+This map can safelly be used since \textsf{\textbf{(get-maze-map)}} makes
+a new copy every time it is called.
+
+\subsection{Timing}
+
+There are also some timing atributes that can be given to the game.
+The more time the feeb takes make a decision, greater is the probability
+of its command to be aborted.
+
+To make this available, someone must set these parameters:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{'slow-feeb-noop-switch}}}] If is non-nil, 
+there is a possibility that the move of a feeb is aborted according 
+to its function time. 
+\item [{\textsf{\textbf{'slow-feeb-noop-factor}}}] The probability 
+of the feeb to abort will be this factor times the amount of time 
+the feeb takes to have a decision, divided by the total time taken 
+by all the feebs in the current turn or divided by a reference time. 
+\item [{\textsf{\textbf{'reference-time}}}] Time taken by reference 
+if non-nil.
+\item [{\textsf{\textbf{'points-for-slow-down}}}] Points earned when 
+a feeb's move is aborted due to slowness.
+\end{lyxlist}
+
+\subsection{Sense of location}
+
+Some accessors related to position and orientation of the feeb can
+be turned off.
+
+These are the parameters:
+
+\begin{lyxlist}{00.00.0000}
+\item [{\textsf{\textbf{'sense-location-p}}}] If nil, 
+\textsf{\textbf{x-position}} and \textsf{\textbf{y-position}}
+will return nil when someone tries to invoke it. 
+Otherwise return the position.
+\end{lyxlist}
+
+\subsection{Changing the rules}
+
+To change the rules of the contest, they must be changed before the
+feebs are defined, because in a feeb definition it could use the values
+of the variables to make a global strategy.
+
+All the parameters, values and documentation that can be listed using 
+\textsf{\textbf{(list-parameter-settings)}} can be changed using 
+\textsf{\textbf{(change-feeb-parm name value)}}, which is deactivated 
+during the game. Also, they all have documentation about themselves, so feel free to use 
+\textsf{\textbf{(documentation~}}\textsf{'parameter~'feeb-parm}\textsf{\textbf{)}}
+and see what each parameter does. Documentation is available to external
+functions as well.
+
+
+\section{Reference}
+
+\begin{quote}
+Fahlman, S. E. \textbf{\emph{Planet of the Feebs -}} \emph{A Somewhat
+Educational Game.} \url{ftp://ftp.csl.sri.com/pub/users/gilham/feebs/feebs.tex}. 
+\end{quote}
+
+\end{document}

Added: graphics/graphics.lisp
==============================================================================
--- (empty file)
+++ graphics/graphics.lisp	Sat Feb 16 15:01:42 2008
@@ -0,0 +1,216 @@
+;;; -*- Common Lisp -*-
+
+#|  Copyright (c) 2007,2008 Gustavo Henrique Milar�
+
+    This file is part of The Feebs War.
+
+    The Feebs War is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    The Feebs War is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with The Feebs War.  If not, see <http://www.gnu.org/licenses/>.
+|#
+
+(in-package :the-feebs-war)
+
+(defun print-direction (dir)
+  (case dir
+    (0 #\N)
+    (1 #\E)
+    (2 #\S)
+    (3 #\W)))
+
+(defun print-map ()
+  (dotimes (y *maze-y-size*)
+    (dotimes (x *maze-x-size*)
+      (let ((elt (aref *maze* x y)))
+	(apply 'format t
+	       (cond
+		 ((wallp elt) 
+		  (list " XX"))
+		 ((feeb-p (car elt))
+		  (list "F~1d~a"
+			(position (feeb-name (car elt)) *feebs* :key #'feeb-name)
+			(print-direction (feeb-facing (car elt)))))
+		 ((fireball-p (car elt))
+		  (list " *~a" (print-direction (fireball-direction (car elt)))))
+		 ((eq (car elt) :mushroom)
+		  (list " mm"))
+		 ((eq (car elt) :carcass)
+		  (list " cc"))
+		 (t (list "   "))))))
+    (format t "~%")))
+
+(defun simple-play (&optional layout)
+  (if layout
+      (change-layout layout))
+  (make-auto-feebs (- 10 (length *feebs-to-be*)))
+  (initialize-feebs)
+  (start-round)
+  (loop do
+	(play-one-turn)
+	(print-map)
+	(sleep 0.7)
+	(format t "~%~%")
+	(if (finish-game-p) (return)))
+  (format t "Game Over!!~%~%Scores:~%~%")
+  (dolist (feeb *feebs*)
+    (format t "~a: ~d~%" (feeb-name feeb) (feeb-score feeb))))
+
+
+#|
+
+
+(defconst *default-graphics*
+  (make-feeb-graphics
+   (load-and-convert-image "default-feeb.bmp")))
+
+(defvar *cell-width* 32)
+(defvar *cell-heigth* 32)
+
+(defstruct graphic
+  (walk (make-direction))
+  (flaming (make-direction)))
+
+(defstruct (direction (:conc-name nil))
+  (up (make-array 3))
+  (left (make-array 3))
+  (down (make-array 3))
+  (right (make-array 3)))
+
+(defun make-feeb-graphics (surface)
+  
+  (let ((graphic (make-graphic)))
+    (progn
+      (loop for field in '(walk flaming)
+	    and y0 from 0 by (* 4 *cell-heigth*) do
+	(loop for dir in '(up left right down)
+	      and y from y0 by *cell-heigth* do
+	  (loop for ind below 3
+		and x from 0 by *cell-width*
+		for aux = (surface :width *cell-width* :heigth *cell-heigth*) do
+	    (set-cell :x x :y y :width *cell-width* :heigth *cell-heigth* :surface surface)
+	    (draw-surface surface :surface aux)
+	    (setf (svref (slot-value (slot-value graphic field)
+				     dir)
+			 ind)
+		  aux))))
+      graphic)))
+
+(defgeneric create-graphics (feeb) &key (free-p t))
+
+(defmethod create-graphics ((feeb pathname))
+  (let ((surf (load-and-convert-image feeb)))
+    (make-feeb-grahpics surf)
+    (free-surface surf)))
+
+(defmethod create-graphics ((feeb surface) &key free-p)
+  (with-surface feeb
+    (make-feeb-graphics))
+  (if free-p
+      (fre-surface feeb)))
+
+
+(defvar *time* 0)
+
+(defun human-player (&rest args)
+  (declare (ignore args))
+  (sdl:with-events (:wait)
+    (:key-down-event (:key key)
+      (case key
+	(:sdl-key-up
+	 (return-from human-player :move-forward))
+	(:sdl-key-left
+	 (return-from human-player :turn-left))
+	(:sdl-key-right
+	 (return-from human-player :turn-right))
+	(:sdl-key-up
+	 (return-from human-player :turn-around))
+	(:sdl-key-space
+	 (return-from human-player :flame))
+	(:sdl-key-return
+	 (return-from human-player :wait))))
+    (:video-expose-event
+      (sdl:update-display))))
+
+
+(defun feebs (&key (delay 5) ; 4 min of game
+	           human-player
+	           files &aux (time 0))
+  "The main loop program. Single-step is no longer available.
+If human-player is supplied, it is taken as the name of human player,
+wich will controll a feeb with the keyboard. The end of the game
+only occurs if the player press ESC.
+If there is no human, *game-length* is used instead.
+A number of auto-feebs feebs are created by the system.
+Also, if there are more feebs supplied than places,
+the feeb wich is killed gives room to another feeb to be born."
+  (initialize-feebs)
+  (setf (sdl:frame-rate) 10)
+
+  (init-maze *layout*)
+
+  (dolist (file files)
+    (load file))
+  (if human-player
+    (define-feeb
+	human-player
+	#'human-player))
+  
+  (sdl:with-init ()
+    (sdl:with-display ()
+      (sdl:with-events ()
+	(:idle ()
+	  (sdl:update-display)
+	  (if zerop time
+	      (progn
+		(setf time delay)
+		(play-one-turn)
+		(when (not *continue*)
+		  (return)))
+	      (decf time)))
+      ))
+
+  (setf *feebs-to-be* nil))
+
+;;; Feeb creation.
+
+;; This a little better version of conservative-brain
+;; all others (stupid or redundant) brains of original
+;; feebs.lisp were eliminated
+(defun simple-brain (status proximity vision vision-left vision-right)
+  (declare (ignore vision-left vision-right))
+  (let ((stuff (my-square proximity)))
+    (cond ((and (consp stuff) (member :mushroom stuff :test #'eq))
+	   :eat-mushroom)
+	  ((and (consp stuff) (member :carcass stuff :test #'eq))
+	   :eat-carcass)
+	  ((and (ready-to-fire status)
+		(> (energy-reserve status) 30)
+		(dotimes (index (min (line-of-sight status) 5))
+		  (if (find-if #'feeb-image-p (aref vision index))
+		      (return t))))
+	   :flame)
+	  ((and (not (eq (left-square proximity) :rock))
+		(> 2 (random 10)))
+	   :turn-left)
+	  ((and (not (eq (right-square proximity) :rock))
+		(> 2 (random 10)))
+	   :turn-right)
+	  ((plusp (line-of-sight status))
+	   :move-forward)
+	  ((not (wallp (left-square proximity)))
+	   :turn-left)
+	  ((not (wallp (right-square proximity)))
+	   :turn-right)
+	  ((not (wallp (rear-square proximity)))
+	   :turn-around))))
+
+|#



More information about the The-feebs-war-cvs mailing list