Skip to content

Instantly share code, notes, and snippets.

@mikebridge
Created April 3, 2012 01:17
Show Gist options
  • Save mikebridge/2288537 to your computer and use it in GitHub Desktop.
Save mikebridge/2288537 to your computer and use it in GitHub Desktop.
Codelesson Clojure Assignment 5
(ns codelesson.assignment-5
(:require [clojure.string :as str]))
;; util
(defn log [s]
(println s))
;; delay a function for a random number of milliseconds
(defn wait-and-call [f]
{:pre [(fn? f)]}
(fn [& args]
{:pre [(fn? f)]}
(let [interval (rand-int 2000)]
(Thread/sleep (rand-int interval))
(apply f args))))
(def uniq-id (ref 1))
(defn new-uniq-id []
(dosync (alter uniq-id inc)))
;; plateau
;; the plateau is a map from a position [x y] to a rover id
(def plateau-map (ref {}))
(defn plateau-clear [plateau-ref]
(dosync (ref-set plateau-ref {})))
;; return nil if can't move to new position
(defn plateau-move [plateau-ref id old-coords new-coords]
{:pre [(vector? old-coords) (vector? new-coords)]}
(dosync
(if (and (plateau-ref new-coords)
(not= id (plateau-ref new-coords)))
(do
(log (str "** COLLISION at " new-coords
" between #" id " and #" (plateau-ref new-coords)))
old-coords ;; collision: need to signal it somehow
)
(do
(alter plateau-ref dissoc old-coords)
(alter plateau-ref assoc new-coords id)
new-coords))))
;; move
(defn rotate-left [current-dir]
{:pre [(char? current-dir)]}
((apply hash-map (seq "NWWSSEEN")) current-dir))
(defn rotate-right [current-dir]
{:pre [(char? current-dir)]}
((apply hash-map (seq "NEESSWWN")) current-dir))
(defn rotate [rover f]
(assoc rover :orientation (f (:orientation rover))))
(defn delta-xy [direction]
{:pre [(char? direction)]}
({\N [0 1] \E [1 0] \S [0 -1] \W [-1 0]} direction))
(defn new-point [& v]
(vec (apply map + v)))
(defn move [rover]
{:pre [(map? rover) (char? (:orientation rover))]}
(assoc rover :position
(new-point (:position rover) (delta-xy (:orientation rover)))))
(defn command [cmd]
{:pre [(char? cmd)]}
(cond
(= cmd \M) (fn [rover] (move rover))
(= cmd \R) (fn [rover] (rotate rover rotate-right))
(= cmd \L) (fn [rover] (rotate rover rotate-left))))
(defn movement-functions [movement-commands]
(map #(command %1) movement-commands))
;; collisions return a rover in an identical position
(defn apply-movement [rover plateau-map plateau-size move-fn]
{:pre [(map? rover) (vector? plateau-size) (fn? move-fn)]
:post [(map? %)]}
(let [requested-new-rover (move-fn rover)
maxx (first plateau-size)
maxy (second plateau-size)
x (first (:position requested-new-rover))
y (second (:position requested-new-rover))]
(log (str "#" (:id rover)
" F:" (:position rover)
"/" (:orientation rover)
" T:" (:position requested-new-rover)
"/" (:orientation requested-new-rover)))
(if (or (< x 0)
(> x maxx)
(< y 0)
(> y maxy))
;; off plateau
(do
(log (str "#" (:id rover) " hit the edge of the plateau"))
rover
)
;; try to move to new position
(assoc rover :position
(plateau-move plateau-map
(:id rover)
(:position rover)
(:position requested-new-rover))))))
(defn rover-hash [position-coords orientation]
{:pre [(char? orientation)]}
{:id (new-uniq-id) :position (vec position-coords) :orientation orientation})
;; return future reference which is executing a sequence of
;; commands for this rover.
(defn spawn-rover
[plateau-size plateau-map start-pos commands]
{:pre [(vector? plateau-size) (vector? start-pos) (string? commands)]
:post [(future? %)]}
(let [move-fns (movement-functions commands)
rover (rover-hash (take 2 start-pos) (start-pos 2))
fns-with-delay (map wait-and-call move-fns)]
;;(spawn-movements plateau-size rover fns-with-delay)))
(log (str "spawning #" (:id rover) " at " (:position rover)))
(future (reduce
#(apply-movement %1 plateau-map plateau-size %2)
(cons rover fns-with-delay)))))
;; read data
(defn str-to-vec [s]
(read-string (str "[" s "]")))
(defn char-if-sym [s]
(if (symbol? s)
(first (str s))
s))
(defn convert-position [pos]
{:pre [(string? pos)]}
(vec (map char-if-sym (str-to-vec pos))))
(defn rover-to-string [rover]
(str (first (:position rover)) " " (second (:position rover)) " " (:orientation rover)))
(defn positions-to-string [position-list]
(clojure.string/join "\n" (map rover-to-string position-list)))
(defn rove [in]
{:pre [(string? in)]}
(positions-to-string
(let [lines (clojure.string/split-lines in)
plateau-size (str-to-vec (first lines))
pos-and-command-pairs (partition 2 (rest lines))
futures (map #(spawn-rover plateau-size
plateau-map
(convert-position (first %))
(first (rest %))) pos-and-command-pairs)
]
;; not sure why I have to sort these backwards to launch all threads?
(map deref (reverse futures)))))
@mikebridge
Copy link
Author

(def testdata "5 5\n1 2 N\nLMLMLMLMM\n3 3 E\nMMRMMRMRRM\n 0 0 E\nMMMMM\n5 0 W\nMMMMM")
(rove testdata)
spawning #1 at [1 2]
spawning #2 at [3 3]
spawning #3 at [0 0]
spawning #4 at [5 0]
#1 F:[1 2]/N T:[1 2]/W
#2 F:[3 3]/E T:[4 3]/E
#2 F:[4 3]/E T:[5 3]/E
#3 F:[0 0]/E T:[1 0]/E
#3 F:[1 0]/E T:[2 0]/E
#4 F:[5 0]/W T:[4 0]/W
#2 F:[5 3]/E T:[5 3]/S
#3 F:[2 0]/E T:[3 0]/E
#2 F:[5 3]/E T:[6 3]/E
#2 hit the edge of the plateau
#4 F:[4 0]/W T:[3 0]/W
** COLLISION at [3 0] between #4 and #3
#4 F:[4 0]/W T:[3 0]/W
** COLLISION at [3 0] between #4 and #3
#1 F:[1 2]/N T:[1 3]/N
#4 F:[4 0]/W T:[3 0]/W
** COLLISION at [3 0] between #4 and #3
#4 F:[4 0]/W T:[3 0]/W
** COLLISION at [3 0] between #4 and #3
#2 F:[5 3]/E T:[6 3]/E
#2 hit the edge of the plateau
#2 F:[5 3]/E T:[5 3]/S
#2 F:[5 3]/E T:[6 3]/E
#2 hit the edge of the plateau
#1 F:[1 3]/N T:[1 3]/W
#1 F:[1 3]/N T:[1 4]/N
#2 F:[5 3]/E T:[5 3]/S
#3 F:[3 0]/E T:[4 0]/E
** COLLISION at [4 0] between #3 and #4
#3 F:[3 0]/E T:[4 0]/E
** COLLISION at [4 0] between #3 and #4
#2 F:[5 3]/E T:[5 3]/S
#1 F:[1 4]/N T:[1 4]/W
#1 F:[1 4]/N T:[1 5]/N
#2 F:[5 3]/E T:[6 3]/E
#2 hit the edge of the plateau
#1 F:[1 5]/N T:[1 5]/W
#1 F:[1 5]/N T:[1 6]/N
#1 hit the edge of the plateau
#1 F:[1 5]/N T:[1 6]/N
#1 hit the edge of the plateau

"4 0 W\n3 0 E\n5 3 E\n1 5 N"

@mikebridge
Copy link
Author

Note: the "T:" represents a requested move, not an actual move.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment