;;; ;;; Schlieffen Plan Rail Logistics Model ;;; ;;; Spring 2013 ;;; ;;; Robert Allen - averyallen71@gmail.com ;;; Brian Coffey - btpcoffey@gmail.com ;;; Dante Montgomery - dante.e.montgomery@gmail.com ;;; Ashley Rawson - ashleyrawson@gmail.com ;;; Brian Stebar - stebar2@gmail.combreed [trains train] trains-own[ calling-soldier home-node destination location path previous-path speed cargo active? in-queue? ] ;; ;; cause-train-breakdowns - global method ;; ;; Kills off a number of trains based on the train-attrition-rate ;; to cause-train-breakdowns ;; If the attrition rate is zero, we have nothing to do if (train-attrition-rate > 0) [ ;; Convert attrition rate from breakdown rate per month to breakdowns per tick let breakdown-rate-per-minute (train-attrition-rate / 100 / (24 * 60 * 30)) let breakdown-rate-per-tick (breakdown-rate-per-minute * minutes-per-tick) let breakdowns-per-tick (breakdown-rate-per-tick * (count trains with [active?])) let breakdowns-this-tick (round ((ticks + 1) * breakdowns-per-tick)) - (round (ticks * breakdowns-per-tick)) ;; Check if we need to cause some breakdowns if (breakdowns-this-tick > 0) [ ;; Let's kill some trains! ;; NOTE: We're only going to kill active trains since we're assuming that trains won't break if they're parked. ;; Therefore, make sure we only take active trains (even if we have to kill them all) let trains-to-die [] ifelse ((count trains with [active?]) >= breakdowns-this-tick) [ set trains-to-die n-of breakdowns-this-tick (trains with [active?]) ] [ set trains-to-die (trains with [active?]) ] ask trains-to-die [ ;; Before we kill each train, make sure we put the supplies they were hauling back in the supply-request-queue let supply-request (list calling-soldier cargo) set supply-request-queue fput supply-request supply-request-queue die ] ] ] end ;; ;; check-train-paths - global method ;; ;; checks the paths of the trains to see if any of the nodes in its route are not owned by the germans ;; to check-train-paths ask trains with [active? = true and in-queue? = false][ if(not empty? path)[ let has-contested-station? false foreach path[ ask ?[ if(owner != "German") [ set has-contested-station? true ] ] ] ;;if the train's route uses a path that is not owned by the germans then reset the train if(has-contested-station?)[ let supply-request (list calling-soldier cargo) set supply-request-queue fput supply-request supply-request-queue set cargo 0 set active? false set in-queue? false set color grey set location home-node set xcor [xcor] of location set ycor [ycor] of location set path [] ask my-links [ die ] ] ] ] end ;; ;; increment-path ;; ;; increments the number of trains using each of the nodes on the path. Train-using is used to determine path weight. ;; to increment-path [this-path] (foreach (but-last this-path) (but-first this-path)[ if ? != last path [ ask railway [who] of ? [who] of ?2[ set trains-using trains-using + 1 ] ] ]) end to decrement-path [this-path] (foreach (but-last this-path) (but-first this-path)[ if ? != last path [ ask link [who] of ? [who] of ?2[ set trains-using trains-using - 1 ] ] ]) end ;; ;; min-dist-node ;; ;; helper function for determining the node with the minimum distance. Takes in the unvisited set of german stations ;; and returns the node with the minimum distance ;; to-report min-dist-node [ unvisited-german-stations ] if empty? unvisited-german-stations[ report nobody ] let result first unvisited-german-stations let m [dist] of result foreach unvisited-german-stations[ if [ dist ] of ? < m[ set result ? set m [dist] of ? ] ] report result end ;; ;; move-trains - global method ;; ;; Ask trains that are active an not in a queue to move towards the next station in their path ;; to move-trains ask trains with [active? = true and in-queue? = false] [ set heading towards last path let this-train self ;; Move ahead at full speed unless we're close to the next node if-else ((distance last path) > train-speed-in-patches) [ forward train-speed-in-patches ] [ forward (distance last path) ] let n nodes-here ;;if you have arrived at the next node in the path if(one-of n = last path) [ set location last path ;;if your current location is your destination if-else(length path = 1) [ if-else location = home-node [ ;; We're back at the supply depot set active? false set location home-node ask my-links [ die ] ] [ ;; We've just reached the troops and need to unload, then head back to the depot let new-supply-level (cargo + [current-supplies] of calling-soldier) let new-supplies-requested ([supplies-requested] of calling-soldier - cargo) set color grey ask calling-soldier [ set current-supplies new-supply-level set supplies-requested new-supplies-requested ] set cargo 0 set previous-path path set path shortest-path location home-node increment-path path place-in-queue self ;; Show that this train is heading back to its depot if show-train-targets [ ask my-links [ die ] create-train-target-to home-node ask my-links [ set color gray ] ] ] ] [ ;;if train isn't at the end of its path, place it in the rail line's queue place-in-queue self ] ;;catches when node ownership changes while a train is already heading towards a destination if-else(empty? path) [ reset-train self ] [ set path remove last path path ] ] ] end ;; ;; reset-train - train method ;; ;; removes train from the track, and places a request on the queue for the cargo it was holding ;; to reset-train [train] let supply-request (list calling-soldier cargo) set supply-request-queue fput supply-request supply-request-queue set cargo 0 set active? false set in-queue? false set color grey set location home-node set xcor [xcor] of location set ycor [ycor] of location set path [] ask my-links [ die ] end ;; ;; place-in-queue ;; ;; places train in one of the two queues each link has ;; to place-in-queue[train] if-else(empty? but-last path) [ ;;catches when node ownership changes while a train is already heading towards a destination reset-train self ] [ let next-node last but-last path let this-railway railway [who] of location [who] of next-node let trains-location location let this-train self ask this-railway[ let ends both-ends let this-end nobody let opposite-end nobody ask ends[ if-else (self = trains-location)[ set this-end self ] [ set opposite-end self ] ] if-else(this-end < opposite-end)[ set lower-queue lput this-train lower-queue ] [ set higher-queue lput this-train higher-queue ] ask this-train[ set in-queue? true ] ] ] end ;; ;; set-path ;; ;; sets the route of the train passed in ;; to set-path [train] ask train[ set path shortest-path location destination increment-path path if(length path > 1)[ set path remove last path path ] ] end ;; ;;shortest-path ;; ;;Uses dijkstra's algorthim to calculate the shortest path to the goal node. ;;This implementation of dijkstra's algorthim is a modified version of the version found in ;;the following project: https://code.google.com/p/forest-wildfire-simulation/ and is used under the ;;GNU Lesser General Public License ;; to-report shortest-path [source goal] ;;set distance of all nodes to infinity and set path connections to null ask nodes[ set dist 99999999 set previous nobody ] ;;set source node's distance to zero. ask source[ set dist 0 ] ;; put all nodes into Q, the unvisited list let unvisited-german-stations [] ask nodes with [owner = "German"][ set unvisited-german-stations lput self unvisited-german-stations ] ;;while there is still an unvisited node while [ not empty? unvisited-german-stations ] [ ;;call helper function that finds node with smallest distance that has not been visited let min-node min-dist-node unvisited-german-stations ;;if distance is not equal to infinity if [dist] of min-node != 999999999 [ set unvisited-german-stations remove min-node unvisited-german-stations ;if alternative route to node is shorter, update distance and change link to previous node ask [link-neighbors] of min-node [ let uv-distance link-distance min-node self let alt ([ dist] of min-node) + uv-distance if alt < dist [ set dist alt let thisV self ask thisV [ set previous min-node ] ] ] ] ] let result [] let thisNode goal ;;paths are linked together by their previous value. Start with goal, then loop until previous = nobody while [ thisNode != nobody ] [ set result lput thisNode result set thisNode [previous] of thisNode ] report result end