<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 [get-cell get-num in-bounds? merge-cell]] </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 026 [taoensso.timbre :refer [info warn]])) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 027 </span><br/> <span class="not-tracked" 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 ;;;; mw-engine: the state/transition engine of MicroWorld. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 031 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 032 ;;;; This program is free software; you can redistribute it and/or </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 033 ;;;; modify it under the terms of the GNU General Public License </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 034 ;;;; as published by the Free Software Foundation; either version 2 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 035 ;;;; of the License, or (at your option) any later version. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 036 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 037 ;;;; 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"> 038 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 039 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 040 ;;;; GNU General Public License for more details. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 041 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 042 ;;;; 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"> 043 ;;;; along with this program; if not, write to the Free Software </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 044 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 045 ;;;; USA. </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 046 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 047 ;;;; Copyright (C) 2014 Simon Brooke </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 048 ;;;; </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 049 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; </span><br/> <span class="blank" title="0 out of 0 forms covered"> 050 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 051 (defn coordinate? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 052 "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"> 053 this `world`, else `false`. Assumes square worlds." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 054 [o world] </span><br/> <span class="covered" title="2 out of 2 forms covered"> 055 (try </span><br/> <span class="covered" title="14 out of 14 forms covered"> 056 (and (or (zero? o) (pos-int? o)) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 057 (< o (count world))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 058 (catch Exception e </span><br/> <span class="covered" title="20 out of 20 forms covered"> 059 (warn (format "Not a valid coordinate: %s; %s" o (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 060 false))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 061 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 062 (defn location? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 063 "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"> 064 this `world`, else `false`." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 065 [o world] </span><br/> <span class="partial" title="1 out of 2 forms covered"> 066 (try </span><br/> <span class="partial" title="14 out of 16 forms covered"> 067 (and (map? o) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 068 (integer? (:x o)) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 069 (integer? (:y o)) </span><br/> <span class="covered" title="8 out of 8 forms covered"> 070 (in-bounds? world (:x o) (:y o))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 071 (catch Exception e </span><br/> <span class="not-covered" title="0 out of 20 forms covered"> 072 (warn (format "Not a valid location: %s; %s" o (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 073 false))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 074 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 075 (defn flow? </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 076 "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"> 077 this `world`, else `false`. Assumes square worlds." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 078 [o world] </span><br/> <span class="partial" title="1 out of 2 forms covered"> 079 (try </span><br/> <span class="partial" title="16 out of 20 forms covered"> 080 (and (map? o) </span><br/> <span class="covered" title="6 out of 6 forms covered"> 081 (location? (:source o) world) </span><br/> <span class="covered" title="6 out of 6 forms covered"> 082 (location? (:destination o) world) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 083 (keyword? (:property o)) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 084 (pos? (:quantity o))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 085 (catch Exception e </span><br/> <span class="not-covered" title="0 out of 20 forms covered"> 086 (warn (format "Not a valid flow: %s; %s" o (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 087 false))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 088 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 089 (defn execute </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 090 "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"> 091 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"> 092 to its destination." </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 093 [world flow] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 094 (try </span><br/> <span class="covered" title="6 out of 6 forms covered"> 095 (let [sx (-> flow :source :x) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 096 sy (-> flow :source :y) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 097 source (get-cell world sx sy) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 098 dx (-> flow :destination :x) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 099 dy (-> flow :destination :y) </span><br/> <span class="covered" title="5 out of 5 forms covered"> 100 dest (get-cell world dx dy) </span><br/> <span class="covered" title="3 out of 3 forms covered"> 101 p (:property flow) </span><br/> <span class="covered" title="8 out of 8 forms covered"> 102 q (min (:quantity flow) (get-num source p)) </span><br/> <span class="covered" title="9 out of 9 forms covered"> 103 s' (assoc source p (- (source p) q)) </span><br/> <span class="covered" title="10 out of 10 forms covered"> 104 d' (assoc dest p (+ (get-num dest p) q))] </span><br/> <span class="covered" title="6 out of 6 forms covered"> 105 (if (= q (:quantity flow)) </span><br/> <span class="covered" title="18 out of 18 forms covered"> 106 (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"> 107 (float q) (name p) sx sy dx dy)) </span><br/> <span class="covered" title="18 out of 18 forms covered"> 108 (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"> 109 (name p) sx sy dx dy (float (:quantity flow)) (float q)))) </span><br/> <span class="covered" title="7 out of 7 forms covered"> 110 (merge-cell (merge-cell world s') d')) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 111 (catch Exception e </span><br/> <span class="not-covered" title="0 out of 20 forms covered"> 112 (warn (format "Failed to execute flow %s: %s" flow (.getMessage e))) </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 113 ;; return the world unmodified. </span><br/> <span class="not-covered" title="0 out of 1 forms covered"> 114 world))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 115 </span><br/> <span class="covered" title="1 out of 1 forms covered"> 116 (defn execute-flows </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 117 "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"> 118 [world flows] </span><br/> <span class="covered" title="12 out of 12 forms covered"> 119 (reduce execute world (filter #(flow? % world) flows))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 120 </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 121 ;; building blocks for compiled flow rules </span><br/> <span class="blank" title="0 out of 0 forms covered"> 122 </span><br/> <span class="covered" title="32 out of 32 forms covered"> 123 (defmacro create-location </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 124 [cell] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 125 `(select-keys ~cell [:x :y])) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 126 </span><br/> <span class="covered" title="56 out of 56 forms covered"> 127 (defmacro create-flow-quantity </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 128 [source dest prop quantity] </span><br/> <span class="covered" title="1 out of 1 forms covered"> 129 `{:source (create-location ~source) </span><br/> <span class="covered" title="1 out of 1 forms covered"> 130 :destination (create-location ~dest) </span><br/> <span class="covered" title="1 out of 1 forms covered"> 131 :prop ~prop </span><br/> <span class="covered" title="1 out of 1 forms covered"> 132 :quantity ~quantity}) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 133 </span><br/> <span class="covered" title="48 out of 48 forms covered"> 134 (defmacro create-flow-fraction </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 135 [source dest prop fraction] </span><br/> <span class="covered" title="3 out of 3 forms covered"> 136 `(create-flow-quantity ~source ~dest ~prop </span><br/> <span class="covered" title="3 out of 3 forms covered"> 137 (* ~fraction (get-num ~source ~prop)))) </span><br/> <span class="blank" title="0 out of 0 forms covered"> 138 </span><br/> <span class="covered" title="36 out of 36 forms covered"> 139 (defmacro create-flow-percent </span><br/> <span class="not-tracked" title="0 out of 0 forms covered"> 140 [source dest prop percent] </span><br/> <span class="covered" title="4 out of 4 forms covered"> 141 `(create-flow-fraction ~source ~dest ~prop (/ ~percent 100))) </span><br/> </body> </html>