From c739dd70940fb784f0643844231330419c02c7b5 Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Sat, 6 Apr 2024 16:54:10 +0100 Subject: [PATCH] Drainage is now *mostly* working... not sure about rivers flowing out of lakes. --- docs/codox/index.html | 13 +++--- docs/codox/intro.html | 2 +- docs/codox/mw-engine.core.html | 13 ++++-- docs/codox/mw-engine.display.html | 2 +- docs/codox/mw-engine.drainage.html | 36 +++++++------- docs/codox/mw-engine.flow.html | 22 +++++---- docs/codox/mw-engine.heightmap.html | 2 +- docs/codox/mw-engine.natural-rules.html | 2 +- docs/codox/mw-engine.utils.html | 48 ++++++++++--------- docs/codox/mw-engine.world.html | 16 ++++--- src/cljc/mw_engine/drainage.clj | 62 +++++++++++++++---------- src/cljc/mw_engine/utils.clj | 5 +- 12 files changed, 126 insertions(+), 97 deletions(-) diff --git a/docs/codox/index.html b/docs/codox/index.html index 1c9e38a..748a2e7 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,11 +1,12 @@ -Mw-engine 0.2.0-SNAPSHOT

Mw-engine 0.2.0-SNAPSHOT

Released under the GNU General Public License v2

Cellular automaton world builder.

Installation

To install, add the following dependency to your project or build file:

[mw-engine "0.2.0-SNAPSHOT"]

Topics

Namespaces

mw-engine.core

Functions to transform a world and run rules.

-

Public variables and functions:

mw-engine.display

Simple functions to allow a world to be visualised.

+Mw-engine 0.3.0-SNAPSHOT

Mw-engine 0.3.0-SNAPSHOT

Released under the GNU General Public License v2

Cellular automaton world builder.

Installation

To install, add the following dependency to your project or build file:

[mw-engine "0.3.0-SNAPSHOT"]

Topics

Namespaces

mw-engine.core

Functions to transform a world and run rules.

+

mw-engine.display

Simple functions to allow a world to be visualised.

mw-engine.drainage

Experimental, probably of no interest to anyone else; attempt to compute drainage on a world, assumed to have altitudes already set from a heightmap.

mw-engine.flow

Allow flows of values between cells in the world.

-

mw-engine.heightmap

Functions to apply a heightmap to a world.

mw-engine.natural-rules

A set of MicroWorld rules describing a simplified natural ecosystem.

-

mw-engine.world

Functions to create and to print two dimensional cellular automata.

-

Public variables and functions:

\ No newline at end of file +

mw-engine.world

Functions to create and to print two dimensional cellular automata.

+
\ No newline at end of file diff --git a/docs/codox/intro.html b/docs/codox/intro.html index 2dd3792..9baafa0 100644 --- a/docs/codox/intro.html +++ b/docs/codox/intro.html @@ -1,5 +1,5 @@ -Introduction to mw-engine

Introduction to mw-engine

+Introduction to mw-engine

Introduction to mw-engine

TODO: write great documentation

\ No newline at end of file diff --git a/docs/codox/mw-engine.core.html b/docs/codox/mw-engine.core.html index 229b84b..9b89698 100644 --- a/docs/codox/mw-engine.core.html +++ b/docs/codox/mw-engine.core.html @@ -1,19 +1,22 @@ -mw-engine.core documentation

mw-engine.core

Functions to transform a world and run rules.

+mw-engine.core documentation

mw-engine.core

Functions to transform a world and run rules.

Every rule is a function of two arguments, a cell and a world. If the rule fires, it returns a new cell, which should have the same values for :x and :y as the old cell. Anything else can be modified.

While any function of two arguments can be used as a rule, a special high level rule language is provided by the mw-parser package, which compiles rules expressed in a subset of English rules into suitable functions.

A cell is a map containing at least values for the keys :x, :y, and :state; a transformation should not alter the values of :x or :y, and should not return a cell without a keyword as the value of :state. Anything else is legal.

A world is a two dimensional matrix (sequence of sequences) of cells, such that every cell’s :x and :y properties reflect its place in the matrix. See world.clj.

Each time the world is transformed (see transform-world), for each cell, rules are applied in turn until one matches. Once one rule has matched no further rules can be applied to that cell.

-

apply-rule

(apply-rule world cell rule)(apply-rule world cell rule source)

Apply a single rule to a cell. What this is about is that I want to be able, for debugging purposes, to tag a cell with the rule text of the rule which fired (and especially so when an exception is thrown. So a rule may be either an ifn, or a list (ifn source-text). This function deals with despatching on those two possibilities. world is also passed in in order to be able to access neighbours.

-

run-world

(run-world world init-rules rules generations)

Run this world with these rules for this number of generations.

+

*with-history*

dynamic

I suspect that caching history on the cells is greatly worsening the memory problems. Make it optional, but by default false.

+

apply-rule

(apply-rule world cell rule)

Apply a single rule to a cell. What this is about is that I want to be able, for debugging purposes, to tag a cell with the rule text of the rule which fired (and especially so when an exception is thrown).

+

known-rule-types

Types of rules we know about.

+

run-world

(run-world world rules generations)(run-world world init-rules rules generations)

Run this world with these rules for this number of generations.

  • world a world as discussed above;
  • init-rules a sequence of rules as defined above, to be run once to initialise the world;
  • rules a sequence of rules as defined above, to be run iteratively for each generation;
  • generations an (integer) number of generations.
+

NOTE THAT all rules must be tagged with rule-type metadata, or they will not be executed.

Return the final generation of the world.

-

transform-world

(transform-world world rules)

Return a world derived from this world by applying these rules to each cell.

-
\ No newline at end of file +

transform-world

(transform-world world rules)

Return a world derived from this world by applying the production rules found among these rules to each cell.

+
\ No newline at end of file diff --git a/docs/codox/mw-engine.display.html b/docs/codox/mw-engine.display.html index 287f634..1d58efa 100644 --- a/docs/codox/mw-engine.display.html +++ b/docs/codox/mw-engine.display.html @@ -1,6 +1,6 @@ -mw-engine.display documentation

mw-engine.display

Simple functions to allow a world to be visualised.

+mw-engine.display documentation

mw-engine.display

Simple functions to allow a world to be visualised.

*image-base*

dynamic

Base url (i.e., url of directory) from which to load tile images.

format-css-class

(format-css-class state)

Format this state, assumed to be a keyword indicating a state in the world, into a CSS class

format-image-path

(format-image-path state)

Render this state, assumed to be a keyword indicating a state in the world, into a path which should recover the corresponding image file.

diff --git a/docs/codox/mw-engine.drainage.html b/docs/codox/mw-engine.drainage.html index 1726886..fa2d299 100644 --- a/docs/codox/mw-engine.drainage.html +++ b/docs/codox/mw-engine.drainage.html @@ -1,22 +1,22 @@ -mw-engine.drainage documentation

mw-engine.drainage

Experimental, probably of no interest to anyone else; attempt to compute drainage on a world, assumed to have altitudes already set from a heightmap.

+mw-engine.drainage documentation

mw-engine.drainage

Experimental, probably of no interest to anyone else; attempt to compute drainage on a world, assumed to have altitudes already set from a heightmap.

*sealevel*

dynamic

TODO: write docs

-

explore-lake

(explore-lake _world _cell)

Return a sequence of cells starting with this cell in this world which form a contiguous lake

-

find-lakes

(find-lakes _world)

TODO: write docs

-

flood-hollow

(flood-hollow _world cell neighbours)(flood-hollow world cell)

Raise the altitude of a copy of this cell of this world to the altitude of the lowest of its neighbours.

-

flood-hollows

(flood-hollows world)

Flood all local hollows in this world. At this stage only floods single cell hollows.

-

flow

Compute the total flow upstream of this cell in this world, and return a cell identical to this one but having a value of its flow property set from that computation. The function is memoised because the consequence of mapping a recursive function across an array is that many cells will be revisited - potentially many times.

+

explore-lake

(explore-lake _world _cell)

Return a sequence of cells starting with this cell in this world which form a contiguous lake

+

find-lakes

(find-lakes world)

Identify cells in this world which are lakes.

+

flood-hollow

(flood-hollow _world cell neighbours)(flood-hollow world cell)

Raise the altitude of a copy of this cell of this world to the altitude of the lowest of its neighbours.

+

flood-hollows

(flood-hollows world)

Flood all local hollows in this world. At this stage only floods single cell hollows.

+

flow

Compute the total flow upstream of this cell in this world, and return a cell identical to this one but having a value of its flow property set from that computation. The function is memoised because the consequence of mapping a recursive function across an array is that many cells will be revisited - potentially many times.

Flow comes from a higher cell to a lower only if the lower is the lowest neighbour of the higher.

-

flow-contributors

(flow-contributors cell world)

Return a list of the cells in this world which are higher than this cell and for which this cell is the lowest neighbour, or which are at the same altitude and have greater flow

-

flow-nr

(flow-nr cell world)

Experimental non recursive flow algorithm, needs to be run on a world as many times as there are distinct altitude values. This algorithm works only if applied sequentially from the highest altitude to the lowest, see flow-world-nr.

-

flow-world

(flow-world world)

Return a world like this world, but with cells tagged with the amount of water flowing through them.

-

flow-world-nr

(flow-world-nr world)

Experimental non-recursive flow-world algorithm

-

is-hollow

(is-hollow world cell)

Detects point hollows - that is, individual cells all of whose neighbours are higher. Return true if this cell has an altitude lower than any of its neighbours in this world

-

is-lake?

(is-lake? world cell)

If this cell in this world is not part of a lake, return nil. If it is, return a cell like this cell tagged as part of a lake.

-

max-altitude

TODO: write docs

-

rain-row

(rain-row row)(rain-row row rain-probability)(rain-row row map-width previous-altitude drops-in-cloud)

Return a row like this row, across which rainfall has been distributed; if rain-probability is specified, it is the probable rainfall on a cell with no gradient.

-

rain-world

(rain-world world)

Simulate rainfall on this world. TODO: Doesn’t really work just now - should rain more on west-facing slopes, and less to the east of high ground

-

rainfall

(rainfall gradient remaining map-width)

Compute rainfall for a cell with this gradient west-east, given remaining drops to distribute, and this overall map width.

-

run-drainage

(run-drainage hmap)

Create a world from the heightmap hmap, rain on it, and then compute river flows.

-
\ No newline at end of file +

flow-contributors

(flow-contributors cell world)

Return a list of the cells in this world which are higher than this cell and for which this cell is the lowest neighbour, or which are at the same altitude and have greater flow

+

flow-nr

(flow-nr cell world)

Experimental non recursive flow algorithm, needs to be run on a world as many times as there are distinct altitude values. This algorithm works only if applied sequentially from the highest altitude to the lowest, see flow-world-nr.

+

flow-world

(flow-world world)

Return a world like this world, but with cells tagged with the amount of water flowing through them.

+

flow-world-nr

(flow-world-nr world)

Experimental non-recursive flow-world algorithm

+

is-hollow

(is-hollow world cell)

Detects point hollows - that is, individual cells all of whose neighbours are higher. Return true if this cell has an altitude lower than any of its neighbours in this world

+

is-lake?

(is-lake? world cell)

If this cell in this world is not part of a lake, return nil. If it is, return a cell like this cell tagged as part of a lake.

+

max-altitude

TODO: write docs

+

rain-row

(rain-row row)(rain-row row rain-probability)(rain-row row map-width previous-altitude drops-in-cloud)

Return a row like this row, across which rainfall has been distributed; if rain-probability is specified, it is the probable rainfall on a cell with no gradient.

+

rain-world

(rain-world world)

Simulate rainfall on this world. TODO: Doesn’t really work just now - should rain more on west-facing slopes, and less to the east of high ground

+

rainfall

(rainfall gradient remaining map-width)

Compute rainfall for a cell with this gradient west-east, given remaining drops to distribute, and this overall map width.

+

run-drainage

(run-drainage hmap)

Create a world from the heightmap hmap, rain on it, and then compute river flows.

+
\ No newline at end of file diff --git a/docs/codox/mw-engine.flow.html b/docs/codox/mw-engine.flow.html index 30863b5..5548d21 100644 --- a/docs/codox/mw-engine.flow.html +++ b/docs/codox/mw-engine.flow.html @@ -1,6 +1,6 @@ -mw-engine.flow documentation

mw-engine.flow

Allow flows of values between cells in the world.

+mw-engine.flow documentation

mw-engine.flow

Allow flows of values between cells in the world.

The design here is: a flow object is a map with the following properties:

  1. :source, whose value is a location;
  2. @@ -16,12 +16,14 @@

    To execute a flow is transfer the quantity specified of the property specified from the cell at the source specified to the cell at the destination specified; if the source doesn’t have sufficient of the property, then all it has should be transferred, but no more: properties to be flowed cannot be pulled negative.

    Flowing values through the world is consequently a two stage process: firstly there’s a planning stage, in which all the flows to be executed are computed without changing the world, and then an execution stage, where they’re all executed. This namespace deals with mainly with execution.

coordinate?

(coordinate? o world)

Return true if this object o is a valid coordinate with respect to this world, else false. Assumes square worlds.

-

create-flow-fraction

macro

(create-flow-fraction source dest prop fraction)

TODO: write docs

-

create-flow-percent

macro

(create-flow-percent source dest prop percent)

TODO: write docs

-

create-flow-quantity

macro

(create-flow-quantity source dest prop quantity)

TODO: write docs

-

create-location

macro

(create-location cell)

TODO: write docs

-

execute

(execute world flow)

Return a world like this world, except with the quantity of the property described in this flow object transferred from the source of that flow to its destination.

-

execute-flows

(execute-flows world flows)

Return a world like this world, but with each of these flows executed.

-

flow?

(flow? o world)

Return true if this object o is a flow as defined above with respect to this world, else false. Assumes square worlds.

-

location?

(location? o world)

Return true if this object o is a location as defined above with respect to this world, else false.

-
\ No newline at end of file +

create-flow-fraction

macro

(create-flow-fraction source dest prop fraction)

TODO: write docs

+

create-flow-percent

macro

(create-flow-percent source dest prop percent)

TODO: write docs

+

create-flow-quantity

macro

(create-flow-quantity source dest prop quantity)

TODO: write docs

+

create-location

macro

(create-location cell)

TODO: write docs

+

execute

(execute world flow)

Return a world like this world, except with the quantity of the property described in this flow object transferred from the source of that flow to its destination.

+

execute-flows

(execute-flows world flows)

Return a world like this world, but with each of these flows executed.

+

flow-world

(flow-world world rules)

Return a world derived from this world by applying the flow rules found among these rules to each cell, and executing all the flows planned.

+

flow?

(flow? o world)

Return true if this object o is a flow as defined above with respect to this world, else false. Assumes square worlds.

+

location?

(location? o world)

Return true if this object o is a location as defined above with respect to this world, else false.

+

plan-flows

(plan-flows world rules)

Plan, but do not execute, all the flows in this world implied by those of these rules (which are expected to be pre-compiled) which are flow rules. Return the list of plans, as flow objects.

+
\ No newline at end of file diff --git a/docs/codox/mw-engine.heightmap.html b/docs/codox/mw-engine.heightmap.html index 465c151..f5d987f 100644 --- a/docs/codox/mw-engine.heightmap.html +++ b/docs/codox/mw-engine.heightmap.html @@ -1,6 +1,6 @@ -mw-engine.heightmap documentation

mw-engine.heightmap

Functions to apply a heightmap to a world.

+mw-engine.heightmap documentation

mw-engine.heightmap

Functions to apply a heightmap to a world.

Heightmaps are considered only as greyscale images, so colour is redundent (will be ignored). Darker shades are higher.

apply-heightmap

(apply-heightmap world imagepath)(apply-heightmap imagepath)

Apply the image file loaded from this path to this world, and return a world whose altitudes are modified (added to) by the altitudes in the heightmap. It is assumed that the heightmap is at least as large in x and y dimensions as the world. Note that, in addition to setting the :altitude of each cell, this function also sets the :gradient.

    diff --git a/docs/codox/mw-engine.natural-rules.html b/docs/codox/mw-engine.natural-rules.html index 8392590..e37c174 100644 --- a/docs/codox/mw-engine.natural-rules.html +++ b/docs/codox/mw-engine.natural-rules.html @@ -1,6 +1,6 @@ -mw-engine.natural-rules documentation

    mw-engine.natural-rules

    A set of MicroWorld rules describing a simplified natural ecosystem.

    +mw-engine.natural-rules documentation

    mw-engine.natural-rules

    A set of MicroWorld rules describing a simplified natural ecosystem.

    Since the completion of the rule language this is more or less obsolete - there are still a few things that you can do with rules written in Clojure that you can’t do in the rule language, but not many and I doubt they’re important.

    herbivore-rules

    TODO: write docs

    init-rules

    TODO: write docs

    diff --git a/docs/codox/mw-engine.utils.html b/docs/codox/mw-engine.utils.html index 864449d..74001d9 100644 --- a/docs/codox/mw-engine.utils.html +++ b/docs/codox/mw-engine.utils.html @@ -1,21 +1,22 @@ -mw-engine.utils documentation

    mw-engine.utils

    Utility functions needed by MicroWorld and, specifically, in the interpretation of MicroWorld rule.

    -

    get-cell

    (get-cell world x y)

    Return the cell a x, y in this world, if any.

    +mw-engine.utils documentation

    mw-engine.utils

    Utility functions needed by MicroWorld and, specifically, in the interpretation of MicroWorld rule.

    +

    add-history-event

    (add-history-event cell rule)(add-history-event result rule detail)

    If cell is non-nil, expect it to be a map representing a cell; add to its history an an event recording the firing of this rule. If detail is passed, treat it as a map of additional data to be added to the event.

    +

    get-cell

    (get-cell world x y)

    Return the cell a x, y in this world, if any.

    • world a world as defined in world.clj;
    • x a number which may or may not be a valid x coordinate within that world;
    • y a number which may or may not be a valid y coordinate within that world.
    -

    get-int

    (get-int map key)

    Get the value of a property expected to be an integer from a map; if not present (or not an integer) return 0.

    +

    get-int

    (get-int map key)

    Get the value of a property expected to be an integer from a map; if not present (or not an integer) return 0.

    • map a map;
    • key a symbol or keyword, presumed to be a key into the map.
    -

    get-int-or-zero

    (get-int-or-zero map property)

    Return the value of this property from this map if it is a integer; otherwise return zero.

    -

    get-least-cell

    (get-least-cell cells property)

    Return the cell from among these cells which has the lowest numeric value for this property.

    -

    get-most-cell

    (get-most-cell cells property)

    Return the cell from among these cells which has the highest numeric value for this property.

    -

    get-neighbours

    (get-neighbours world x y depth)(get-neighbours world cell depth)(get-neighbours world cell)

    Get the neighbours to distance depth of a cell in this world.

    +

    get-int-or-zero

    (get-int-or-zero map property)

    Return the value of this property from this map if it is a integer; otherwise return zero.

    +

    get-least-cell

    (get-least-cell cells property)

    Return the cell from among these cells which has the lowest numeric value for this property.

    +

    get-most-cell

    (get-most-cell cells property)

    Return the cell from among these cells which has the highest numeric value for this property.

    +

    get-neighbours

    (get-neighbours world x y depth)(get-neighbours world cell depth)(get-neighbours world cell)

    Get the neighbours to distance depth of a cell in this world.

    Several overloads: * world a world, as described in world.clj; * cell a cell within that world Gets immediate neighbours of the specified cell.

    • world a world, as described inworld.clj;
    • @@ -28,7 +29,7 @@
    • y an integer representing an y coordinate in that world;
    • depth an integer representing the distance from x,y that should be searched Gets the neighbours within the specified distance of the cell at coordinates x,y in this world.
    -

    get-neighbours-with-property-value

    (get-neighbours-with-property-value world x y depth property value op)(get-neighbours-with-property-value world x y depth property value)(get-neighbours-with-property-value world cell depth property value)(get-neighbours-with-property-value world cell property value)

    Get the neighbours to distance depth of the cell at x, y in this world which have this value for this property.

    +

    get-neighbours-with-property-value

    (get-neighbours-with-property-value world x y depth property value op)(get-neighbours-with-property-value world x y depth property value)(get-neighbours-with-property-value world cell depth property value)(get-neighbours-with-property-value world cell property value)

    Get the neighbours to distance depth of the cell at x, y in this world which have this value for this property.

    • world a world, as described in world.clj;
    • cell a cell within that world;
    • @@ -38,41 +39,44 @@
    • op a comparator function to use in place of = (optional).

    It gets messy.

    -

    get-neighbours-with-state

    (get-neighbours-with-state world x y depth state)(get-neighbours-with-state world cell depth state)(get-neighbours-with-state world cell state)

    Get the neighbours to distance depth of the cell at x, y in this world which have this state.

    +

    get-neighbours-with-state

    (get-neighbours-with-state world x y depth state)(get-neighbours-with-state world cell depth state)(get-neighbours-with-state world cell state)

    Get the neighbours to distance depth of the cell at x, y in this world which have this state.

    • world a world, as described in world.clj;
    • cell a cell within that world;
    • depth an integer representing the distance from x,y that should be searched;
    • state a keyword representing a state in the world.
    -

    get-num

    (get-num map key)

    Get the value of a property expected to be a number from a map; if not present (or not a number) return 0.

    +

    get-num

    macro

    (get-num map key)

    Get the value of a property expected to be a number from a map; if not present (or not a number) return 0.

    • map a map;
    • key a symbol or keyword, presumed to be a key into the map.
    -

    in-bounds

    deprecated in 1.1.7

    (in-bounds world x y)

    True if x, y are in bounds for this world (i.e., there is a cell at x, y) else false. DEPRECATED: it’s a predicate, prefer in-bounds?.

    +

    history-string

    (history-string cell)

    Return the history of this cell as a string for presentation to the user.

    +

    in-bounds

    deprecated in 1.1.7

    (in-bounds world x y)

    True if x, y are in bounds for this world (i.e., there is a cell at x, y) else false. DEPRECATED: it’s a predicate, prefer in-bounds?.

    • world a world as defined in world.clj;
    • x a number which may or may not be a valid x coordinate within that world;
    • y a number which may or may not be a valid y coordinate within that world.
    -

    in-bounds?

    added in 1.1.7

    (in-bounds? world x y)

    True if x, y are in bounds for this world (i.e., there is a cell at x, y) else false.

    +

    in-bounds?

    added in 1.1.7

    (in-bounds? world x y)

    True if x, y are in bounds for this world (i.e., there is a cell at x, y) else false.

    • world a world as defined in world.clj;
    • x a number which may or may not be a valid x coordinate within that world;
    • y a number which may or may not be a valid y coordinate within that world.
    -

    init-generation

    (init-generation _ cell)

    Return a cell like this cell, but having a value for :generation, zero if the cell passed had no integer value for generation, otherwise the value taken from the cell passed. The world argument is present only for consistency with the rule engine and is ignored.

    -

    map-world

    (map-world world function)(map-world world function additional-args)

    Apply this function to each cell in this world to produce a new world. the arguments to the function will be the world, the cell, and any additional-args supplied. Note that we parallel map over rows but just map over cells within a row. That’s because it isn’t worth starting a new thread for each cell, but there may be efficiency gains in running rows in parallel.

    -

    map-world-n-n

    (map-world-n-n world function)(map-world-n-n world function additional-args)

    Wholly non-parallel map world implementation; see documentation for map-world.

    -

    map-world-p-p

    (map-world-p-p world function)(map-world-p-p world function additional-args)

    Wholly parallel map-world implementation; see documentation for map-world.

    -

    member?

    (member? elt col)

    Return ‘true’ if elt is a member of col, else ‘false’.

    -

    memo-get-neighbours

    Memoised get neighbours is more efficient when running deeply recursive algorithms on the same world. But it’s less efficient when running the engine in its normal iterative style, because then we will rarely call get naighbours on the same cell of the same world twice.

    -

    merge-cell

    (merge-cell world cell)

    Return a world like this world, but merge the values from this cell with those from the cell in the world with the same co-ordinates

    -

    population

    (population cell species)

    Return the population of this species in this cell. Currently a synonym for get-int, but may not always be (depending whether species are later implemented as actors)

    +

    init-generation

    (init-generation _ cell)

    Return a cell like this cell, but having a value for :generation, zero if the cell passed had no integer value for generation, otherwise the value taken from the cell passed. The world argument is present only for consistency with the rule engine and is ignored.

    +

    map-world

    (map-world world function)(map-world world function additional-args)

    Apply this function to each cell in this world to produce a new world. the arguments to the function will be the world, the cell, and any additional-args supplied. Note that we parallel map over rows but just map over cells within a row. That’s because it isn’t worth starting a new thread for each cell, but there may be efficiency gains in running rows in parallel.

    +

    map-world-n-n

    (map-world-n-n world function)(map-world-n-n world function additional-args)

    Wholly non-parallel map world implementation; see documentation for map-world.

    +

    map-world-p-p

    (map-world-p-p world function)(map-world-p-p world function additional-args)

    Wholly parallel map-world implementation; see documentation for map-world.

    +

    member?

    (member? elt col)

    Return ‘true’ if elt is a member of col, else ‘false’.

    +

    memo-get-neighbours

    Memoised get neighbours is more efficient when running deeply recursive algorithms on the same world. But it’s less efficient when running the engine in its normal iterative style, because then we will rarely call get naighbours on the same cell of the same world twice.

    +

    merge-cell

    (merge-cell world cell)

    Return a world like this world, but merge the values from this cell with those from the cell in the world with the same co-ordinates

    +

    population

    (population cell species)

    Return the population of this species in this cell. Currently a synonym for get-int, but may not always be (depending whether species are later implemented as actors)

    • cell a map;
    • species a keyword representing a species which may populate that cell.
    -

    set-property

    (set-property world cell property value)(set-property world x y property value)

    Return a world like this world but with the value of exactly one property of one cell changed to this value

    -
    \ No newline at end of file +

    rule-type

    (rule-type rule)

    Return the rule-type of this compiled rule.

    +

    set-property

    (set-property world cell property value)(set-property world x y property value)

    Return a world like this world but with the value of exactly one property of one cell changed to this value

    +

    summarise-history

    (summarise-history cell)

    Return, as a string, a shorter summary of the history of this cell

    +
    \ No newline at end of file diff --git a/docs/codox/mw-engine.world.html b/docs/codox/mw-engine.world.html index 8337954..fea1dba 100644 --- a/docs/codox/mw-engine.world.html +++ b/docs/codox/mw-engine.world.html @@ -1,23 +1,25 @@ -mw-engine.world documentation

    mw-engine.world

    Functions to create and to print two dimensional cellular automata.

    +mw-engine.world documentation

    mw-engine.world

    Functions to create and to print two dimensional cellular automata.

    Nothing in this namespace should determine what states are possible within the automaton, except for the initial state, :new.

    A cell is a map containing at least values for the keys :x, :y, and :state.

    A world is a two dimensional matrix (sequence of sequences) of cells, such that every cell’s :x and :y properties reflect its place in the matrix.

    -

    format-cell

    (format-cell cell)

    Return a formatted string summarising the current state of this cell.

    -

    make-cell

    macro

    (make-cell x y)

    Create a minimal default cell at x, y

    +

    cell?

    (cell? obj)

    Return true if obj is a cell, as understood by MicroWorld, else false.

    +

    format-cell

    (format-cell cell)

    Return a formatted string summarising the current state of this cell.

    +

    make-cell

    macro

    (make-cell x y)

    Create a minimal default cell at x, y

    • x the x coordinate at which this cell is created;
    • y the y coordinate at which this cell is created.
    -

    make-world

    (make-world width height)

    Make a world width cells from east to west, and height cells from north to south.

    +

    make-world

    (make-world width height)

    Make a world width cells from east to west, and height cells from north to south.

    • width a natural number representing the width of the matrix to be created;
    • height a natural number representing the height of the matrix to be created.
    -

    print-world

    (print-world world)

    Print the current state of this world, and return nil.

    +

    print-world

    (print-world world)

    Print the current state of this world, and return nil.

    • world a world as defined above.
    -

    truncate-state

    (truncate-state cell limit)

    Truncate the print name of the state of this cell to at most limit characters.

    -
    \ No newline at end of file +

    truncate-state

    (truncate-state cell limit)

    Truncate the print name of the state of this cell to at most limit characters.

    +

    world?

    (world? obj)

    Return true if obj is a world, as understood by MicroWorld, else false.

    +
    \ No newline at end of file diff --git a/src/cljc/mw_engine/drainage.clj b/src/cljc/mw_engine/drainage.clj index 4280c05..9ad5140 100644 --- a/src/cljc/mw_engine/drainage.clj +++ b/src/cljc/mw_engine/drainage.clj @@ -84,9 +84,10 @@ "Simulate rainfall on this `world`. TODO: Doesn't really work just now - should rain more on west-facing slopes, and less to the east of high ground" [world] - (map - rain-row - world)) + (info "rain-world started.") + (let [w' (into [] (map #(apply vector (rain-row %)) world))] + (info "rain-world completed") + w')) (defn flow-contributors @@ -134,9 +135,11 @@ "Flood all local hollows in this `world`. At this stage only floods single cell hollows." [world] - (map-world world - #(if (is-hollow %1 %2) (flood-hollow %1 %2) %2))) - + (info "flood-hollows started.") + (let [w' (map-world world + #(if (is-hollow %1 %2) (flood-hollow %1 %2) %2))] + (info "flood-hollows completed") + w')) (def max-altitude 255) @@ -157,12 +160,6 @@ (get-int-or-zero % :flow)) contributors))}))))) -(defn flow-nr-wrapper - [cell world] - (do - (info (format "Flowing cell at %d, %d" (:x cell) (:y cell))) - (flow-nr cell world))) - (def flow "Compute the total flow upstream of this `cell` in this `world`, and return a cell identical to this one but having a value of its flow property set from that computation. The function is @@ -185,13 +182,19 @@ (defn flow-world-nr "Experimental non-recursive flow-world algorithm" [world] - (run-world world (list (vary-meta flow-nr assoc :rule-type :ad-hoc)) max-altitude)) + (info "Non recursive flow-world started.") + (let [w' (run-world world (list (vary-meta flow-nr assoc :rule-type :ad-hoc)) max-altitude)] + (info "Non recursive flow-world completed") + w')) (defn flow-world "Return a world like this `world`, but with cells tagged with the amount of water flowing through them." [world] - (map-world (rain-world world) flow)) + (info "Recursive flow-world started.") + (let [w' (map-world (rain-world world) flow)] + (info "Recursive flow-world completed") + w')) (defn explore-lake "Return a sequence of cells starting with this `cell` in this `world` which @@ -202,23 +205,34 @@ "If this `cell` in this `world` is not part of a lake, return nil. If it is, return a cell like this `cell` tagged as part of a lake." [world cell] - (if + (cond ;; if it's already tagged as a lake, it's a lake - (:lake cell) cell - (let - [outflow (apply min (map :altitude (get-neighbours world cell)))] - (when-not - (> (:altitude cell) outflow) - (assoc cell :lake true :state :lake))))) - + (:lake cell) cell + (#{:lake :sea :water} (:state cell)) cell ;; if it's already tagged as + ;; wet, no need for change + (and (integer? (:x cell)) (integer? (:y cell))) + (let + [outflow (apply min (map :altitude (get-neighbours world cell)))] + (when-not + (> (:altitude cell) outflow) + (do + (info (format "tagging cell at %d, %d as lake" (:x cell) (:y cell))) + (assoc cell :lake true :state :lake)))) + :else (throw (ex-info "Invalid cell?" + {:cell cell})))) (defn find-lakes - [_world]) + "Identify cells in this `world` which are lakes." + [world] + (info "find-lakes started.") + (let [w' (map-world world is-lake?)] + (info "find-lakes completed.") + w')) (defn run-drainage "Create a world from the heightmap `hmap`, rain on it, and then compute river flows." [hmap] - (flow-world (rain-world (flood-hollows (apply-heightmap hmap))))) + (find-lakes (flow-world-nr (rain-world (flood-hollows (apply-heightmap hmap)))))) ;; (run-drainage "resources/heightmaps/20x20/crucible.png") \ No newline at end of file diff --git a/src/cljc/mw_engine/utils.clj b/src/cljc/mw_engine/utils.clj index a45ef0e..635e0d7 100644 --- a/src/cljc/mw_engine/utils.clj +++ b/src/cljc/mw_engine/utils.clj @@ -198,7 +198,10 @@ Gets the neighbours within the specified distance of the cell at coordinates [x,y] in this world." ([world x y depth] - (memo-get-neighbours world x y depth)) + (if (and (integer? x) (integer? y) (integer? depth)) + (memo-get-neighbours world x y depth) + (throw (ex-info "get-neighbours: integer arguments expected." + {:x x :y y :depth depth})))) ([world cell depth] (memo-get-neighbours world (:x cell) (:y cell) depth)) ([world cell]