CIS 554 Clojure 3: Sharks
Fall 2016, David Matuszek

Purposes of this assignment

The "rules"

This assignment is based loosely (very loosely) on a well-known simulation program, "Wator," in which sharks and smaller fish interact in a two-dimensional world. To avoid unnecessary complications so that you can concentrate on the main Clojure points, I have greatly simplified the simulation.


Use an agent, whose value is a struct, to represent a shark. The struct can contain whatever information is needed. My starter code (below) probably contains enough information.

Use a sequence of some sort (a vector is probably best) to represent the one-dimensional world. For each location in the sequence, either generate a random shark (probability = 0.5) or leave it "empty" (probability = 0.5). It might be helpful to put some kind of explicit "empty object" in the locations that don't contain sharks.

Sharks interact with other sharks (by eating them), so you will have to coordinate updates to sharks; use a transaction for this. Note that transactions may fail--shark A may be eating shark B, which is eating shark C, while shark D (which is coming from the other direction) is eating shark C.

Once a second, print out what is happening, so we can see what the program is doing. The print-sharks method provided is designed for up to 26 sharks (\a to \z), and shows the direction in which the shark is trying to move. However, since the sharks are moving, printing should be the task of an agent.

Since the only food source for a shark is another shark, eventually all the sharks will die. When this happens, the simulation should quit.

The built-in function (rand) returns a random floating-point number between 0 and 1. The Java method (. Thread (sleep ms)) pauses for ms milliseconds.

Starter code

Here is some Clojure code that I have written that you can use as a starting point. Or, you can ignore it if you wish. It is intended to be a help, not an extra set of requirements.

(defstruct shark :id :direction :weight :hunger :status)

(defn make-shark
  "Defines a shark with :id = n and various attributes."
  (struct-map shark
    :id (char (+ (int \a) (dec n)))
    :direction (if (< (rand) 0.5)
      :right )
    :weight  (+ 90 (* 20 (rand)))
    :hunger 0
    :status :alive) )

(defn str-shark
  "Returns a string representation of a shark (or () for an empty list)."
    (= shark ()) ".."
    (= (shark :direction) :left) (str "<" (str (shark :id)))
    :else  (str (str (shark :id)) ">") ) )

(defn print-sharks
  "Prints out a list containing sharks and empty locations."
  (println (map str-shark lst)) )

(defn make-shark-list
  "Makes a list of how-many actual sharks, and some number of empty locations."
  ([how-many] (make-shark-list [] 1 how-many))
  ([lst n how-many]
      (> n how-many) lst
      (> (rand) 0.50) (cons '() (make-shark-list lst n how-many))
      :else (cons (make-shark n) (make-shark-list lst (inc n) how-many)) ) ) )

(defn run-me []
    (print-sharks (make-shark-list 3))
    (print-sharks (make-shark-list 3))
    (print-sharks (make-shark-list 3))
    (print-sharks (make-shark-list 26)) ) )

(defn run-me-too []
    (let [
      ; create a shark called Sherman (as in
      sherman (make-shark 1)
      ; create a reference to Sherman
      look!-a-shark! (ref sherman)]
      ; print Sherman in two different ways
      (println sherman)
      (println (str-shark sherman))
      ; put Sherman on a diet
        (ref-set look!-a-shark! (assoc @look!-a-shark! :hunger 4))
        (ref-set look!-a-shark!
          (assoc  @look!-a-shark! :weight (* 0.75 (@look!-a-shark! :weight))) )
        (ref-set look!-a-shark! (assoc @look!-a-shark! :status :ill-tempered))
      ; show off Sherman's hoped-for look
      (println @look!-a-shark!)
      (println (str-shark @look!-a-shark!))
      ; sorry, Sherman, you're immutable
      (println sherman)
      (println (str-shark sherman)) ) ) ) )

Due date:

Turn in your shark.clj file to Canvas by 11:59pm Sunday, Oct. 16. As only one file is required, there is no need to zip it.Another assignment will be given out on Wednesday before this one is due.