examples/life.lisp @ 613775e34d23

Add game of life example
author Steve Losh <steve@stevelosh.com>
date Tue, 14 Mar 2017 13:34:16 +0000
parents (none)
children e95ecd878abd
(ql:quickload '(:cl-blt :losh :iterate))

(defpackage :cl-blt.examples.life
  (:use :cl :losh :iterate))

(in-package :cl-blt.examples.life)

(defconstant +world-size+ 128)

(deftype world-index ()
  `(integer 0 (,+world-size+)))

(deftype world ()
  `(simple-array bit (,+world-size+ ,+world-size+)))

(defun make-world ()
  (make-array (list +world-size+ +world-size+)
              :element-type 'bit
              :initial-element 0))

(defun randomize-world (world)
  (iterate (for (cell row col) :in-array world)
           (setf (aref world row col)
                 (if (randomp) 1 0))))

(defun clear-world (world)
  (iterate (for (cell row col) :in-array world)
           (setf (aref world row col) 0)))

(defun-inline wref (world row col)
  (aref world
        (mod row +world-size+)
        (mod col +world-size+)))

(defun count-neighbors (world row col)
  (declare (optimize speed)
           (type world world)
           (type world-index row col))
  (flet ((cell (dr dc)
           (wref world (+ row dr) (+ col dc))))
    (+ (cell -1 -1)
       (cell -1  0)
       (cell -1  1)
       (cell  0 -1)
       (cell  0  1)
       (cell  1 -1)
       (cell  1  0)
       (cell  1  1))))

(defun tick-cell (cell neighbors)
  (if (= cell 1)
    (cond
      ((<= neighbors 1) 0)
      ((<= neighbors 3) 1)
      ((>= neighbors 4) 0))
    (if (= neighbors 3)
      1
      0)))

(defun tick-world (world next)
  (iterate
    (for (cell row col) :in-array world)
    (setf (aref next row col)
          (tick-cell cell (count-neighbors world row col)))))

(defun draw (world)
  (iterate
    (with srows = (blt:height))
    (with scols = (blt:width))
    (for (cell row col) :in-array world)
    (when (and (< row srows)
               (< col scols))
      (setf (blt:cell-char col row)
            (if (zerop cell) #\Space #\*))))
  (blt:refresh))

(defun config ()
  (blt:set "window.resizeable = true")
  (blt:set "window.cellsize = 6x6")
  (blt:set "window.title = LIFE"))

(defun input ()
  (blt:key-case (blt:read)
    (:r :randomize)
    (:c :clear)
    (:space :tick)
    (:escape :quit)
    (:close :quit)))

(defun main ()
  (blt:with-terminal
    (config)
    (iterate
      (with world = (make-world))
      (with next = (make-world))
      (initially (setf
                   (aref world 3 4) 1
                   (aref world 4 5) 1
                   (aref world 5 3) 1
                   (aref world 5 4) 1
                   (aref world 5 5) 1))
      (draw world)
      (case (input)
        (:randomize (randomize-world world))
        (:clear (clear-world world))
        (:tick (tick-world world next) (rotatef world next))
        (:quit (return))))))