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) + ,@finalize) + (declare (list ,v ,vl ,vr) + (fixnum ,count)) + (dolist (,vis ,v) + ,@vis-body) + (dolist (,vis-l ,vl) + ,@vis-l-body) + (dolist (,vis-r ,vr) + ,@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 ,@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/%7D, 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%7D%27s 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)))) + +|#