Water Flowing Uphill NetLogo Model
Produced for the book series "Artificial Intelligence";
Author: W. J. Teahan; Publisher: Ventus Publishing Aps, Denmark.
powered by NetLogo
view/download model file: Water-Flowing-Uphill.nlogo
WHAT IS IT?
This model tries to visually simulate one possible solution to the problem of trying to get water to flow uphill. The solution is to use tanks or reservoirs at increasing heights, and have these fill up one after the other. A stop-valve (shown in blue at the end of the connecting pipes between reservoirs in the model) is used to prevent back filling of the reservoirs - once the water moves through the valve, it cannot flow backwards.
Various methods are tried to simulate how the "water" flows. All of these methods are failures, some of them producing strange effects that are impossible in the real world. The reader is encouraged to add his or her own methods which better approximate how water flows in real life.
WHAT IS ITS PURPOSE?
The purpose of the model is to draw an analogy between a seemingly impossible task (trying to get water to flow uphill) and what some people claim to also be impossible (creating Artificial Intelligence). For both of these tasks, we can try to find solutions that change the problem in some way, or we can try to change the physical properties of the universe (i.e. have it work in a virtual environment, but it would not work when transferred to a real environment), or we could create an illusion (i.e. it would look like the problem was solved, but like magic, it would just be trickery.)
A secondary purpose is to highlight the process of problem solving that humans are very good at. No computer program is capable of creating the variety of solutions that are shown in this model. For that matter, no computer programs are yet capable of understanding the problem itself.
HOW IT WORKS
The model implements various types of agents to try to simulate water flow - turtle agents, patch agents, agents that are boids (from Craig Reynolds work) with a cone of vision, and particle agents that simulate the velocity of each particle and takes into consideration viscosity, wind and gravity.
None of these solutions work i.e. they behave nothing like water does.
HOW TO USE IT
Press the setup-environment button in the Interface first to reset the simulation. Then choose one of the solutions using the solution chooser. See what happens when you press the go-simulation button.
THE INTERFACE
The model's Interface buttons are defined as follows:
- setup-simulation: This will reset the simulation back to the start state which is where all the reserviors have no water in them, and the tap is turned off.
- go-simulation: This turns on the tap, and agents that simulate water droplets start flowing downwards out of the tap into the first reservoir on the left.
The model's Interface sliders, switches and chooser are defined as follows:
1. For when the solution chooser is set to either "turtle agents 1" or "turtle agents 2":
- drops-per-tick: This is the number of drops that will emerge out of the tap every tick.
- size-of-drop: This is the size of each drop.
- shape-of-drop: This is the shape of each drop. Possible shapes are "drop", "circle", "square" and "hex".
2. For when the solution chooser is set to "boid agents 1":
- rate-of-random-turn: This controls how much the boid randomly turns each tick.
- radius-angle and radius-length: This defines the boids' cone of vision.
- boid-speed: This specifies how far the boids should move each tick.
- boid-bounce: This specifies how far the boids should bounce back when they hit the tank walls or the earth.
3. For when the solution chooser is set to "particle agents 1":
- max-particles: This specifies the maximum number of particles and can be used to control how many particles come out of the tap.
- initial-velocity-x and initial-velocity-y: This specifies the initial velocity of the particle in the x and y directions.
- initial-range-x: This provides some random variability in the velocity in the x direction.
- restitution-constant: This can be used to control how much bounce with floor damping occurs if the particle touches the tank floor.
- gravity-constant: This is used to scale the force of the gravity according to the particle's mass thereby simulating the effect of air resistance.
- viscosity-constant: This can be used to simulate the effect of viscosity.
- rate: This controls the rate of particle emission.
- step-size: The new location and speed of each particle is calculated by multiplying the forces by this number. The ticks also advance by this number.
- wind-velocity-x and wind-velocity-y: This specifies the velocity of the wind in the x and y directions.
THINGS TO NOTICE
When the chooser solution in the Interface is set to "turtle agents 1", the agents behave a bit like water, but do not spread out at the bottom. Why? How can their behaviour be changed to fix the problem?
When the chooser solution in the Interface is set to "turtle agents 1", the agents behave a bit like snow. Why? How can their behaviour be changed so that they behave more like water?
When the chooser solution in the Interface is set to "patch agents 1", the agents do not flow correctly on the right. Why? How can their behaviour be changed so that the right side of the reservoir fills up correctly and then flows via the connecting pipe to the next reservoir and so on?
When the chooser solution in the Interface is set to "patch agents 2", "patch agents 3", or "patch agents 4", the simulations are complete failures. Can any of these be salvaged in order to find a possible solution?
When the chooser solution in the Interface is set to "boid agents 1", the agents behave a bit like spray-painting in some configurations. Why? Also, like the "turtle agents 1" solution, they do not spread out at the bottom. Why? How can their behaviour be changed to fix the problem? How can their behaviour be changed so that they behave more like water?
When the chooser solution in the Interface is set to "particle agents 1", the agents tunnel through the surrounding earth. Why? How can their behaviour be changed so that they behave more like water?
THINGS TO TRY
Try out each of the solutions in turn to see what happens. Which one seems to be closest to what happens in real life?
Try changing the values of the sliders to see what effect they have on each solution (for turtle agents solutions 1 and 2, patch agents solution 1, boids agents solution 1 and particle agents solution 1).
When the solution variable is set to "particle agents 1", try sliding the "max-particles" slider back and forth. The effect of this is that the tap will seem to be temporarily turned on and off.
EXTENDING THE MODEL
Try devising your own solution that simulates water flow more accurately. Observe the process of problem solving that you apply in order to come up with your solution. Would it be possible to devise a computer program to replicate your behaviour?
CREDITS AND REFERENCES
This model was created by William John Teahan.
To refer to this model in publications, please use:
Water Flowing Uphill NetLogo model.
Teahan, W. J. (2010). Artificial Intelligence. Ventus Publishing Aps
The particle agents code used in this model was obtained from the Particle System Waterfall produced by Uri Wilensky. See the model for for a list of references and how to cite it.
PROCEDURES
; Water Flowing Uphill model.
;
; How not to make water go uphill.
; See if you can do better than me.
;
; Copyright William John Teahan 2010. All Rights Reserved.
breed [taps tap] ; the tap
breed [drops drop] ; water drops simulated as turtle agents
breed [particles particle] ; water drops simulated using particle agents
particles-own
[
mass
velocity-x ; particle velocity in the x axis
velocity-y ; particle velocity in the y axis
force-accumulator-x ; force exerted in the x axis
force-accumulator-y ; force exerted in the y axis
]
globals
[
colour-of-earth ; colour used for earth around tanks
]
to setup-environment
ca ;; clear everything
ask patches
[
set pcolor white ;; make background full of white patches
if (pxcor >= min-pxcor) and (pxcor <= 15 - max-pxcor) and
(pycor >= max-pycor - 12) and (pycor <= max-pycor - 11)
[ set pcolor black ] ; drawn pipe to tap
; draw earth below the tanks
; this will make the code easier as we can make stray drops
; 'disappear' into the earth by killing them off
set colour-of-earth 38
if (pycor < max-pxcor - 100 + 7)
[ set pcolor colour-of-earth ]
if (pxcor > 8 - max-pxcor + 90) and (pycor < max-pycor - 44)
[ set pcolor colour-of-earth ]
if (pxcor > 8 - max-pxcor + 140) and (pycor < max-pycor - 24)
[ set pcolor colour-of-earth ]
]
create-taps 1
[ set shape "tap"
setxy (19 - max-pxcor) (max-pycor - 10)
set heading 0
set color gray
set size 10 ]
setup-tank (8 - max-pxcor) (max-pycor - 100) 40 35
setup-tank (8 - max-pxcor + 50) (max-pycor - 100 + 20) 40 35
setup-tank (8 - max-pxcor + 100) (max-pycor - 100 + 40) 40 35
setup-pipe (8 - max-pxcor + 40) (max-pycor - 100 + 30) 10 3
setup-pipe (8 - max-pxcor + 90) (max-pycor - 100 + 50) 10 3
end
to setup-tank [xpos ypos xlen ylen]
; sets up a water tank
ask patches
[
; draw left side
if (pxcor = xpos) and (pycor >= ypos) and (pycor <= ypos + ylen)
[ set pcolor black ]
; draw bottom
if (pxcor >= xpos) and (pxcor <= xpos + xlen) and (pycor = ypos)
[ set pcolor black ]
; draw right side
if (pxcor = xpos + xlen) and (pycor >= ypos) and (pycor <= ypos + ylen)
[ set pcolor black ]
; drawn white inside tank
if (pxcor >= xpos + 1) and (pxcor <= xpos + xlen - 1) and
(pycor >= ypos + 1) and (pycor <= ypos + ylen)
[ set pcolor white ]
]
end
to setup-pipe [xpos ypos xlen ylen]
; setups a pipe between tanks
ask patches
[
; draw top of pipe
if (pxcor >= xpos) and (pxcor <= xpos + xlen) and (pycor = ypos)
[ set pcolor gray ]
; draw bottom of pipe
if (pxcor >= xpos) and (pxcor <= xpos + xlen) and (pycor = ypos + ylen)
[ set pcolor gray ]
; clear left entrance of pipe, and pipe itself
if (pxcor >= xpos) and (pxcor <= xpos + xlen - 1) and
(pycor >= ypos + 1) and (pycor <= ypos + ylen - 1)
[ set pcolor white ]
; draw a light blue coloured light over right exit of pipe to indicate
; a one-way valve
if (pxcor = xpos + xlen) and (pycor >= ypos + 1) and (pycor <= ypos + ylen - 1)
[ set pcolor cyan ]
]
end
to go-simulation
; runs the simulation creating a steady folow of water
; using different solutions
if (solution = "turtle agents 1") ; a bit like water, but doesn't spread out at bottom
[ go-using-turtle-agents-1 ]
if (solution = "turtle agents 2") ; like snowflakes
[ go-using-turtle-agents-2 ]
if (solution = "turtle agents 3") ; try you own solution here
[ go-using-turtle-agents-3 ]
if (solution = "patch agents 1") ; like sand
[ go-using-patch-agents-1 ]
if (solution = "patch agents 2") ; failed initial attempt that covers whole screen
[ go-using-patch-agents-2 ]
if (solution = "patch agents 3") ; failed second attempt that fills tank with diagonal steps
[ go-using-patch-agents-3 ]
if (solution = "patch agents 4") ; failed third attempt that creates a rectangle of water
[ go-using-patch-agents-4 ]
if (solution = "patch agents 5") ; try you own solution here
[ go-using-patch-agents-5 ]
if (solution = "boid agents 1") ; like spray painting in some configurations
[ go-using-boid-agents-1 ]
if (solution = "boid agents 2") ; try you own solution here
[ go-using-boid-agents-2 ]
if (solution = "particle agents 1") ; tunnels through the surrounding earth
[ go-using-particle-agents-1 ]
if (solution = "particle agents 2") ; try you own solution here
[ go-using-particle-agents-2 ]
end
to create-new-drops
; creates the drops coming out of the tap each tick
create-drops drops-per-tick ; create drops of water
[ ; have the drops come out of the tap
set color blue
set shape shape-of-drop
set size size-of-drop
set heading 180 ; head down
setxy (22 - max-pxcor) (max-pycor - 15)
if (random 2 = 0) [ set xcor xcor + 1 ] ; half the drops come out of the right of the tap
]
end
to go-using-turtle-agents-1
; Solution 1 using turtle agents
; With this solution, the turtle agents flow down out of the tap OK,
; but they don't spread out to fill up the tank properly.
create-new-drops
ask drops
[ turtles-flow ] ; make all the water drops flow
end
to-report turtles-all-clear? [xinc yinc]
; returns true if the location at dx = xinc, dy - yinc is all clear
; i.e. no tank wall or another water drop
ifelse ([pcolor] of patch-at xinc yinc != white)
[ report false ]
[ report (count drops-at xinc yinc = 0) ]
end
to turtles-move-drop [xinc yinc]
; tries to move the drop in the direction dx=xinc, dy=yinc
if not (turtles-all-clear? xinc yinc)
[ stop ]
setxy xcor + xinc ycor + yinc
end
to turtles-flow ;; turtle procedure
; makes the water drops flow downwards or sideways, but never up!
; try to move down first
let white-patch one-of patches in-radius 1 with [pcolor = white]
if (white-patch = nobody)
[ stop ] ; can't move
ifelse (count drops-at 0 -1 > drops-per-tick) ; too many water drops below me
or ([pcolor] of patch-at 0 -1 = black) ; at edge of tank
[ turtles-spread-out white-patch ]
[ turtles-flow-down ]
end
to turtles-spread-out [white-patch];; turtle procedure
; makes the water drops spread out rather than fall down
; (when there are too many drops nearby)
setxy [pxcor] of white-patch [pycor] of white-patch
end
to turtles-flow-down ;; turtle procedure
; makes the water drops flow downhill due to gravity
set ycor ycor - 1 ; make movement downhill constant due to gravity
let r random 20
if (r = 0) and ([pcolor] of patch-at 1 0 != black)
[ set xcor xcor + 1 ] ; make a few drops move to the right
if (r = 1) and ([pcolor] of patch-at 1 0 != black)
[ set xcor xcor - 1 ] ; make a few drops move to the left
end
to go-using-turtle-agents-2
; A different solution using turtle agents.
; The water drops behave more like snow flakes
create-new-drops
ask drops
[ turtles-flow-1 ] ; make all the water drops flow
end
to turtles-flow-1
let r random 20
ifelse (r < 17)
[ turtles-move-drop 0 -1 ] ; move downwards due to gravity
[ ifelse (r < 18)
[ turtles-move-drop -1 0 ] ; move left
[ turtles-move-drop 1 0 ] ; move right
]
end
to go-using-turtle-agents-3
; Insert your own solution here.
user-message "Not implemented yet!"
end
to go-using-patch-agents-1
; Solution 1 uses patch agents.
; With this solution, the patch agents end up behaving like sand.
let tapx (19 - max-pxcor)
let tapy (max-pycor - 10)
let drawn false
ask patches
[ ; draw "water" at bottom of tap
if (pxcor >= tapx + 3) and (pxcor <= tapx + 4) and (pycor = tapy - 5)
[ set pcolor blue ]
]
ask patches with [pcolor = blue]
[
set drawn false
let r random 20
ifelse (r > 1)
[ set drawn patches-draw-drop? pxcor (pycor - 1) ] ; draw drop going down most of the time
[ ifelse (r = 1)
[ set drawn patches-draw-drop? (pxcor - 1) pycor ] ; draw drop left sometimes
[ set drawn patches-draw-drop? (pxcor + 1) pycor ] ; draw drop right sometimes
]
if (drawn)
[ set pcolor white ] ; drop has moved on; reset patch to white
]
end
to-report patches-draw-drop? [xpos ypos]
; draw a drop at location xpos, ypos if patch there is white
; return true if the drop was drawn, false otherwise
let drawn true
if (xpos < min-pxcor) or (xpos > max-pxcor) or
(ypos < min-pycor) or (ypos > max-pycor)
[ report false ] ; outside environment - ignore
ask patch xpos ypos
[ ifelse (pcolor != white)
[ set drawn false ]
[ set pcolor blue ]
]
report drawn
end
to-report patches-draw-drop-1? [xpos ypos]
; draw a drop at location xpos, ypos if patch there is not black
; return true if the drop was drawn, false otherwise
let drawn true
if (xpos < min-pxcor) or (xpos > max-pxcor) or
(ypos < min-pycor) or (ypos > max-pycor)
[ report false ] ; outside environment - ignore
ask patch xpos ypos
[ ifelse (pcolor = black)
[ set drawn false ]
[ set pcolor blue ]
]
report drawn
end
to go-using-patch-agents-2
; Solution 2 using patch agents.
; This fails completely (run it to see why).
let tapx (19 - max-pxcor)
let tapy (max-pycor - 10)
let drawn false
let r 0
ask patches
[ ; draw "water" at bottom of tap
if (pxcor >= tapx + 3) and (pxcor <= tapx + 4) and (pycor = tapy - 5)
[ set pcolor blue ]
]
ask patches with [pcolor = blue]
[
if not patches-draw-drop? pxcor (pycor - 1) ; check that down is free to move to
[ set drawn false
set r 0
ifelse (r = 0)
[ set drawn patches-draw-drop? (pxcor - 1) pycor ]; check left is free
[ set drawn patches-draw-drop? (pxcor + 1) pycor ]; check right is free
if not drawn
[ ifelse (r = 0)
[ set drawn patches-draw-drop? (pxcor + 1) pycor ]; check right is free
[ set drawn patches-draw-drop? (pxcor - 1) pycor ]; check left is free
]
]
]
end
to-report patches-hit-surface? [xpos ypos]
; draw a drop at location adjacent to xpos, ypos if the blue patch
; at xpos, ypos is surrounded by a blue patch on the left/right and below it.
let drawn false
if ([pcolor] of patch (xpos + 1) ypos = white) ; space to right
[ if ([pcolor] of patch xpos (ypos - 1) = blue) and
([pcolor] of patch (xpos + 1) (ypos - 1) = blue) and
([pcolor] of patch (xpos + 2) (ypos - 1) = blue)
[ set drawn true
ask patch (xpos + 1) ypos
[ set pcolor blue ]
]
]
if ([pcolor] of patch (xpos - 1) ypos = white) ; space to left
[ if ([pcolor] of patch xpos (ypos - 1) = blue) and
([pcolor] of patch (xpos - 1) (ypos - 1) = blue) and
([pcolor] of patch (xpos - 2) (ypos - 1) = blue)
[ set drawn true
ask patch (xpos - 1) ypos
[ set pcolor blue ]
]
]
report drawn
end
to-report patches-hit-surface-1? [xpos ypos]
; draw a drop at location adjacent to xpos, ypos if the blue patch
; at xpos, ypos is surrounded by a blue patch on the left/right and below it.
let drawn false
if ([pcolor] of patch (xpos + 1) ypos = white) ; space to right
[ if ([pcolor] of patch xpos (ypos - 1) = blue) and
([pcolor] of patch (xpos + 1) (ypos - 1) = blue)
[ set drawn true
ask patch (xpos + 1) ypos
[ set pcolor blue ]
]
]
if ([pcolor] of patch (xpos - 1) ypos = white) ; space to left
[ if ([pcolor] of patch xpos (ypos - 1) = blue) and
([pcolor] of patch (xpos - 1) (ypos - 1) = blue)
[ set drawn true
ask patch (xpos - 1) ypos
[ set pcolor blue ]
]
]
report drawn
end
to go-using-patch-agents-3
; Solution 3 using patch agents.
; This also fails completely (run it to see why).
let tapx (19 - max-pxcor)
let tapy (max-pycor - 10)
let drawn false
let r 0
ask patches
[ ; draw "water" at bottom of tap
if (pxcor >= tapx + 3) and (pxcor <= tapx + 4) and (pycor = tapy - 5)
[ set pcolor blue ]
]
ask patches with [pcolor = blue]
[
if not patches-hit-surface? pxcor pycor ; check hit surface to right and left
[
if not patches-draw-drop-1? pxcor (pycor - 1) ; check that down is free to move to
[ set drawn false
set r random 2
ifelse (r = 0)
[ set drawn patches-draw-drop? (pxcor - 1) pycor ]; check left is free
[ set drawn patches-draw-drop? (pxcor + 1) pycor ]; check right is free
if not drawn
[ ifelse (r = 0)
[ set drawn patches-draw-drop? (pxcor + 1) pycor ]; check right is free
[ set drawn patches-draw-drop? (pxcor - 1) pycor ]; check left is free
]
]
]
]
end
to go-using-patch-agents-4
; Solution 4 using patch agents.
; This also fails completely (run it to see why).
let tapx (19 - max-pxcor)
let tapy (max-pycor - 10)
let drawn false
let r 0
ask patches
[ ; draw "water" at bottom of tap
if (pxcor >= tapx + 3) and (pxcor <= tapx + 4) and (pycor = tapy - 5)
[ set pcolor blue ]
]
ask patches with [pcolor = blue]
[
if not patches-hit-surface-1? pxcor pycor ; check hit surface to right and left
[
if not patches-draw-drop-1? pxcor (pycor - 1) ; check that down is free to move to
[ set drawn false
set r random 2
ifelse (r = 0)
[ set drawn patches-draw-drop? (pxcor - 1) pycor ]; check left is free
[ set drawn patches-draw-drop? (pxcor + 1) pycor ]; check right is free
if not drawn
[ ifelse (r = 0)
[ set drawn patches-draw-drop? (pxcor + 1) pycor ]; check right is free
[ set drawn patches-draw-drop? (pxcor - 1) pycor ]; check left is free
]
]
]
]
end
to go-using-patch-agents-5
; Insert your own solution here.
user-message "Not implemented yet!"
end
to go-using-boid-agents-1
; Solution 1 using boid agents that use a cone of vision to sense the world (i.e. like Craig Reynold's boids).
create-new-drops
ask drops
[
rt random-float rate-of-random-turn
lt (rate-of-random-turn / 2)
fd random boid-speed
if count patches in-cone radius-length radius-angle with
[pcolor = black or pcolor = colour-of-earth] > 0 ; checks patches in its vision
[ bk boid-bounce / size-of-drop ] ; bounces back if hit tank wall or earth
]
end
to go-using-boid-agents-2
; Insert your own solution here.
user-message "Not implemented yet!"
end
to go-using-particle-agents-1
; Solution 1 using particle agents.
let tapx (22 - max-pxcor)
let tapy (max-pycor - 15)
create-new-particles tapx tapy
compute-forces ; calculate the forces and add them to the accumulator
apply-forces ; calculate the new location and speed by multiplying the
; forces by the step-size
tick-advance step-size
display
end
to go-using-particle-agents-2
; Insert your own solution here.
user-message "Not implemented yet!"
end
to create-new-particles [init-x init-y]
;; using a Poisson distribution keeps the rate of particle emission
;; the same regardless of the step size
let n random-poisson (rate * step-size)
if n + count turtles > max-particles
[ set n max-particles - count turtles ]
ask patch init-x init-y
[
sprout-particles n
[
set color blue
set shape shape-of-drop
set size 1 + random-float 1
set mass 5 * size ^ 2 ; mass proportional to square of size
setxy init-x init-y
set velocity-x initial-velocity-x
- random-float initial-range-x
+ random-float initial-range-x
set velocity-y initial-velocity-y
]
]
end
to compute-forces
ask particles
[
; clear the force-accumulator
set force-accumulator-x 0
set force-accumulator-y 0
; calculate the forces
apply-gravity
apply-wind
apply-viscosity
]
end
to apply-gravity ;; turtle procedure
; scale the force of the gravity according to the particle's mass; this
; simulates the effect of air resistance
set force-accumulator-y force-accumulator-y - gravity-constant * mass
end
to apply-wind ;; turtle procedure
set force-accumulator-x force-accumulator-x + wind-constant-x
set force-accumulator-y force-accumulator-y + wind-constant-y
end
to apply-viscosity ;; turtle procedure
set force-accumulator-x force-accumulator-x - viscosity-constant * velocity-x
set force-accumulator-y force-accumulator-y - viscosity-constant * velocity-y
end
; calculates the position of the particle at each step but bounces if the particle
; reaches the edge
to apply-forces
let this-patch nobody
let these-colours lput colour-of-earth [black]
ask particles
[
; calculate the new velocity of the particle
set velocity-x velocity-x + ( force-accumulator-x * step-size)
set velocity-y velocity-y + ( force-accumulator-y * step-size)
; calculate the displacement of the particle
let step-x velocity-x * step-size
let step-y velocity-y * step-size
let new-x xcor + step-x
let new-y ycor + step-y
set this-patch patch-at step-x step-y
if (this-patch = nobody) or (member? ([pcolor] of this-patch) these-colours)
[
; if the particle touches a wall or the ceiling, it dies
if new-x > max-pxcor or new-x < min-pxcor or new-y > max-pycor
[ die ]
; if the particle touches the tank floor, bounce with floor-damping
set velocity-y (- velocity-y * restitution-coefficient)
set step-y velocity-y * step-size
set new-y ycor + step-y
]
facexy new-x new-y
setxy new-x new-y
]
end
;
; Copyright 2010 by William John Teahan. All rights reserved.
;
; Permission to use, modify or redistribute this model is hereby granted,
; provided that both of the following requirements are followed:
; a) this copyright notice is included.
; b) this model will not be redistributed for profit without permission
; from William John Teahan.
; Contact William John Teahan for appropriate licenses for redistribution for
; profit.
;
; To refer to this model in publications, please use:
;
; Teahan, W. J. (2010). Water Flowing Uphill NetLogo model.
; Artificial Intelligence. Ventus Publishing Aps.
;
; Copyright for Particle Agents code:
;
; Copyright 2007 Uri Wilensky. All rights reserved.
;
; Permission to use, modify or redistribute this model is hereby granted,
; provided that both of the following requirements are followed:
; a) this copyright notice is included.
; b) this model will not be redistributed for profit without permission
; from Uri Wilensky. Contact Uri Wilensky for appropriate licenses for redistribution for profit.
;
;
