[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