001  (ns ^{:doc "Functions to create and to print two dimensional cellular automata.
002              
003              Nothing in this namespace should determine what states are possible within
004              the automaton, except for the initial state, :new.
005  
006              A cell is a map containing at least values for the keys `:x`, `:y`, and `:state`.
007  
008              A world is a two dimensional matrix (sequence of sequences) of cells, such
009              that every cell's `:x` and `:y` properties reflect its place in the matrix."
010        :author "Simon Brooke"}
011   mw-engine.world
012    (:require [clojure.string :as string]
013              [mw-engine.utils :refer [population]]))
014  
015  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
016  ;;;;
017  ;;;; mw-engine: the state/transition engine of MicroWorld.
018  ;;;;
019  ;;;; This program is free software; you can redistribute it and/or
020  ;;;; modify it under the terms of the GNU General Public License
021  ;;;; as published by the Free Software Foundation; either version 2
022  ;;;; of the License, or (at your option) any later version.
023  ;;;;
024  ;;;; This program is distributed in the hope that it will be useful,
025  ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
026  ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
027  ;;;; GNU General Public License for more details.
028  ;;;;
029  ;;;; You should have received a copy of the GNU General Public License
030  ;;;; along with this program; if not, write to the Free Software
031  ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
032  ;;;; USA.
033  ;;;;
034  ;;;; Copyright (C) 2014 Simon Brooke
035  ;;;;
036  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
037  
038  (defmacro make-cell
039    "Create a minimal default cell at x, y
040  
041    * `x` the x coordinate at which this cell is created;
042    * `y` the y coordinate at which this cell is created."
043    [x y]
044    `{:x ~x :y ~y :state :new})
045  
046  (defn make-world
047    "Make a world width cells from east to west, and height cells from north to
048     south.
049  
050    * `width` a natural number representing the width of the matrix to be created;
051    * `height` a natural number representing the height of the matrix to be created."
052    [width height]
053    (apply vector
054           (map (fn [h]
055                  (apply vector (map #(make-cell % h) (range width))))
056                (range height))))
057  
058  (defn truncate-state
059    "Truncate the print name of the state of this cell to at most limit characters."
060    [cell limit]
061    (let [s (:state cell)]
062      (cond (> (count (str s)) limit) (subs s 0 limit)
063            :else s)))
064  
065  (defn format-cell
066    "Return a formatted string summarising the current state of this cell."
067    [cell]
068    (format "%10s(%2d/%2d)"
069            (truncate-state cell 10)
070            (population cell :deer)
071            (population cell :wolves)))
072  
073  (defn- format-world-row
074    "Format one row in the state of a world for printing."
075    [row]
076    (string/join (map format-cell row)))
077  
078  (defn print-world
079    "Print the current state of this world, and return nil.
080  
081    * `world` a world as defined above."
082    [world]
083    (println)
084    (dorun
085     (map
086      #(println
087        (format-world-row %))
088      world))
089    nil)