CIS 554 Clojure 3: Sharks
Fall 2016, David Matuszek

# Purposes of this assignment

• To teach agents and Software Transactional Memory

# 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.

• The only kind of fish is the shark.
• Sharks live in a one-dimensional world (a sequence).
• Sharks differ in weight, and start out weighing 100 pounds, plus or minus a certain random amount.
• Use real numbers, and don't worry about two sharks weighing exactly the same.
• Sharks start swimming in a randomly chosen direction, and must continue swimming if possible.
• Sharks move independently (they are separate processes). To move:
• A shark must wait a random amount of time, between 1/2 second and 1 second, before each move.
• When a shark moves, it moves to the next (or previous) location in the sequence, end-around (so it is actually swimming in a circle).
• If a shark is about to swim into a space containing a larger, living shark, it turns and swims in the opposite direction.
• If a shark has larger sharks on both sides of it, this is the one case where it does not move.
• A shark can and will move into a space containing a smaller shark, or a dead shark--it "eats" the other shark, and gains 25% of the eaten shark's weight.
• A shark starves to death (and stops moving) if it moves ten times without finding something to eat.

# Details

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."
[n]
(struct-map shark
:id (char (+ (int \a) (dec n)))
:direction (if (< (rand) 0.5)
:left
: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]
(cond
(= 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."
[lst]
(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]
(cond
(> 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 []
(do
(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 []
(do
(let [
; create a shark called Sherman (as in http://shermanslagoon.com/)
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
(dosync
(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.