[the-feebs-war-cvs] r5 -
gmilare at common-lisp.net
gmilare at common-lisp.net
Thu Dec 20 20:16:45 UTC 2007
Author: gmilare
Date: Thu Dec 20 15:16:44 2007
New Revision: 5
Modified:
brains.lisp
extra.lisp
graphics.lisp
main.lisp
package.lisp
system.lisp
Log:
Modified: brains.lisp
==============================================================================
--- brains.lisp (original)
+++ brains.lisp Thu Dec 20 15:16:44 2007
@@ -5,7 +5,6 @@
;;; 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)))
Modified: extra.lisp
==============================================================================
--- extra.lisp (original)
+++ extra.lisp Thu Dec 20 15:16:44 2007
@@ -98,6 +98,9 @@
(the boolean
(eq :rock 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)
Modified: graphics.lisp
==============================================================================
--- graphics.lisp (original)
+++ graphics.lisp Thu Dec 20 15:16:44 2007
@@ -54,7 +54,7 @@
(make-auto-feebs (- 10 (length *feebs-to-be*)))
(initialize-feebs)
(loop repeat *game-length* do
- (play-one-turn) (print-map) (sleep 0.3) (format t "~%~%"))
+ (play-one-turn) (print-map) (sleep 0.7) (format t "~%~%"))
(format t "Fim de jogo!!~%~%Pontuações:~%~%")
(dolist (feeb *feebs*)
(format t "~a: ~d~%" (name (feeb-status feeb)) (score (feeb-status feeb)))))
@@ -62,12 +62,125 @@
#|
+
+(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)
+(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))
Modified: main.lisp
==============================================================================
--- main.lisp (original)
+++ main.lisp Thu Dec 20 15:16:44 2007
@@ -23,9 +23,10 @@
;;; Some functions
-(defmacro def-feeb-parm (name value doc)
+(defmacro define-parameter (name value doc)
`(progn
(defvar ,name ,value ,doc)
+ (export ,name)
(pushnew ',name *feeb-parameters*)))
(defun list-parameter-settings ()
@@ -34,207 +35,42 @@
(push (cons parm (symbol-value parm)) settings))
settings))
-(defun chance (ratio)
- (< (random (denominator ratio)) (numerator ratio)))
-
-;;; General game parameters:
-
-(def-feeb-parm *game-length* 320
- "Number of cycles in the simulation.")
-
-(def-feeb-parm *number-of-auto-feebs* 0
- "Number of dumb system-provided feebs.")
-
-(def-feeb-parm *slow-feeb-noop-switch* nil
- "If non-null, each feeb has a chance of having its orders aborted in
- proportion to the time it takes to produce them.
- See *slow-feeb-noop-factor*.")
-
-(def-feeb-parm *slow-feeb-noop-factor* 1/4
- "If *slow-feeb-noop-switch* is non-null, a feeb's orders will be aborted
- with probability equal to the product of this factor times the time
- taken by this feeb divided by *reference-time*, if non-nil, or
- the total time taken by all feebs this turn otherwise.")
-
-(def-feeb-parm *reference-time* nil
- "Time taken by reference if non-nil. See *slow-feeb-noop-factor*.")
-
-(def-feeb-parm *sense-location-p* t
- "If non-null, x-position and y-position will return nil when
- some a behavior function tries to invoke it.")
-
-;;(def-feeb-parm *sense-facing-p* t
-;; "If non-null, facing will return nil when one tries to
-;; invoke it.")
-
-;;; Scoring:
-
-(def-feeb-parm *points-for-killing* 5
- "Added to one's score for killing an opponent.")
-
-(def-feeb-parm *points-for-dying* -3
- "Added to one's score for being killed or starving.")
-
-(def-feeb-parm *points-for-slow-down* -1
- "Points earned when a feeb's move is aborted due to slowness.")
-
-;;; Cheating
-
-(def-feeb-parm *exclude-cheater-p* nil
- "Tells if a feeb is excluded from the game when a cheating is done.")
-
-(def-feeb-parm *warn-when-cheating-p* t
- "Tells if a continuable error must be signaled when a cheating is done.")
;;; Characteristics of the maze:
-(def-feeb-parm *may-get-maze-map-p* t
+(define-parameter *may-get-maze-map-p* t
"Tells if the function (get-maze-map) returns the map layout of nil
during the game.")
-(def-feeb-parm *maze-x-size* 32
- "Number of columns in the maze.")
-
-(def-feeb-parm *maze-y-size* 32
- "Number of rows in the maze.")
-
-(def-feeb-parm *number-of-mushrooms* 8
- "Average number of mushrooms in the maze at any given time.")
-
;;; Energies:
-(def-feeb-parm *flame-energy* 10
- "Energy used when a feeb flames.")
-(def-feeb-parm *mushroom-energy* 50
- "Energy gained when a mushroom is eaten.")
-
-(def-feeb-parm *carcass-energy* 30
- "Energy gained by feeding on a carcass.")
-
-(def-feeb-parm *maximum-energy* 100
- "The most energy a feeb can accumulate.")
+;;; Carcasses:
-(def-feeb-parm *starting-energy* 50
- "Smallest amount of energy a feeb will start with.")
-;;; Carcasses:
+;;; Fireballs:
-(def-feeb-parm *carcass-guaranteed-lifetime* 5
- "Minimum number of turns a carcass will hang around.")
-(def-feeb-parm *carcass-rot-probability* 1/3
- "Chance of a carcass rotting away each turn after its guaranteed lifetime.")
+;;; Tests that behavior functions might use
-;;; Fireballs:
+(declare (inline feeb-image-p fireball-image-p))
-(def-feeb-parm *fireball-dissipation-probability* 1/5
- "Chance that a fireball will dissipate each turn after it is fired.")
+(defun feeb-image-p (thing)
+ (typep thing 'feeb))
-(def-feeb-parm *fireball-reflection-probability* 2/3
- "Chance that a fireball will reflect when it hits a wall.")
+(defun fireball-image-p (thing)
+ (typep thing 'fireball))
-(def-feeb-parm *flame-recovery-probability* 1/3
- "Chance a feeb will regain its ability to flame each turn after flaming once.")
-;;; Structures:
-
-;;; The Feeb structure contains all of the info relevant to a particular feeb.
-;;; The info available to the brain function is in the Status sub-structure.
-
-(defstruct (feeb
- (:print-function print-feeb)
- (:constructor make-feeb (id brain)))
- id
- brain
- image
- status
- proximity
- time
- last-score
- last-kills
- facing
- x-position
- y-position
- (dead-p nil)
- (turns-since-flamed 0)
- (vision (make-array (max *maze-x-size* *maze-y-size*)))
- (vision-left (make-array (max *maze-x-size* *maze-y-size*)))
- (vision-right (make-array (max *maze-x-size* *maze-y-size*))))
-
-(defstruct (status
- (:conc-name nil)
- (:constructor make-status (name graphics)))
- (name "" :read-only t)
- facing
- graphics
- x-position
- y-position
- peeking
- line-of-sight
- (energy-reserve *starting-energy*)
- (score 0)
- (kills 0)
- (ready-to-fire t)
- (aborted nil)
- (last-move :dead))
-
-(defun print-feeb (structure stream depth)
- (declare (ignore depth))
- (format stream "#<Feeb ~S>"
- (name (feeb-status structure))))
-
-
-(defstruct (proximity
- (:conc-name nil))
- my-square
- rear-square
- left-square
- right-square)
-
-
-;;; These image structures are used to represent feebs and fireballs in
-;;; the sensory displays of other feebs.
-
-(defstruct (feeb-image
- (:print-function print-feeb-image)
- (:constructor make-feeb-image (name feeb)))
- (name "" :read-only t)
- facing
- (feeb nil :read-only t)
- peeking)
-
-(defun print-feeb-image (structure stream depth)
- (declare (ignore depth))
- (format stream "#<Feeb-Image of ~S facing ~S>"
- (feeb-image-name structure)
- (feeb-image-facing structure)))
-
-(defstruct (fireball-image
- (:print-function print-fireball-image)
- (:constructor make-fireball-image (direction owner x y dx dy)))
- direction
- owner
- x
- y
- dx
- dy
- (new t))
-
-(defun print-fireball-image (structure stream depth)
- (declare (ignore depth))
- (format stream "#<Fireball moving ~S>"
- (fireball-image-direction structure)))
-
-(defstruct (pos (:constructor make-pos (x y)))
- x
- y)
+;;; The maze
;;; Changing the maze
(defun change-layout (layout)
+ "Changes the layout of the map. See variables
+*maze-0* throw *maze-5* for examples (or options) of layouts"
(when *feebs-to-be*
(warn "There are some feebs that have already been defined.
They could have used (get-maze-map). Those are they:
@@ -246,22 +82,33 @@
(error "Not all the strings in ~a have the same size." layout)))
(setf *layout* layout
*maze-y-size* y
- *maze-x-size* x)))
+ *maze-x-size* x))
+ (init-maze))
(defun get-maze-map ()
- (when *may-get-maze-map-p*
- (unless (and *maze* *fake-maze*)
- (init-maze))
- (let ((new-maze (make-array (list *maze-x-size* *maze-y-size*))))
- (dotimes (x *maze-x-size*)
- (dotimes (y *maze-y-size*)
- (setf (aref new-maze x y) (aref *fake-maze* x y))))
- new-maze)))
+ "Gets the current maze in the map. It returns an array
+of *maze-x-size* by *maze-y-size*. Each element of the
+array is one of these:
+ :rock - a wall
+ :mushroom-place - here is a place where mushrooms can grow up
+ :feeb-entry-place - here is a place where a feeb can reincarnate
+ nil - nothing special
+Just remember that variables can change the behavior of this function,
+like *may-get-maze-map-p* which, if nil, makes this function return
+an array of nils"
+ (let ((new-maze (make-array (list *maze-x-size* *maze-y-size*))))
+ (dotimes (x *maze-x-size*)
+ (dotimes (y *maze-y-size*)
+ (setf (aref new-maze x y) (aref *fake-maze* x y))))
+ new-maze)))
(defun init-maze ()
(setf *maze* (make-array (list *maze-x-size* *maze-y-size*))
*fake-maze* (make-array (list *maze-x-size* *maze-y-size*))
- *entry-points* nil)
+ *entry-points* nil
+ *mushroom-sites* nil
+ *number-of-mushroom-sites* 0
+ *number-of-entry-points* 0)
(do ((rows *layout* (cdr rows))
(i (1- *maze-y-size*) (1- i)))
((null rows))
@@ -271,13 +118,17 @@
(aref *fake-maze* j i) nil)
(case (schar str j)
(#\X
- (setf (aref *maze* j i) :rock
- (aref *fake-maze* j i) :rock))
+ (setf (aref *fake-maze* j i) (and *may-get-maze-map-p* :rock)
+ (aref *maze* j i) :rock))
(#\*
- (setf (aref *fake-maze* j i) :mushroom-place)
+ (setf (aref *fake-maze* j i) (and *may-get-maze-map-p*
+ :mushroom-place))
+ (incf *number-of-mushroom-sites*)
(push (make-pos j i) *mushroom-sites*))
(#\e
- (setf (aref *fake-maze* j i) :feeb-entry-place)
+ (setf (aref *fake-maze* j i) (and *may-get-maze-map-p*
+ :feeb-entry-place))
+ (incf *number-of-entry-points*)
(push (make-pos j i) *entry-points*))
(#\space nil)
(t
@@ -294,119 +145,90 @@
*static-parameters*
(loop for (symbol . value) in (list-parameter-settings)
collect value))
- (init-maze)
- (setf *number-of-mushroom-sites* (length *mushroom-sites*)
- *number-of-entry-points* (length *entry-points*))
(create-feebs)) ; The feebs are defined here
-(defun create-mushrooms ()
- (dotimes (i (- *number-of-mushrooms* (length *mushrooms-alive*) (random 3)))
- (do ((site (nth (random *number-of-mushroom-sites*) *mushroom-sites*)
- (nth (random *number-of-mushroom-sites*) *mushroom-sites*)))
- ((null (aref *maze* (pos-x site) (pos-y site)))
- (setf (aref *maze* (pos-x site) (pos-y site)) :mushroom)))))
-;;; Setting up the feebs.
-(defvar *feebs* nil
- "A list of all the feebs in the current game.")
+;;; Setting up the feebs.
-(defvar *next-feeb-id* 0
- "A counter used to assign a unique numerical code to each feeb.")
+(defvar *feebs* nil)
;;; Define-Feeb builds a list of feebs to create. Create-Feebs actually
;;; builds the feebs on this list.
(defvar *feebs-to-be* nil)
-(defun define-feeb (name brain &optional prepare graphs)
- (if (delete-feeb name)
+(defun define-feeb (name brain &optional initializer graphs)
+ "Defines a feeb with name NAME, behavior function BRAIN.
+The INITIALIZER key option must be either a function that
+will be called in the very start of the game, or nil.
+If there is another feeb with the same name, overwrites it
+with a case sensitive test"
+ (when (find name *feebs-to-be* :key #'car
+ :test #'string= (delete-feeb name))
(warn "Feeb ~s already exists, deleting..." name))
(push (list name brain prepare graphs) *feebs-to-be*))
(defun delete-feeb (name)
- (not
- (equal *feebs-to-be*
- (setf *feebs-to-be*
- (remove name *feebs-to-be* :key #'car :test #'string=)))))
+ "Deletes the feeb which has name NAME, causing it not to
+be created when the game begins. Does not work for feebs in
+the game"
+ (setf *feebs-to-be*
+ (remove name *feebs-to-be* :key #'car :test #'string=)))
+
+(defun list-of-feebs ()
+ "Returns a copy of the list of feebs that will be created
+when the game begins."
+ (loop for (name . rest) in *feebs-to-be*
+ collect name))
+
+(defun delete-all-feebs ()
+ "Deletes all feebs that are to be defined when the game begins."
+ (setf *feebs-to-be* nil))
(defun create-feebs ()
- (flet ((create-feeb (name brain prepare graphs)
- (let ((pos (pick-random-entry-point))
- (feeb (make-feeb *next-feeb-id* brain)))
- (incf *next-feeb-id*)
- (setf (feeb-image feeb)
- (make-feeb-image name feeb)
- (feeb-status feeb)
- (make-status name nil); (sdl:load-and-convert-image graphs))
- (feeb-proximity feeb)
- (make-proximity))
- (change-feeb-pos feeb (pos-x pos) (pos-y pos))
- (change-feeb-facing feeb (random 4))
- (push feeb *feebs*)
- (place-object (feeb-image feeb) (pos-x pos) (pos-y pos))
- (when prepare
- (let (*static-parameters* *fake-maze*)
- (funcall prepare))
- (check-cheating name)))))
- (setf *feebs* nil
- *next-feeb-id* 0)
- (dolist (feeb-spec (reverse *feebs-to-be*))
- (apply #'create-feeb feeb-spec))))
-
-;;; Start at some randomly chosen entry point. If this one is occupied,
-;;; scan successive entry points until a winner is found. Circle back
-;;; to start of list if necessary.
-
-(defun pick-random-entry-point ()
- (do ((points (nth (random *number-of-entry-points*) *entry-points*)
- (nth (random *number-of-entry-points*) *entry-points*)))
- (nil)
- (when (null (aref *maze* (pos-x points)
- (pos-y points)))
- (return points))))
-
-;;; Movement interface.
-
-(defun delete-object (thing x y)
- (when (eq thing :mushroom)
- (decf *mushrooms-alive*))
- (setf (aref *maze* x y)
- (delete thing (aref *maze* x y))))
-
-(defun place-object (thing x j)
- (when (eq thing :mushroom)
- (incf *mushrooms-alive*))
- (push thing (aref *maze* x j)))
-
-;;; Functions to change optional structure in status
-
-(defun change-feeb-pos (feeb x y)
- (setf (feeb-x-position feeb) x
- (feeb-y-position feeb) y)
- (if *sense-location-p*
- (setf (x-position (feeb-status feeb)) x
- (y-position (feeb-status feeb)) y)))
-
-(defun change-feeb-facing (feeb facing)
- (setf (feeb-facing feeb)
-;; ;; use this code to make *sense-facing-p* available
-;; ;; but be carefull - it does not really work
-;; (if (or *sense-location-p* *sense-facing-p*)
-;; (setf (facing (feeb-status feeb))
-;; facing)
-;; facing)
- (setf (facing (feeb-status feeb))
- (setf (feeb-image-facing (feeb-image feeb))
- facing))))
-
-(defun kill-feeb (feeb)
- (setf *dead-feebs* (nconc *dead-feebs* (list feeb))
- (feeb-dead-p feeb) t)
- (let* ((status (feeb-status feeb))
- (x (feeb-x-position feeb))
- (y (feeb-y-position feeb)))
- (push (list 0 x y) *carcasses*)
- (incf (score status) *points-for-dying*)
- (delete-object (feeb-image feeb) x y)
- (place-object :carcass x y)))
+ (let ((entries (sort *entry-points* #'(lambda (x y)
+ (declare (ignore x y))
+ (zerop (random 2))))))
+ (setf *feebs* nil)
+ (dolist (feeb-spec *feebs-to-be*)
+ (let ((pos (pop entries)))
+ (apply 'create-feeb (car pos) (cdr pos) feeb-spec)))))
+
+
+(defun play-one-turn ()
+ ;; This is defined by rules
+ (start-turn)
+ ;; Maybe grow up mushrooms
+ (let ((m-sites (sort *mushroom-sites*
+ #'(lambda (x y)
+ (declare (ignore x y))
+ (zerop (random 2))))))
+ (dotimes (i *mushrooms-to-grow*)
+ (let ((site (pop m-sites)))
+ (create-mushroom (car site) (cdr site)))))
+ ;; Rot some carcasses:
+ (loop for carc in *carcasses*
+ with ncarcasses do
+ (unless (rot-carcass (second carc) (third carc) (first carc))
+ (push carc ncarcasses)
+ (incf (first carc))
+ (reincarnate-feeb (pop *dead-feebs*))))
+ ;; Move some fireballs:
+ (dolist (fireball *fireballs-flying*)
+ (move-fireball fireball))
+ ;; Playing with the feebs:
+ (dolist (feeb *feebs*)
+ (unless (feeb-dead-p feeb)
+ ;; Starve the feeb:
+ (when (<= (decf (feeb-energy-reserve feeb)) 0)
+ (kill-feeb feeb :starve))
+ ;; Compute vision for the feeb:
+ (compute-vision feeb)
+ ;; Collect the feeb's move
+ (make-move-choice feeb)))
+ ;; Do all the feebs' moves.
+ (dolist (feeb *feebs*)
+ (unless (feeb-dead-p feeb)
+ (setf (feeb-peeking feeb) nil)
+ (move-feeb feeb (feeb-last-move feeb)))))
Modified: package.lisp
==============================================================================
--- package.lisp (original)
+++ package.lisp Thu Dec 20 15:16:44 2007
@@ -45,7 +45,7 @@
(defpackage :feebs
- (:use :common-lisp)
+ (:use :common-lisp :lispbuilder-sdl :lispbuilder-sdl-image :cffi)
;; Export everything we want the players to get their hands on.
(:export *number-of-feebs* *game-length*
*number-of-auto-feebs*
@@ -64,8 +64,10 @@
;; Probabilities
*carcass-guaranteed-lifetime*
*carcass-rot-probability*
+ *fireball-guaranteed-lifetime*
*fireball-dissipation-probability*
*fireball-reflection-probability*
+ *flame-no-recovery-time*
*flame-recovery-probability*
;; Difficulty variables
@@ -122,7 +124,7 @@
behind-dx behind-dy
;; Others
- wallp
+ wallp chance
;; Graphics for alpha release
simple-play print-map))
@@ -136,6 +138,9 @@
(defconstant south 2)
(defconstant west 3)
+;;; This is t if someone call (asdf:oos 'asdf:load-op 'feebs-c-interface)
+
+(defvar *c-interface-available* nil)
;;; Parameters that affect strategy of the game.
@@ -190,8 +195,10 @@
"XXXXX XXXXXXXXXXXXX X"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
-(defparameter *maze-x-size* 32)
-(defparameter *maze-y-size* 32)
+(defparameter *maze-x-size* 32
+ "Horizontal size of the maze")
+(defparameter *maze-y-size* 32
+ "Vertical size of the maze")
;;; Quantities during the game
Modified: system.lisp
==============================================================================
--- system.lisp (original)
+++ system.lisp Thu Dec 20 15:16:44 2007
@@ -21,26 +21,205 @@
(in-package :feebs)
-(defun reincarnate-feeb (feeb)
- (let ((pos (nth (random (length *entry-points*)) *entry-points*))
+
+;;; We start defining the main system rules by defining the classes
+
+;;; This class is used by the system
+
+(defclass object ()
+ ((direction :accessor object-direction)
+ (x-position :accessor object-x-position)
+ (y-position :accessor object-y-position)
+ (lifetime :accessor object-lifetime :initform 0)))
+
+(defclass feeb (object)
+ (;; These are structures accessible from behavior functions.
+ ;; These (whose :reader start with feeb-image)
+ ;; are intended to be accessed by other feebs
+ (name :accessor feeb-name :reader name :initarg :name
+ :reader feeb-image-name)
+ (direction :reader facing :reader feeb-image-facing
+ :initform (random 4))
+ (peeking :accessor feeb-peeking :reader peeking
+ :reader feeb-image-peeking)
+
+ ;; These are intended to be accessed only by the feeb itself
+ (x-position :reader x-position :initform 0 :initarg :x-position)
+ (y-position :reader y-position :initform 0 :initarg :y-position)
+ (line-of-sight :accessor feeb-line-of-sight :reader line-of-sight
+ :initform 0)
+ (energy-reserve :accessor feeb-energy-reserve :reader energy-reserve
+ :initform *starting-energy*)
+ (ready-to-fire :accessor feeb-ready-to-fire :reader ready-to-fire
+ :initform t)
+ (aborted :accessor feeb-aborted :reader aborted)
+ (last-move :accessor feeb-last-move :reader last-move
+ :initform :dead)
+
+ ;; These are available for the system only
+ (brain :accessor feeb-brain :initarg :brain)
+ (graphics :accessor feeb-graphics :initarg :graphics)
+ (time :accessor feeb-time :initform 0)
+ (last-score :accessor feeb-last-score :initform 0)
+ (last-kills :accessor feeb-last-kills :initform 0)
+ (score :accessor feeb-score :initform 0)
+ (kills :accessor feeb-kills :initform 0)
+ (dead-p :accessor feeb-dead-p)
+ (playing-p :accessor feeb-playing-p)
+ (turns-since-flamed :accessor feeb-turns-since-flamed :initform 0)
+ (proximity :accessor feeb-proximity :initform (make-proximity))
+ (vision :accessor feeb-vision
+ :initform (make-array (list (max *maze-y-size* *maze-x-size*))))
+ (vision-left :accessor feeb-vision-left
+ :initform (make-array (list (max *maze-y-size* *maze-x-size*))))
+ (vision-right :accessor feeb-vision-right
+ :initform (make-array (list (max *maze-y-size* *maze-x-size*))))))
+
+;;; These make sure that these accessors are just available
+;;; for the feeb itself
+
+(defmethod name :around ((fb feeb))
+ (if (feeb-playing-p fb) ;; check if the feeb itself is accessing its name
+ (call-next-method)))
+
+(defmethod facing :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod peeking :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod graphics :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod x-position :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod y-position :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod line-of-sight :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod energy-reserve :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod ready-to-fire :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod aborted :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+(defmethod last-move :around ((fb feeb))
+ (if (feeb-playing-p fb)
+ (call-next-method)))
+
+
+
+;;; Place and delete
+
+(defun create-mushroom (x y)
+ (unless (member :mushroom (aref *maze* x y))
+ (place-object :mushroom x y)
+ t))
+
+(defun rot-carcass (x y)
+ (delete-object :carcass x y)
+ t)
+
+(defun delete-object (thing x y)
+ (when (eq thing :mushroom)
+ (decf *mushrooms-alive*))
+ (setf (aref *maze* x y)
+ (delete thing (aref *maze* x y))))
+
+(defun place-object (thing x j)
+ (when (eq thing :mushroom)
+ (incf *mushrooms-alive*))
+ (push thing (aref *maze* x j)))
+
+(defun change-object-pos (obj x y)
+ (delete-object obj (object-x-position obj)
+ (object-y-position obj))
+ (place-object obj x y)
+ (setf (object-x-position obj) x
+ (object-y-position obj) y))
+
+(defun get-forward-pos (object)
+ (let ((new-x (+ (forward-dx (object-direction object))
+ (object-x-position object)))
+ (new-y (+ (forward-dy (object-direction object))
+ (object-y-position object))))
+ (values (aref *maze* new-x new-y) new-x new-y)))
+
+
+;;; --**-- System Rules --**--
+
+;;; -*- General Rules -*-
+
+(defmethod start-round ()
+ t)
+
+(defmethod start-turn ()
+ t)
+
+(defmethod create-feeb (x-pos y-pos name brain prepare graphs)
+ (let ((feeb (make-instance 'feeb
+ :name name
+ :brain brain
+ :graphics (if graphs
+ (sdl:load-and-convert-image graphs))
+ :x-position x-pos
+ :y-position y-pos)))
+ (push feeb *feebs*)
+ (place-object (feeb-image feeb) x-pos y-pos)
+ (when prepare
+ (funcall prepare))))
+
+
+
+;;; -*- Dying and Killing -*-
+
+;;; Reincarnating
+
+(defmethod reincarnate-feeb ((feeb feeb))
+ (let ((pos (nth (random *number-of-entry-points*) *entry-points*))
(status (feeb-status feeb)))
- (place-object (feeb-image feeb)
- (pos-x pos) (pos-y pos))
- (change-feeb-pos feeb (pos-x pos) (pos-y pos))
- (change-feeb-facing feeb (random 4))
- (setf (feeb-dead-p feeb) nil
+ (change-object-pos feeb (pos-x pos) (pos-y pos))
+ (setf (feeb-facing feeb) (random 4)
+ (feeb-dead-p feeb) nil
(ready-to-fire status) t
(energy-reserve status) *starting-energy*
(last-move status) :dead)))
+;;; Dying
+
+(defmethod destroy-object ((feeb feeb) cause)
+ (setf *dead-feebs* (nconc *dead-feebs* (list feeb))
+ (feeb-dead-p feeb) t)
+ (let* ((status (feeb-status feeb))
+ (x (feeb-x-position feeb))
+ (y (feeb-y-position feeb)))
+ (push (list 0 x y) *carcasses*)
+ (delete-object (feeb-image feeb) x y)
+ (place-object :carcass x y)))
-;;; Vision calculation.
-;;; These guys tell us offsets given an orientation.
+
+;;; -*- Vision Calculation -*-
+
+;;; Computes what the feeb is seeing
(defun compute-vision (feeb)
- (let ((status (feeb-status feeb))
- (proximity (feeb-proximity feeb))
+ (let ((proximity (feeb-proximity feeb))
(vision (feeb-vision feeb))
(vision-left (feeb-vision-left feeb))
(vision-right (feeb-vision-right feeb))
@@ -62,7 +241,7 @@
(setf x (+ x (forward-dx facing))
y (+ y (forward-dy facing)))
;; Figure out which direction to scan in.
- (case (peeking status)
+ (case (feeb-peeking feeb)
(:left (setf facing (left-of facing)))
(:right (setf facing (right-of facing))))
(setf vision-dx (forward-dx facing)
@@ -92,213 +271,119 @@
;;; A peeking feeb must be facing in the specified direction in order to count.
(defun side-imagify (stuff facing)
- (cond
- ((wallp stuff)
- stuff)
- ((find-if #'(lambda (thing)
- (and (feeb-image-p thing)
- (peeking (feeb-status (feeb-image-feeb thing)))
- (= facing (feeb-image-facing thing))
- (setf facing thing)))
- stuff)
- (peeking (feeb-status (feeb-image-feeb facing))))
- (t nil)))
-
-;;; Movement.
-
-;;; Each turn, the following stuff has to happen:
-;;; 1. Bump the turn counter; end the game if we should.
-;;; 2. Maybe grow some mushrooms.
-;;; 3. Maybe disappear some carcasses.
-;;; 4. Move fireballs around.
-;;; 5. See if any feebs have starved.
-;;; 6. See if any feebs can flame again.
-;;; 7. Compute vision and stuff for feebs.
-;;; 8. Collect the feebs' moves.
-;;; 9. Do the feeb's moves.
-
-(defun play-one-turn ()
- ;; Grow some mushrooms:
- (dotimes (x (- *number-of-mushrooms* *mushrooms-alive*))
- (let* ((site (nth (random *number-of-mushroom-sites*) *mushroom-sites*))
- (x (pos-x site))
- (y (pos-y site)))
- (unless (member :mushroom (aref *maze* x y))
- (place-object :mushroom x y))))
- ;; Rot some carcasses:
- (dolist (carc *carcasses*)
- (when (and
- (> (incf (first carc) *carcass-guaranteed-lifetime*))
- (chance *carcass-rot-probability*))
- (delete-object :carcass (second carc) (third carc))
- (setf *carcasses* (delete carc *carcasses*))
- (if *dead-feebs*
- (reincarnate-feeb (pop *dead-feebs*)))))
- ;; Move some fireballs:
- (dolist (fireball *fireballs-flying*)
- (move-one-fireball fireball))
- ;; Starve some feebs:
- (dolist (feeb *feebs*)
- (unless (feeb-dead-p feeb)
- (when (<= (decf (energy-reserve (feeb-status feeb))) 0)
- (kill-feeb feeb))))
- ;; Let some feebs regain the power to flame:
- (dolist (feeb *feebs*)
- (unless (and (feeb-dead-p feeb)
- (ready-to-fire (feeb-status feeb)))
- (when (and (> (incf (feeb-turns-since-flamed feeb))
- 1)
- (chance *flame-recovery-probability*))
- (setf (ready-to-fire (feeb-status feeb)) t))))
- ;; Collect all the feebs' moves, keeping track of the time each one takes.
- (let ((total-time 1))
- (dolist (feeb *feebs*)
- (unless (feeb-dead-p feeb)
- (compute-vision feeb) ; Compute vision for all the feeb.
- (let ((time (get-internal-real-time)))
- (let ( *static-parameters* *fake-maze*)
- (setf (last-move (feeb-status feeb))
- (funcall (feeb-brain feeb)
- (feeb-status feeb)
- (feeb-proximity feeb)
- (feeb-vision feeb)
- (feeb-vision-left feeb)
- (feeb-vision-right feeb))
- time (- (get-internal-real-time) time)))
- (incf total-time time)
- (setf (feeb-time feeb) time))))
- ;; Do all the feebs' moves, or perhaps abort the move according
- ;; to the time taken by the feeb.
- (setf total-time (float total-time))
- (dolist (feeb *feebs*)
- (unless (feeb-dead-p feeb)
- (if (and *slow-feeb-noop-switch*
- (< (random 1.0)
- (* *slow-feeb-noop-factor*
- (/ (float (feeb-time feeb))
- (or *reference-time* total-time)))))
- (progn
- (setf (aborted (feeb-status feeb)) t)
- (incf (score (feeb-status feeb)) *points-for-slow-down*))
- (progn
- (setf (aborted (feeb-status feeb)) nil)
- (do-move feeb (last-move (feeb-status feeb)))))
- ;; Make the image consistent with the feeb.
- (setf (feeb-image-facing (feeb-image feeb))
- (feeb-facing feeb))))))
-
-(defun move-one-fireball (fireball)
- (let ((x (fireball-image-x fireball))
- (y (fireball-image-y fireball)))
- ;; Remove fireball from current square, unless it is new.
- (if (fireball-image-new fireball)
- (setf (fireball-image-new fireball) nil)
- (delete-object fireball x y))
- ;; The fireball might dissipate.
- (when (chance *fireball-dissipation-probability*)
- (setq *fireballs-flying* (delete fireball *fireballs-flying*))
- (return-from move-one-fireball nil))
- ;; Now move it to new coordinates.
- (incf x (fireball-image-dx fireball))
- (incf y (fireball-image-dy fireball))
+ (if (wallp stuff)
+ stuff
+ (loop for thing in stuff
+ and elt = (and (feeb-image-p thing)
+ (= facing (feeb-image-facing thing))
+ (feeb-image-peeking thing))
+ if elt
+ return it)))
+
+(defparameter *mushrooms-to-grow* 0)
+
+(defun number-of-mushrooms (n)
+ (setf *mushrooms-to-grow* n))
+
+
+;;; Lets the feeb make a choice
+
+(defmethod make-move-choice ((feeb feeb))
+ (setf (last-move (feeb-status feeb))
+ (funcall (feeb-brain feeb)
+ (feeb-status feeb)
+ (feeb-proximity feeb)
+ (feeb-vision feeb)
+ (feeb-vision-left feeb)
+ (feeb-vision-right feeb))))
+
+
+
+;;; Moves the fireball
+
+(defmethod make-move ((fireball fireball))
+ ;; move it to new coordinates.
+ (let ((x (incf (fireball-x fireball)
+ (forward-dx (fireball-direction fireball))))
+ (y (incf (fireball-y fireball)
+ (forward-dy (fireball-direction fireball)))))
;; If it hits rock, either reflect or dissipate.
(when (wallp (aref *maze* x y))
- (cond ((chance *fireball-reflection-probability*)
- (setf (fireball-image-dx fireball)
- (- (fireball-image-dx fireball)))
- (setf (fireball-image-dy fireball)
- (- (fireball-image-dy fireball)))
- (setf (fireball-image-direction fireball)
- (behind (fireball-image-direction fireball)))
- (setq x (fireball-image-x fireball))
- (setq y (fireball-image-y fireball)))
- (t (setq *fireballs-flying*
- (delete fireball *fireballs-flying*))
- (return-from move-one-fireball nil))))
+ (if (and (> (incf (fireball-age fireball))
+ *fireball-guaranteed-lifetime*)
+ (chance *fireball-reflection-probability*))
+ (setf (fireball-direction fireball)
+ (behind (fireball-direction fireball))
+ x (fireball-x fireball)
+ y (fireball-y fireball))
+ (progn
+ (setf *fireballs-flying*
+ (delete fireball *fireballs-flying*))
+ (return-from move-one-fireball))))
;; Now put the fireball into the new square.
- (setf (fireball-image-x fireball) x)
- (setf (fireball-image-y fireball) y)
- (place-object fireball x y)
- ;; And destroy whatever is there.
- (delete-object :mushroom x y)
- (dolist (thing (aref *maze* x y))
- (if (feeb-image-p thing)
- (score-kill fireball (feeb-image-feeb thing))))))
-
-;;; The fireball kills the feeb. Update score for killer and victims.
-;;; No credit for the kill if you shoot yourself.
-
-(defun score-kill (fireball feeb)
- (unless (eq (fireball-image-owner fireball) feeb)
- (incf (score (feeb-status (fireball-image-owner fireball)))
- *points-for-killing*)
- (incf (kills (feeb-status (fireball-image-owner fireball)))))
- (kill-feeb feeb))
+ (setf (fireball-x fireball) x
+ (fireball-y fireball) y)
+ (change-object-pos fireball x y)))
;;; Doing feeb moves.
-(defun do-move (feeb move)
- (let ((status (feeb-status feeb))
- (facing (feeb-facing feeb)))
- ;; Peeking gets undone every move.
+(defmethod make-move ((feeb feeb) (move (eql :turn-right)))
+ (setf (feeb-facing feeb) (right-of facing)) (call-next-method))
+
+(defmethod make-move ((feeb feeb) (move (eql :turn-around)))
+ (setf (feeb-facing feeb) (behind facing)) (call-next-method))
+
+(defmethod make-move (object (move (eql :move-forward)))
+ (multiple-value-bind (stuff new-x new-y) (get-forward-pos object)
+ (when (wallp stuff)
+ (return-from make-move nil))
+ (change-object-pos object new-x new-y)
+ (let ((thing (find-if #'fireball-image-p stuff)))
+ (when thing (kill-feeb feeb thing)
+ (return-from make-move t))))
+ (call-next-method))
+
+(defmethod make-move ((feeb feeb) (move (eql :flame)))
+ (let ((x (feeb-x-position feeb))
+ (y (feeb-y-position feeb))
+ (fireball
+ (make-fireball-image (feeb-facing feeb)
+ feeb x y (forward-dx facing)
+ (forward-dy facing))))
+ (push fireball *fireballs-flying*))
+ (call-next-method))
+
+(defmethod make-move ((feeb feeb) (move (eql :eat-mushroom)))
+ (let ((x (feeb-x-position feeb))
+ (y (feeb-y-position feeb)))
+ (when (member :mushroom (aref *maze* x y))
+ (delete-object :mushroom x y)
+ t)))
+
+(defmethod make-move ((feeb feeb) (move (eql :eat-carcass)))
+ (let ((x (feeb-x-position feeb))
+ (y (feeb-y-position feeb)))
+ (when (member :carcass (aref *maze* x y))
+ t)))
+
+(defmethod make-move ((feeb feeb) (move (eql :peek-left)))
+ (unless
+ (wallp
+ (aref *maze* (+ (feeb-x-position feeb)
+ (forward-dx (feeb-facing feeb)))
+ (+ (feeb-y-position feeb)
+ (forward-dy (feeb-facing feeb)))))
+ (setf (peeking status)
+ (setf (feeb-image-peeking (feeb-image feeb)) move)))
+ (call-next-method))
+
+(defmethod make-move ((feeb feeb) (move (eql :peek-right)))
+ (unless
+ (wallp
+ (aref *maze* (+ (feeb-x-position feeb)
+ (forward-dx (feeb-facing feeb)))
+ (+ (feeb-y-position feeb)
+ (forward-dy (feeb-facing feeb)))))
(setf (peeking status)
- (setf (feeb-image-peeking (feeb-image feeb)) nil))
- (case move
- (:turn-left
- (change-feeb-facing feeb (left-of facing)))
- (:turn-right
- (change-feeb-facing feeb (right-of facing)))
- (:turn-around
- (change-feeb-facing feeb (behind facing)))
- (:move-forward
- (let* ((old-x (feeb-x-position feeb))
- (old-y (feeb-y-position feeb))
- (new-x (+ (forward-dx facing) old-x))
- (new-y (+ (forward-dy facing) old-y))
- (stuff (aref *maze* new-x new-y)))
- (when (wallp stuff)
- (return-from do-move nil))
- (delete-object (feeb-image feeb) old-x old-y)
- (change-feeb-pos feeb new-x new-y)
- (place-object (feeb-image feeb) new-x new-y)
- ;; Look for a fireball in the destination square.
- (let ((thing (find-if #'fireball-image-p stuff)))
- (when thing
- (score-kill thing feeb)
- (return-from do-move nil)))))
- (:flame
- (when (ready-to-fire status)
- (let* ((x (feeb-x-position feeb))
- (y (feeb-y-position feeb))
- (fireball (make-fireball-image
- facing feeb x y
- (forward-dx facing) (forward-dy facing))))
- ;; Queue the fireball, marked as new, but don't put it on map yet.
- (push fireball *fireballs-flying*)
- (decf (energy-reserve status) *flame-energy*)
- (setf (ready-to-fire status) nil)
- (setf (feeb-turns-since-flamed feeb) 0))))
- (:eat-mushroom
- (let* ((x (feeb-x-position feeb))
- (y (feeb-y-position feeb)))
- (when (member :mushroom (aref *maze* x y))
- (delete-object :mushroom x y)
- (setf (energy-reserve status)
- (min (+ (energy-reserve status) *mushroom-energy*)
- *maximum-energy*)))))
- (:eat-carcass
- (let* ((x (feeb-x-position feeb))
- (y (feeb-y-position feeb)))
- (when (member :carcass (aref *maze* x y))
- (setf (energy-reserve status)
- (min (+ (energy-reserve status) *carcass-energy*)
- *maximum-energy*)))))
- ((:peek-left :peek-right)
- (unless (wallp (aref *maze* (+ (feeb-x-position feeb)
- (forward-dx facing))
- (+ (feeb-y-position feeb)
- (forward-dy facing))))
- (setf (peeking status)
- (setf (feeb-image-peeking (feeb-image feeb)) move))))
- (:wait nil)
- (t (warn "Unknown feeb movement: ~a." move)))))
+ (setf (feeb-image-peeking (feeb-image feeb)) move)))
+ (call-next-method))
More information about the The-feebs-war-cvs
mailing list