プログラミングを頑張る日記

プログラミングを勉強して、ハッカーになろう

Common Lispを頑張る(30)

さあさあ「Land of Lisp」の時間です!
今日はloopコマンドを使った進化ゲームを作っていきます!
シミュレーションゲームが大好きなのでこのゲームに早く着手したくてたまりませんでした。

ゲーム世界

マップは長方形で、ドラクエのごとく右端と左端が、上端と下端が繋がっています。
マップの大部分は乾燥帯で植物もほぼ生えません。中央にはジャングルがあり、そこは植物が育ちやすい環境です。
この世界に草食動物を配置し、彼らがどのように子孫を残し進化していくか観察するゲームです。

それでは変数を記述していきます。

CL-USER> (defparameter *width* 100)
*WIDTH*
CL-USER> (defparameter *height* 30)
*HEIGHT*
CL-USER> (defparameter *jungle* '(45 10 10 10))
*JUNGLE*
CL-USER> (defparameter *plant-energy* 80)
*PLANT-ENERGY*
CL-USER>

*height*と*width*はマップの大きさを表しています。
*jungle*は長方形のジャングルの位置を示す座標のリストです。
最初の2つの要素がジャングルの左上の角の位置を、後の2つがジャングルのサイズを表します。
*plant-energy*は各植物の持つ生命力です。動物がこの植物を食べたら80日生きられるってことです。

草を生やす

ネットスラングではないです。
植物をその座標をキーとしたハッシュテーブルに格納します。
動物たちが移動するたびにその座標に植物があるか計算しなければならず、
これは最初から重い計算になることが予想されるので効率重視のハッシュテーブルです。

CL-USER> (defparameter *plants* (make-hash-table :test #'equal))
*PLANTS*
CL-USER>

特に指定がなければ、ハッシュテーブルはキーの比較にeqlを使うそうですが、
今回はxとyの座標のペアをキーとしたいため、:testキーワードを与えequalで比較するよう指定しています。

それでは植物を生やす関数を書きます。

CL-USER> (defun random-plant (left top width height)
           (let ((pos (cons (+ left (random width)) (+ top (random height)))))
             (setf (gethash pos *plants*) t))) ;ランダムな位置(pos)を*plants*に登録
RANDOM-PLANT
CL-USER> (defun add-plants ()
           (apply #'random-plant *jungle*)      ;ジャングルの座標内に植物を生やす
           (random-plant 0 0 *width* *height*)) ;マップ全体のランダムな位置に植物を生やす
ADD-PLANTS
CL-USER>

random-plantでハッシュテーブルに座標をキーとして登録する値は、ただのtです。つまり値に意味がありません。
なぜこの様なハッシュテーブルを作るのかと言うと、Common Lispに集合を扱うデータ構造が無いことが理由だそう。
そのため、全ての植物の座標を記録するために手っ取り早くハッシュテーブルということだそうです。
値を単にtとすることで、ハッシュテーブル*plants*をキー座標の集合とみなせる、ふむふむ。

動物を作る

動物はそれぞれ独自の属性を持っていてほしいものです。構造体を作ります。

CL-USER> (defstruct animal x y energy dir genes)
ANIMAL
CL-USER>

xとyはマップ上での動物の位置を記録します。
energyはその動物の持つ生命力です。あと何日生きられるかを示します。植物を食べることで増えます。
dirは動物の向きです。

  0  1  2

  7  a  3

  6  5  4

aが動物を表します。dirが0なら翌日その動物はその動物から見て左上に進む感じだそう。
genesはその動物の遺伝子です。方向と対応した8個の整数からなるリストがgenesになるそうです。
毎日動物は向きをそのままにするか、ランダムに変えるかを決め、
その時に各方向へと向きを返る確率がリストの対応する各要素に比例すると。

(1 10 1 1 1 1 1 1)

この様な遺伝子を持った動物がいたとすると、彼はひたすら前へ前へと進んで行くということですね。
勿論たまには気まぐれに右向いたりするかもしれませんが。

それでは実際の動物を作ってみます。性別は無いそうです。課題かな。

CL-USER> (defparameter *animals*
           (list (make-animal :x (ash *width* -1)
                               :y (ash *height* -1) ;最初の動物はマップの真ん中に現れる
                               :energy 1000         ;生命力は1000、しばらく生き延びてもらうため長寿
                               :dir 0               ;左上を向いている
                               :genes (loop repeat 8 ;8回ランダムに1から10までの整数を生成してリストにする
                                           collecting (1+ (random 10))))))
*ANIMALS*
CL-USER>

*plants*と比べ、*animals*は一つの要素だけを持つただのリストです。
これはシミュレーションの中で特定の動物をリストから探す必要が無いからだそう。
シミュレーションの一日ごとに*animals*リストをスキャンして、
登録されている動物たちに各々行動させればいいだけで、
リストは線形にスキャンしていく分には十分効率がよいからこれでいいということです。

動物を動かす

CL-USER> (defun move (animal)
           (let ((dir (animal-dir animal))
                 (x (animal-x animal))
                 (y (animal-y animal)))
             (setf (animal-x animal) (mod (+ x
                                             (cond ((and (>= dir 2) (< dir 5)) 1) ;東向きならx座標に+1
                                                   ((or (= dir 1) (= dir 5)) 0)   ;北か南向きなら+0
                                                   (t -1)))                       ;西向きなら-1
                                          *width*))    ;マップから出ていかないようにmod
             (setf (animal-y animal) (mod (+ y
                                             (cond ((and (>= dir 0) (< dir 3)) -1) ;南向きなら-1
                                                   ((and (>= dir 4) (< dir 7)) 1)  ;北向きなら+1
                                                   (t 0)))                         ;それ以外なら0
                                          *height*))  ;マップから出ていかないように
             (decf (animal-energy animal))))          ;動いた後は生命力が減る
MOVE
CL-USER>

よしよし、理解できています。

動物の向きを変える

CL-USER> (defun turn (animal)
           (let ((x (random (apply #'+ (animal-genes animal))))) ;遺伝子の合計未満の整数をxに
             (labels ((angle (genes x)
                        (let ((xnu (- x (car genes)))) ;xnuはxからgenesのcar要素を引いたもの
                          (if (< xnu 0)  ;xnuが0以下なら
                              0          ;0を返す
                              (1+ (angle (cdr genes) xnu)))))) ;でなきゃ再帰する
               (setf (animal-dir animal)                            ;dirに
                     (mod (+ (animal-dir animal) (angle (animal-genes animal) x)) ;angleの戻り値を加算
                          8)))))                                    ;8は超えないように
TURN
CL-USER>

xが0になったときのxnuがangleの戻り値になるってことですね。
そしてそれが新しいdirになると。
genesのある要素が大きければ大きいほどそこで0になる確率が高いということですかね。

植物を食べる

CL-USER> (defun eat (animal)
           (let ((pos (cons (animal-x animal) (animal-y animal))))
             (when (gethash pos *plants*)
               (incf (animal-energy animal) *plant-energy*)
               (remhash pos *plants*))))
EAT
CL-USER>

gethashで動物のいる座標に植物が生えていたら、動物に生命力を足して
そこにある植物をremhashで消すんですね。

繁殖

増殖はこういったゲームで一番おもしろい部分でありますが、
(簡略化のため)この動物は単性生殖で増えるのです。残念。
でも、増える時に遺伝子に変化が起きて突然変異するのです!

CL-USER> (defparameter *reproduction-energy* 200) ;繁殖可能な生命力の最低量
*REPRODUCTION-ENERGY*
CL-USER> (defun reproduce (animal)
           (let ((e (animal-energy animal)))
             (when (> e *reproduction-energy*) ;生命力に満ちている場合のみ繁殖できる
               (setf (animal-energy animal) (ash e -1))  ;繁殖したら生命力半減
               (let ((animal-nu (copy-structure animal))
                     (genes     (copy-list (animal-genes animal)))
                     (mutation  (random 8)))
                 (setf (nth mutation genes) (max 1 (+ (nth mutation genes) (random 3) -1))) ;ランダムな遺伝子に+1~-1。0以下にはならない。
                 (setf (animal-genes animal-nu) genes)
                 (push animal-nu *animals*)))))
REPRODUCE
CL-USER>

子供を作るのはcopy-structで構造体をコピーし、親の遺伝子をランダムに変化させたものを子の遺伝子とし
それを*animals*に追加するだけですね。なんとなくは分かりますが知らない関数が多いです。

copy-strunctは浅いコピーを行うのが注意ポイントだということです。
それは、構造体が数値やシンボルという単純なデータでなくリストや構造体、複雑なデータを持っていた場合、
それがコピー元とコピー先で共有されてしまうということだそう。値と参照のコピーの違いでしょうか。
それを避けるために、このgenesを直接弄るのではなく、copy-listで親のgenesをコピーして
それを変更したものをanimal-nuのgenesにsetfしているようですね。

シミュレーション世界の1日

CL-USER> (defun update-world ()
           (setf *animals* (remove-if (lambda (animal)                 ;生命力が0以下になった動物は
                                        (<= (animal-energy animal) 0)) ;*animals*から取り除く
                                      *animals*))
           (mapc (lambda (animal)
                   (turn animal)
                   (move animal)
                   (eat animal)
                   (reproduce animal))
                 *animals*)
           (add-plants))
UPDATE-WORLD
CL-USER>

最初に死んだ動物が取り除かれ、動物が行動し、植物が生えるのが一日の流れですね。

世界を描く

シミュレーション世界をREPLに表示するための関数を書きます。

CL-USER> (defun draw-world ()
           (loop for y
                below *height* ;高さの分だけ繰り返す
                do (progn (fresh-line)
                          (princ "|")
                          (loop for x
                               below *width* ;横幅の分だけ繰り返す
                               do (princ (cond ((some (lambda (animal)
                                                        (and (= (animal-x animal) x)
                                                             (= (animal-y animal) y)))
                                                      *animals*)
                                                #\M) ;その座標に動物がいれば'M'を表示
                                               ((gethash (cons x y) *plants*) #\*) ;植物があれば*
                                               (t #\space)))) ;何もなければスペース
                          (princ "|")))) ;一行の端は'|'
DRAW-WORLD
CL-USER>

そして最後にユーザインタフェースを作ります。

CL-USER> (defun evolution ()
           (draw-world)
           (fresh-line)
           (let ((str (read-line)))
             (cond ((equal str "quit") ()) ;quitが入力されたら終了
                   (t (let ((x (parse-integer str :junk-allowed t))) ;入力を整数に変換
                        (if x
                            (loop for i
                                 below x                ;入力された数値分
                                 do (update-world)      ;update-worldを回す
                                 if (zerop (mod i 1000)) ;1000日回すごとに
                                 do (princ #\.))         ;'.'を表示する
                            (update-world))          ;belowでは1回入力に足りないのでもう一回
                            (evolution))))))    ;再帰しインターフェースに戻る
EVOLUTION
CL-USER>

…!よしやってみます!
f:id:programcat:20181010235702p:plain
アダム(イブでもいいけど)が世界に降り立ちました。
まだ植物も生えていません。一日進めます。f:id:programcat:20181010235819p:plain
まだまだ寂しいです。1週間ほど進めてみます。
f:id:programcat:20181011000027p:plain
一気に増えました。生命力を活かしてきましたね。
それでは、参考書通り500万日進めてみます…!
...流石に時間がかかりますね…とはいえ1秒に5個弱はドットが打たれているぐらいのスピードです。
お、終わりました。
f:id:programcat:20181011000653p:plain
植物がよく生えるジャングル周辺に動物たちも集まっているようです。

最後に、この動物たちのパラメータをドバっと出して終わろうと思います。

quit
NIL
CL-USER> *animals*
(#S(ANIMAL :X 45 :Y 17 :ENERGY 125 :DIR 4 :GENES (2 4 7 107 2 3 1 1))
 #S(ANIMAL :X 51 :Y 17 :ENERGY 100 :DIR 0 :GENES (120 51 4 8 10 6 4 29))
 #S(ANIMAL :X 51 :Y 17 :ENERGY 100 :DIR 0 :GENES (120 51 4 8 10 6 4 29))
 #S(ANIMAL :X 48 :Y 15 :ENERGY 99 :DIR 0 :GENES (2 4 8 108 2 3 1 4))
 #S(ANIMAL :X 85 :Y 10 :ENERGY 100 :DIR 6 :GENES (120 52 5 7 9 6 3 28))
 #S(ANIMAL :X 53 :Y 17 :ENERGY 128 :DIR 5 :GENES (2 5 8 109 2 3 1 4))
 #S(ANIMAL :X 2 :Y 6 :ENERGY 123 :DIR 4 :GENES (121 51 9 8 4 6 5 25))
 #S(ANIMAL :X 45 :Y 20 :ENERGY 93 :DIR 3 :GENES (3 4 7 107 2 2 1 2))
 #S(ANIMAL :X 54 :Y 15 :ENERGY 118 :DIR 3 :GENES (1 4 7 109 2 4 1 3))
 #S(ANIMAL :X 45 :Y 17 :ENERGY 125 :DIR 4 :GENES (2 4 7 107 2 3 1 1))
 #S(ANIMAL :X 51 :Y 12 :ENERGY 93 :DIR 6 :GENES (2 4 7 108 2 4 1 1))
 #S(ANIMAL :X 52 :Y 17 :ENERGY 118 :DIR 5 :GENES (1 4 7 109 2 4 1 3))
 #S(ANIMAL :X 91 :Y 18 :ENERGY 159 :DIR 7 :GENES (120 49 9 8 5 6 5 24))
 #S(ANIMAL :X 57 :Y 11 :ENERGY 77 :DIR 4 :GENES (2 4 7 108 2 2 1 3))
 #S(ANIMAL :X 50 :Y 12 :ENERGY 95 :DIR 4 :GENES (2 4 5 108 2 4 1 3))
 #S(ANIMAL :X 84 :Y 21 :ENERGY 73 :DIR 2 :GENES (120 50 10 8 5 7 5 27))
 #S(ANIMAL :X 44 :Y 12 :ENERGY 79 :DIR 1 :GENES (2 4 5 108 1 3 1 2))
 #S(ANIMAL :X 27 :Y 16 :ENERGY 82 :DIR 2 :GENES (120 50 10 9 5 6 5 24))
 #S(ANIMAL :X 49 :Y 18 :ENERGY 164 :DIR 6 :GENES (2 4 8 109 2 4 1 3))
 #S(ANIMAL :X 50 :Y 18 :ENERGY 159 :DIR 0 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 23 :Y 10 :ENERGY 64 :DIR 2 :GENES (120 51 5 8 10 6 4 29))
 #S(ANIMAL :X 54 :Y 21 :ENERGY 89 :DIR 5 :GENES (121 50 9 8 5 5 5 25))
 #S(ANIMAL :X 55 :Y 21 :ENERGY 122 :DIR 0 :GENES (120 51 5 8 10 6 4 29))
 #S(ANIMAL :X 64 :Y 5 :ENERGY 141 :DIR 6 :GENES (120 50 9 8 5 7 5 26))
 #S(ANIMAL :X 26 :Y 25 :ENERGY 153 :DIR 0 :GENES (120 50 10 8 5 6 5 24))
 #S(ANIMAL :X 79 :Y 1 :ENERGY 68 :DIR 6 :GENES (120 50 9 7 8 7 4 28))
 #S(ANIMAL :X 49 :Y 15 :ENERGY 75 :DIR 2 :GENES (2 4 7 107 2 3 1 2))
 #S(ANIMAL :X 44 :Y 13 :ENERGY 58 :DIR 5 :GENES (4 3 6 109 1 2 1 2))
 #S(ANIMAL :X 52 :Y 10 :ENERGY 142 :DIR 2 :GENES (2 3 7 108 1 3 1 5))
 #S(ANIMAL :X 50 :Y 18 :ENERGY 164 :DIR 4 :GENES (2 4 8 109 2 4 1 4))
 #S(ANIMAL :X 16 :Y 12 :ENERGY 48 :DIR 3 :GENES (120 52 5 7 9 6 3 28))
 #S(ANIMAL :X 47 :Y 8 :ENERGY 70 :DIR 0 :GENES (121 50 10 8 5 5 5 24))
 #S(ANIMAL :X 54 :Y 21 :ENERGY 145 :DIR 1 :GENES (2 4 8 107 2 4 1 4))
 #S(ANIMAL :X 50 :Y 17 :ENERGY 91 :DIR 7 :GENES (1 4 7 109 2 3 1 3))
 #S(ANIMAL :X 51 :Y 10 :ENERGY 93 :DIR 3 :GENES (2 4 7 108 2 3 1 1))
 #S(ANIMAL :X 46 :Y 17 :ENERGY 145 :DIR 5 :GENES (2 4 8 108 2 4 1 4))
 #S(ANIMAL :X 95 :Y 15 :ENERGY 42 :DIR 3 :GENES (121 50 10 8 5 5 6 24))
 #S(ANIMAL :X 23 :Y 3 :ENERGY 59 :DIR 1 :GENES (120 49 9 7 8 7 4 28))
 #S(ANIMAL :X 49 :Y 18 :ENERGY 145 :DIR 3 :GENES (2 4 8 108 2 4 1 4))
 #S(ANIMAL :X 40 :Y 16 :ENERGY 113 :DIR 5 :GENES (121 50 10 7 5 5 6 25))
 #S(ANIMAL :X 50 :Y 6 :ENERGY 70 :DIR 3 :GENES (121 50 10 8 5 5 5 24))
 #S(ANIMAL :X 45 :Y 5 :ENERGY 18 :DIR 2 :GENES (121 51 9 8 6 7 5 26))
 #S(ANIMAL :X 44 :Y 14 :ENERGY 108 :DIR 6 :GENES (2 4 5 108 1 3 1 2))
 #S(ANIMAL :X 55 :Y 12 :ENERGY 100 :DIR 2 :GENES (2 4 7 108 2 1 1 3))
 #S(ANIMAL :X 96 :Y 10 :ENERGY 148 :DIR 3 :GENES (120 50 9 7 8 7 4 28))
 #S(ANIMAL :X 44 :Y 15 :ENERGY 91 :DIR 0 :GENES (2 4 7 107 2 3 1 1))
 #S(ANIMAL :X 47 :Y 19 :ENERGY 159 :DIR 1 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 99 :Y 16 :ENERGY 108 :DIR 3 :GENES (121 50 10 8 5 5 7 25))
 #S(ANIMAL :X 16 :Y 27 :ENERGY 89 :DIR 4 :GENES (120 50 10 8 5 7 5 26))
 #S(ANIMAL :X 15 :Y 5 :ENERGY 23 :DIR 2 :GENES (120 50 9 8 5 6 4 24))
 #S(ANIMAL :X 52 :Y 17 :ENERGY 114 :DIR 4 :GENES (2 3 8 113 1 4 1 2))
 #S(ANIMAL :X 47 :Y 9 :ENERGY 8 :DIR 6 :GENES (4 3 6 110 1 3 1 2))
 #S(ANIMAL :X 45 :Y 12 :ENERGY 192 :DIR 7 :GENES (4 3 6 108 1 3 1 2))
 #S(ANIMAL :X 35 :Y 23 :ENERGY 82 :DIR 4 :GENES (120 50 10 8 5 6 5 24))
 #S(ANIMAL :X 55 :Y 21 :ENERGY 5 :DIR 6 :GENES (2 4 8 108 2 3 1 2))
 #S(ANIMAL :X 46 :Y 17 :ENERGY 117 :DIR 7 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 43 :Y 10 :ENERGY 5 :DIR 4 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 2 :Y 25 :ENERGY 88 :DIR 2 :GENES (123 50 5 9 3 4 4 28))
 #S(ANIMAL :X 44 :Y 19 :ENERGY 74 :DIR 1 :GENES (4 3 6 108 1 3 1 3))
 #S(ANIMAL :X 76 :Y 0 :ENERGY 109 :DIR 4 :GENES (120 50 9 8 5 6 5 24))
 #S(ANIMAL :X 44 :Y 11 :ENERGY 142 :DIR 6 :GENES (2 3 7 108 1 2 1 5))
 #S(ANIMAL :X 14 :Y 21 :ENERGY 88 :DIR 2 :GENES (123 50 9 8 5 5 5 26))
 #S(ANIMAL :X 50 :Y 18 :ENERGY 81 :DIR 3 :GENES (2 5 7 108 1 2 2 3))
 #S(ANIMAL :X 51 :Y 15 :ENERGY 128 :DIR 0 :GENES (2 5 8 109 2 3 1 4))
 #S(ANIMAL :X 17 :Y 28 :ENERGY 122 :DIR 6 :GENES (121 50 10 7 5 5 6 24))
 #S(ANIMAL :X 93 :Y 8 :ENERGY 100 :DIR 2 :GENES (120 51 5 7 9 6 3 28))
 #S(ANIMAL :X 53 :Y 13 :ENERGY 141 :DIR 2 :GENES (3 4 5 108 2 3 1 3))
 #S(ANIMAL :X 13 :Y 18 :ENERGY 103 :DIR 6 :GENES (120 50 9 8 5 6 4 24))
 #S(ANIMAL :X 23 :Y 10 :ENERGY 72 :DIR 2 :GENES (120 50 9 7 8 8 4 28))
 #S(ANIMAL :X 57 :Y 14 :ENERGY 17 :DIR 6 :GENES (2 3 7 108 1 2 1 4))
 #S(ANIMAL :X 24 :Y 21 :ENERGY 33 :DIR 0 :GENES (121 50 10 7 5 5 6 25))
 #S(ANIMAL :X 29 :Y 17 :ENERGY 153 :DIR 1 :GENES (120 50 10 8 5 6 5 24))
 #S(ANIMAL :X 48 :Y 10 :ENERGY 159 :DIR 0 :GENES (2 4 5 108 2 3 1 2))
 #S(ANIMAL :X 48 :Y 17 :ENERGY 82 :DIR 0 :GENES (4 3 6 109 1 3 1 3))
 #S(ANIMAL :X 47 :Y 0 :ENERGY 172 :DIR 4 :GENES (121 50 10 8 5 5 6 25))
 #S(ANIMAL :X 52 :Y 15 :ENERGY 50 :DIR 0 :GENES (122 50 9 7 5 5 6 25))
 #S(ANIMAL :X 52 :Y 13 :ENERGY 123 :DIR 3 :GENES (2 4 7 108 2 2 1 2))
 #S(ANIMAL :X 2 :Y 27 :ENERGY 126 :DIR 4 :GENES (119 51 5 8 7 4 4 28))
 #S(ANIMAL :X 47 :Y 9 :ENERGY 58 :DIR 1 :GENES (4 3 6 109 1 3 1 2))
 #S(ANIMAL :X 58 :Y 27 :ENERGY 108 :DIR 4 :GENES (120 50 9 6 8 6 4 28))
 #S(ANIMAL :X 7 :Y 25 :ENERGY 125 :DIR 4 :GENES (123 50 9 8 5 5 5 26))
 #S(ANIMAL :X 37 :Y 15 :ENERGY 106 :DIR 7 :GENES (120 50 9 7 8 7 4 28))
 #S(ANIMAL :X 75 :Y 27 :ENERGY 98 :DIR 7 :GENES (121 51 9 8 5 7 5 26))
 #S(ANIMAL :X 79 :Y 13 :ENERGY 28 :DIR 7 :GENES (120 50 9 7 8 7 4 29))
 #S(ANIMAL :X 55 :Y 16 :ENERGY 5 :DIR 6 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 61 :Y 0 :ENERGY 30 :DIR 5 :GENES (120 50 9 7 5 6 5 24))
 #S(ANIMAL :X 46 :Y 20 :ENERGY 93 :DIR 4 :GENES (2 4 7 107 2 2 1 2))
 #S(ANIMAL :X 10 :Y 1 :ENERGY 73 :DIR 3 :GENES (120 50 10 8 5 7 5 27))
 #S(ANIMAL :X 46 :Y 9 :ENERGY 28 :DIR 3 :GENES (2 3 7 108 2 3 1 3))
 #S(ANIMAL :X 46 :Y 24 :ENERGY 38 :DIR 6 :GENES (2 4 7 107 2 2 1 2))
 #S(ANIMAL :X 49 :Y 14 :ENERGY 155 :DIR 5 :GENES (2 4 7 107 2 3 1 2))
 #S(ANIMAL :X 74 :Y 23 :ENERGY 16 :DIR 0 :GENES (121 51 10 8 5 5 5 24))
 #S(ANIMAL :X 65 :Y 13 :ENERGY 164 :DIR 5 :GENES (121 49 9 7 5 5 5 25))
 #S(ANIMAL :X 95 :Y 29 :ENERGY 156 :DIR 4 :GENES (119 50 9 8 5 6 5 24))
 #S(ANIMAL :X 51 :Y 18 :ENERGY 61 :DIR 3 :GENES (3 4 5 108 2 3 1 3))
 #S(ANIMAL :X 45 :Y 8 :ENERGY 4 :DIR 4 :GENES (2 3 7 108 3 3 1 4))
 #S(ANIMAL :X 58 :Y 27 :ENERGY 66 :DIR 5 :GENES (121 50 9 7 5 6 5 25))
 #S(ANIMAL :X 46 :Y 15 :ENERGY 99 :DIR 0 :GENES (2 4 8 108 2 4 1 4))
 #S(ANIMAL :X 56 :Y 19 :ENERGY 24 :DIR 3 :GENES (2 4 8 109 2 3 1 4))
 #S(ANIMAL :X 59 :Y 7 :ENERGY 8 :DIR 4 :GENES (2 3 7 108 2 2 1 4))
 #S(ANIMAL :X 50 :Y 16 :ENERGY 47 :DIR 2 :GENES (1 4 7 108 2 3 1 2))
 #S(ANIMAL :X 1 :Y 6 :ENERGY 45 :DIR 5 :GENES (120 50 9 8 5 7 5 27))
 #S(ANIMAL :X 46 :Y 12 :ENERGY 95 :DIR 2 :GENES (2 4 5 108 2 3 1 3))
 #S(ANIMAL :X 42 :Y 20 :ENERGY 144 :DIR 6 :GENES (120 51 5 8 9 6 4 29))
 #S(ANIMAL :X 48 :Y 8 :ENERGY 81 :DIR 3 :GENES (2 5 7 108 1 2 1 3))
 #S(ANIMAL :X 40 :Y 26 :ENERGY 38 :DIR 4 :GENES (121 50 10 7 5 5 5 25))
 #S(ANIMAL :X 39 :Y 16 :ENERGY 85 :DIR 7 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 69 :Y 16 :ENERGY 113 :DIR 6 :GENES (121 50 9 8 5 5 5 25))
 #S(ANIMAL :X 46 :Y 14 :ENERGY 126 :DIR 7 :GENES (3 3 6 107 1 3 1 2))
 #S(ANIMAL :X 31 :Y 8 :ENERGY 31 :DIR 3 :GENES (123 50 5 9 3 5 3 29))
 #S(ANIMAL :X 54 :Y 11 :ENERGY 32 :DIR 4 :GENES (122 50 9 7 5 5 5 25))
 #S(ANIMAL :X 46 :Y 24 :ENERGY 43 :DIR 3 :GENES (2 4 6 108 2 3 1 3))
 #S(ANIMAL :X 45 :Y 8 :ENERGY 61 :DIR 3 :GENES (2 3 7 108 2 3 1 4))
 #S(ANIMAL :X 13 :Y 29 :ENERGY 123 :DIR 5 :GENES (121 50 9 8 4 6 5 25))
 #S(ANIMAL :X 25 :Y 17 :ENERGY 102 :DIR 5 :GENES (124 49 3 9 2 4 4 29))
 #S(ANIMAL :X 41 :Y 18 :ENERGY 108 :DIR 5 :GENES (121 50 10 8 5 5 6 25))
 #S(ANIMAL :X 30 :Y 19 :ENERGY 90 :DIR 0 :GENES (119 50 9 7 8 6 4 28))
 #S(ANIMAL :X 50 :Y 17 :ENERGY 76 :DIR 1 :GENES (2 2 7 108 2 3 2 4))
 #S(ANIMAL :X 30 :Y 18 :ENERGY 76 :DIR 0 :GENES (120 50 9 8 5 6 5 24))
 #S(ANIMAL :X 78 :Y 25 :ENERGY 86 :DIR 7 :GENES (123 50 9 9 5 5 5 26))
 #S(ANIMAL :X 56 :Y 22 :ENERGY 85 :DIR 7 :GENES (2 4 7 108 2 3 1 2))
 #S(ANIMAL :X 64 :Y 21 :ENERGY 111 :DIR 0 :GENES (121 50 8 9 5 4 5 25))
 #S(ANIMAL :X 58 :Y 10 :ENERGY 29 :DIR 0 :GENES (2 2 7 108 2 3 1 4))
 #S(ANIMAL :X 55 :Y 10 :ENERGY 77 :DIR 0 :GENES (2 4 7 108 2 2 1 3))
 #S(ANIMAL :X 47 :Y 15 :ENERGY 114 :DIR 5 :GENES (2 3 8 113 1 4 1 2))
 #S(ANIMAL :X 54 :Y 4 :ENERGY 16 :DIR 1 :GENES (3 3 6 108 1 3 1 2))
 #S(ANIMAL :X 53 :Y 13 :ENERGY 26 :DIR 5 :GENES (121 50 10 8 5 5 6 25))
 #S(ANIMAL :X 52 :Y 13 :ENERGY 115 :DIR 6 :GENES (2 4 7 108 2 4 1 3))
 #S(ANIMAL :X 38 :Y 8 :ENERGY 102 :DIR 0 :GENES (121 50 9 8 5 5 4 25))
 #S(ANIMAL :X 46 :Y 7 :ENERGY 70 :DIR 6 :GENES (2 4 7 107 2 3 1 3))
 #S(ANIMAL :X 63 :Y 25 :ENERGY 109 :DIR 7 :GENES (120 50 9 8 5 6 5 24))
 #S(ANIMAL :X 47 :Y 16 :ENERGY 38 :DIR 7 :GENES (2 4 7 108 2 3 1 3))
 #S(ANIMAL :X 55 :Y 12 :ENERGY 89 :DIR 0 :GENES (2 4 7 108 2 3 2 2))
 #S(ANIMAL :X 70 :Y 23 :ENERGY 89 :DIR 4 :GENES (121 50 9 8 5 5 4 25))
 #S(ANIMAL :X 4 :Y 5 :ENERGY 190 :DIR 1 :GENES (121 50 9 8 5 7 5 26))
 #S(ANIMAL :X 49 :Y 10 :ENERGY 56 :DIR 7 :GENES (2 4 7 108 2 4 1 3))
 #S(ANIMAL :X 73 :Y 19 :ENERGY 39 :DIR 0 :GENES (121 50 10 8 5 5 5 24))
 #S(ANIMAL :X 94 :Y 20 :ENERGY 40 :DIR 6 :GENES (123 51 5 7 7 6 3 27))
 #S(ANIMAL :X 50 :Y 14 :ENERGY 156 :DIR 2 :GENES (2 2 7 108 2 3 1 4))
 #S(ANIMAL :X 92 :Y 27 :ENERGY 59 :DIR 0 :GENES (120 50 9 7 8 7 4 28))
 #S(ANIMAL :X 7 :Y 22 :ENERGY 79 :DIR 5 :GENES (120 50 9 8 5 6 5 24))
 #S(ANIMAL :X 54 :Y 11 :ENERGY 4 :DIR 6 :GENES (2 3 7 108 3 3 1 4))
 #S(ANIMAL :X 95 :Y 17 :ENERGY 88 :DIR 6 :GENES (123 50 9 8 5 5 5 26))
 #S(ANIMAL :X 48 :Y 9 :ENERGY 49 :DIR 7 :GENES (2 4 7 108 2 3 1 2))
 #S(ANIMAL :X 49 :Y 11 :ENERGY 70 :DIR 3 :GENES (2 4 7 108 2 4 1 3))
 #S(ANIMAL :X 0 :Y 14 :ENERGY 57 :DIR 5 :GENES (120 50 9 7 5 6 5 24))
 #S(ANIMAL :X 35 :Y 17 :ENERGY 4 :DIR 7 :GENES (121 50 9 7 5 5 5 25))
 #S(ANIMAL :X 48 :Y 19 :ENERGY 46 :DIR 5 :GENES (120 51 5 8 7 4 4 28))
 #S(ANIMAL :X 51 :Y 18 :ENERGY 46 :DIR 7 :GENES (2 4 7 109 2 3 1 3))
 #S(ANIMAL :X 63 :Y 19 :ENERGY 61 :DIR 7 :GENES (120 50 9 8 5 7 5 26))
 #S(ANIMAL :X 56 :Y 12 :ENERGY 46 :DIR 2 :GENES (3 3 6 108 1 3 1 2))
 #S(ANIMAL :X 28 :Y 19 :ENERGY 23 :DIR 0 :GENES (121 50 10 8 5 5 5 25))
 #S(ANIMAL :X 52 :Y 12 :ENERGY 117 :DIR 5 :GENES (3 3 6 108 1 3 1 3))
 #S(ANIMAL :X 14 :Y 17 :ENERGY 8 :DIR 5 :GENES (123 50 5 9 3 4 4 29)))
CL-USER> 

見事に2種類の動物に変化しました。
ちなみに最初の動物のパラメータはこんな感じでした。

CL-USER> *animals*
(#S(ANIMAL :X 50 :Y 15 :ENERGY 1000 :DIR 2 :GENES (8 6 6 3 10 3 1 9)))
CL-USER>

ここに肉食動物を解き放ったり、性別の概念を足したり、色々改造してみたい夢が広がりますね!
しかし、今日はもう寝なくては…おやすみなさい。