<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" href="../coverage.css"/> <title> mw_engine/flow.clj </title> </head> <body> <span class="covered" title="1 out of 1 forms covered"> 001 (ns mw-engine.flow </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 002 "Allow flows of values between cells in the world. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 003 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 004 The design here is: a flow object is a map with the following properties: </span><br/> <span class="blank" title="0 out of 0 forms covered"> 005 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 006 1. `:source`, whose value is a location; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 007 2. `:destination`, whose value is a location; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 008 3. `:property`, whose value is a keyword; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 009 4. `:quantity`, whose value is a positive real number. </span><br/> <span class="blank" title="0 out of 0 forms covered"> 010 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 011 A location object is a map with the following properties: </span><br/> <span class="blank" title="0 out of 0 forms covered"> 012 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 013 1. `:x`, whose value is a natural number not greater than the extent of the world; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 014 2. `:y`, whose value is a natural number not greater than the extent of the world. </span><br/> <span class="blank" title="0 out of 0 forms covered"> 015 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 016 To execute a flow is transfer the quantity specified of the property specified </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 017 from the cell at the source specified to the cell at the destination specified; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 018 if the source doesn't have sufficient of the property, then all it has should </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 019 be transferred, but no more: properties to be flowed cannot be pulled negative. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 020 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 021 Flowing values through the world is consequently a two stage process: firstly </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 022 there's a planning stage, in which all the flows to be executed are computed </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 023 without changing the world, and then an execution stage, where they're all </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 024 executed. This namespace deals with mainly with execution." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 025 (:require [mw-engine.utils :refer [add-history-event get-cell get-num </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 026 in-bounds? map-world merge-cell rule-type]] </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 027 [taoensso.timbre :refer [info warn]])) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 028 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 029 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 030 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 031 ;;;; mw-engine: the state/transition engine of MicroWorld. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 032 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 033 ;;;; This program is free software; you can redistribute it and/or </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 034 ;;;; modify it under the terms of the GNU General Public License </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 035 ;;;; as published by the Free Software Foundation; either version 2 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 036 ;;;; of the License, or (at your option) any later version. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 037 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 038 ;;;; This program is distributed in the hope that it will be useful, </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 039 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 040 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 041 ;;;; GNU General Public License for more details. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 042 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 043 ;;;; You should have received a copy of the GNU General Public License </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 044 ;;;; along with this program; if not, write to the Free Software </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 045 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 046 ;;;; USA. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 047 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 048 ;;;; Copyright (C) 2014 Simon Brooke </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 049 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 050 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; </span><br/> <span class="blank" title="0 out of 0 forms covered"> 051 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 052 (defn coordinate? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 053 "Return `true` if this object `o` is a valid coordinate with respect to </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 054 this `world`, else `false`. Assumes square worlds." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 055 [o world] </span><br/> <span class="covered" title="2 out of 2 forms covered"> 056 (try </span><br/> <span class="covered" title="14 out of 14 forms covered"> 057 (and (or (zero? o) (pos-int? o)) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 058 (< o (count world))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 059 (catch Exception e </span><br/> <span class="covered" title="20 out of 20 forms covered"> 060 (warn (format "Not a valid coordinate: %s; %s" o (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 061 false))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 062 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 063 (defn location? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 064 "Return `true` if this object `o` is a location as defined above with respect to </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 065 this `world`, else `false`." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 066 [o world] </span><br/> <span class="partial" title="1 out of 2 forms covered"> 067 (try </span><br/> <span class="partial" title="14 out of 16 forms covered"> 068 (and (map? o) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 069 (integer? (:x o)) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 070 (integer? (:y o)) </span><br/> <span class="covered" title="8 out of 8 forms covered"> 071 (in-bounds? world (:x o) (:y o))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 072 (catch Exception e </span><br/> <span class="not-covered" title="0 out of 20 forms covered"> 073 (warn (format "Not a valid location: %s; %s" o (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 074 false))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 075 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 076 (defn flow? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 077 "Return `true` if this object `o` is a flow as defined above with respect to </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 078 this `world`, else `false`. Assumes square worlds." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 079 [o world] </span><br/> <span class="partial" title="1 out of 2 forms covered"> 080 (try </span><br/> <span class="partial" title="16 out of 20 forms covered"> 081 (and (map? o) </span><br/> <span class="covered" title="6 out of 6 forms covered"> 082 (location? (:source o) world) </span><br/> <span class="covered" title="6 out of 6 forms covered"> 083 (location? (:destination o) world) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 084 (keyword? (:property o)) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 085 (pos? (:quantity o))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 086 (catch Exception e </span><br/> <span class="not-covered" title="0 out of 20 forms covered"> 087 (warn (format "Not a valid flow: %s; %s" o (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 088 false))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 089 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 090 (defn execute </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 091 "Return a world like this `world`, except with the quantity of the property </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 092 described in this `flow` object transferred from the source of that flow </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 093 to its destination." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 094 [world flow] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 095 (try </span><br/> <span class="covered" title="6 out of 6 forms covered"> 096 (let [sx (-> flow :source :x) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 097 sy (-> flow :source :y) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 098 source (get-cell world sx sy) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 099 dx (-> flow :destination :x) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 100 dy (-> flow :destination :y) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 101 dest (get-cell world dx dy) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 102 p (:property flow) </span><br/> <span class="partial" title="16 out of 22 forms covered"> 103 q (min (:quantity flow) (get-num source p)) </span><br/> <span class="covered" title="2 out of 2 forms covered"> 104 s' (add-history-event </span><br/> <span class="covered" title="9 out of 9 forms covered"> 105 (assoc source p (- (source p) q)) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 106 (:rule flow) </span><br/> <span class="covered" title="13 out of 13 forms covered"> 107 {:direction :sent :other {:x dx :y dy} :property p :quantity q}) </span><br/> <span class="covered" title="2 out of 2 forms covered"> 108 d' (add-history-event </span><br/> <span class="partial" title="20 out of 24 forms covered"> 109 (assoc dest p (+ (get-num dest p) q)) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 110 (:rule flow) </span><br/> <span class="covered" title="13 out of 13 forms covered"> 111 {:direction :received :other {:x sx :y sy} :property p :quantity q})] </span><br/> <span class="covered" title="6 out of 6 forms covered"> 112 (if (= q (:quantity flow)) </span><br/> <span class="covered" title="18 out of 18 forms covered"> 113 (info (format "Moving %f units of %s from %d,%d to %d,%d" </span><br/> <span class="covered" title="9 out of 9 forms covered"> 114 (float q) (name p) sx sy dx dy)) </span><br/> <span class="covered" title="18 out of 18 forms covered"> 115 (warn (format "Moving %s from %d,%d to %d,%d; %f units ordered but only %f available" </span><br/> <span class="covered" title="13 out of 13 forms covered"> 116 (name p) sx sy dx dy (float (:quantity flow)) (float q)))) </span><br/> <span class="covered" title="7 out of 7 forms covered"> 117 (merge-cell (merge-cell world s') d')) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 118 (catch Exception e </span><br/> <span class="not-covered" title="0 out of 20 forms covered"> 119 (warn (format "Failed to execute flow %s: %s" flow (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 120 ;; return the world unmodified. </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 121 world))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 122 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 123 (defn execute-flows </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 124 "Return a world like this `world`, but with each of these flows executed." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 125 [world flows] </span><br/> <span class="covered" title="12 out of 12 forms covered"> 126 (reduce execute world (filter #(flow? % world) flows))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 127 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 128 (defn- plan-cell-flows </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 129 [world cell rules] </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 130 (map ;; across all the rules </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 131 (fn [rule] (let [r (try </span><br/> <span class="not-covered" title="0 out of 7 forms covered"> 132 (apply rule (list cell world)) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 133 (catch Exception any </span><br/> <span class="not-covered" title="0 out of 4 forms covered"> 134 (throw (ex-info "Planning of flows failed" </span><br/> <span class="not-covered" title="0 out of 8 forms covered"> 135 (merge (meta rule) {:cell cell}) </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 136 any))))] </span><br/> <span class="not-covered" title="0 out of 12 forms covered"> 137 (when r (map #(assoc % :rule rule) r)))) </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 138 rules)) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 139 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 140 (defn plan-flows </span><br/> <span class="partial" title="3 out of 4 forms covered"> 141 "Plan, but do not execute, all the flows in this `world` implied by those of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 142 these `rules` (which are expected to be pre-compiled) which are </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 143 flow rules. Return the list of plans, as flow objects." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 144 [world rules] </span><br/> <span class="not-covered" title="0 out of 3 forms covered"> 145 (remove nil? </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 146 (flatten </span><br/> <span class="not-covered" title="0 out of 2 forms covered"> 147 (map-world </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 148 world </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 149 plan-cell-flows </span><br/> <span class="partial" title="4 out of 17 forms covered"> 150 (list (filter #(= :flow (rule-type %)) rules)))))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 151 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 152 (defn flow-world </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 153 "Return a world derived from this `world` by applying the flow rules </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 154 found among these `rules` to each cell, and executing all the flows </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 155 planned." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 156 [world rules] </span><br/> <span class="not-covered" title="0 out of 7 forms covered"> 157 (execute-flows world (plan-flows world rules))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 158 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 159 ;; building blocks for compiled flow rules </span><br/> <span class="blank" title="0 out of 0 forms covered"> 160 </span><br/> <span class="covered" title="32 out of 32 forms covered"> 161 (defmacro create-location </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 162 [cell] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 163 `(select-keys ~cell [:x :y])) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 164 </span><br/> <span class="covered" title="56 out of 56 forms covered"> 165 (defmacro create-flow-quantity </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 166 [source dest prop quantity] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 167 `{:source (create-location ~source) </span><br/> <span class="covered" title="1 out of 1 forms covered"> 168 :destination (create-location ~dest) </span><br/> <span class="covered" title="1 out of 1 forms covered"> 169 :prop ~prop </span><br/> <span class="covered" title="1 out of 1 forms covered"> 170 :quantity ~quantity}) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 171 </span><br/> <span class="covered" title="48 out of 48 forms covered"> 172 (defmacro create-flow-fraction </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 173 [source dest prop fraction] </span><br/> <span class="covered" title="3 out of 3 forms covered"> 174 `(create-flow-quantity ~source ~dest ~prop </span><br/> <span class="covered" title="3 out of 3 forms covered"> 175 (* ~fraction (get-num ~source ~prop)))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 176 </span><br/> <span class="covered" title="36 out of 36 forms covered"> 177 (defmacro create-flow-percent </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 178 [source dest prop percent] </span><br/> <span class="covered" title="4 out of 4 forms covered"> 179 `(create-flow-fraction ~source ~dest ~prop (/ ~percent 100))) </span><br/> </body> </html>