Stableish- Prey-Predator Dynamics

This commit is contained in:
Sarah Jamie Lewis 2019-09-28 14:56:23 -07:00
parent fe96374bee
commit 64431203bf
5 changed files with 229 additions and 16 deletions

View File

@ -4,7 +4,7 @@ type Environment struct {
width, height int
state map[string][][]float32
value [][]bool
col [][]bool
col [][]*Turtle
}
func (e *Environment) Width() int {
@ -29,9 +29,9 @@ func NewEnvironment(width int, height int) *Environment {
env.height = height
env.state = make(map[string][][]float32)
env.col = make([][]bool, width)
env.col = make([][]*Turtle, width)
for x := range env.col {
env.col[x] = make([]bool, height)
env.col[x] = make([]*Turtle, height)
}
env.value = make([][]bool, width)
@ -54,16 +54,20 @@ func (e *Environment) Mark(pheromone string, x, y int, amount float32) {
}
}
func (e Environment) Occupy(x, y int) {
e.col[x][y] = true
func (e Environment) Occupy(turtle *Turtle, x, y int) {
e.col[x][y] = turtle
}
func (e Environment) Check(x, y int) bool {
func (e Environment) Get(x, y int) *Turtle {
return e.col[x][y]
}
func (e Environment) Check(x, y int) bool {
return e.col[x][y] != nil
}
func (e Environment) Leave(x, y int) {
e.col[x][y] = false
e.col[x][y] = nil
}
func (e Environment) HasValue(x, y int) bool {

View File

@ -1,6 +1,7 @@
package core
import (
"image/color"
"math/rand"
)
@ -9,6 +10,8 @@ type Turtle struct {
heading int
actor Actor
width, height int
col color.RGBA
atts map[string]string
}
type NilActor struct {
@ -20,22 +23,36 @@ func (NilActor) Setup(*Environment, *Turtle) {
func (NilActor) Run(*Environment, *Turtle) {
}
func (t *Turtle) GetActor() Actor {
return t.actor
}
func NewTurtle(env *Environment, actor Actor) *Turtle {
for {
for i := 0; i < 5; i++ {
turtle := new(Turtle)
turtle.width = env.width
turtle.height = env.height
turtle.xpos = rand.Intn(env.width)
turtle.ypos = rand.Intn(env.height)
turtle.actor = actor
turtle.atts = make(map[string]string)
if env.Check(turtle.xpos, turtle.ypos) == false {
actor.Setup(env, turtle)
env.Occupy(turtle.xpos, turtle.ypos)
env.Occupy(turtle, turtle.xpos, turtle.ypos)
turtle.setRandomHeading()
return turtle
}
}
return nil
}
func (t *Turtle) GetColor() color.RGBA {
return t.col
}
func (t *Turtle) SetColor(col color.RGBA) {
t.col = col
}
func (t *Turtle) Pos() (int, int) {
@ -75,6 +92,14 @@ func (t *Turtle) Wiggle() {
t.heading = h
}
func (t *Turtle) GetAttribute(name string) string {
return t.atts[name]
}
func (t *Turtle) SetAttribute(name, val string) {
t.atts[name] = val
}
func (t *Turtle) TurnAround() {
t.heading = (t.heading + 4) % 8
}
@ -206,6 +231,22 @@ func (t *Turtle) FollowGradient(env *Environment, distance int, threshold float3
}
}
func (t *Turtle) Check(env *Environment) *Turtle {
dx := headings[t.heading][0]
dy := headings[t.heading][1]
xpos := (t.xpos + dx) % (env.width)
if xpos < 0 {
xpos = env.width - 1
}
ypos := (t.ypos + dy) % (env.height)
if ypos < 0 {
ypos = env.height - 1
}
return env.Get(xpos, ypos)
}
func (t *Turtle) Step(env *Environment) bool {
dx := headings[t.heading][0]
dy := headings[t.heading][1]
@ -228,7 +269,7 @@ func (t *Turtle) Step(env *Environment) bool {
t.ypos = oy
success = false
}
env.Occupy(t.xpos, t.ypos)
env.Occupy(t, t.xpos, t.ypos)
return success
}

View File

@ -25,6 +25,7 @@ type Experiment struct {
turtles []*core.Turtle
graphics *graphics.Graphics
initializedTurtles int
OnStep func(*core.Environment, []*core.Turtle, int)
}
func (e *Experiment) InitializeExperiment() {
@ -46,7 +47,7 @@ func (e *Experiment) InitializeExperiment() {
}
e.env = core.NewEnvironment(*width, *height)
e.turtles = make([]*core.Turtle, *numTurtles)
e.turtles = make([]*core.Turtle, 0)
e.graphics = graphics.NewGraphics(int32(*width), int32(*height), int32(*pxsize))
}
@ -65,10 +66,15 @@ func (e *Experiment) InitEnvironment(f func(environment *core.Environment)) {
}
func (e *Experiment) InitNTurtles(f func() core.Actor, num int) {
numSuccess := 0
for i := e.initializedTurtles; i < e.initializedTurtles+num; i++ {
e.turtles[i] = core.NewTurtle(e.env, f())
t := core.NewTurtle(e.env, f())
if t != nil {
e.turtles = append(e.turtles, t)
numSuccess++
}
}
e.initializedTurtles += num
e.initializedTurtles += numSuccess
}
func (e *Experiment) InitTurtles(f func() core.Actor) {
@ -81,8 +87,24 @@ func (e *Experiment) Run() {
running := true
wait.Add(1)
go func() {
step := 0
for running {
e.graphics.Render(e.env, e.turtles)
e.OnStep(e.env, e.turtles, step)
newTurtles := make([]*core.Turtle, 0)
deleted := 0
for _, t := range e.turtles {
if t.GetAttribute("status") != "dead" {
newTurtles = append(newTurtles, t)
} else {
e.env.Leave(t.Pos()) // Dead turtles occupy no space
deleted++
}
}
e.turtles = newTurtles
step++
}
wait.Done()
}()

View File

@ -0,0 +1,142 @@
package main
import (
"flag"
"fmt"
"git.openprivacy.ca/sarah/microworlds/core"
"git.openprivacy.ca/sarah/microworlds/experiments"
"image/color"
"math"
"math/rand"
)
var numPrey = flag.Int("numPrey", 600, "the number of prey")
var numPred = flag.Int("numPred", 60, "the number of predators")
type Predator struct {
Steps int
Energy int
}
func (sm *Predator) Setup(env *core.Environment, t *core.Turtle) {
// Do nothing
t.SetAttribute("type", "predator")
t.SetColor(color.RGBA{255, 200, 0, 0})
sm.Energy = 50
}
func (sm *Predator) Run(env *core.Environment, t *core.Turtle) {
sm.Steps++
if sm.Steps == 30 {
t.SetAttribute("status", "dead")
return
}
sm.Energy--
if sm.Energy == 0 {
t.SetAttribute("status", "dead")
return
}
t.Wiggle()
prey := t.Check(env)
if prey != nil {
if prey.GetAttribute("type") == "prey" && prey.GetAttribute("status") != "dead" {
prey.SetAttribute("status", "dead")
sm.Energy = int(math.Max(float64(sm.Energy)+10, 120))
}
}
t.FollowGradient(env, 1, 3, "scent")
t.Step(env)
}
type Prey struct {
Steps int
Energy int
}
func (sm *Prey) Setup(env *core.Environment, t *core.Turtle) {
// Do nothing
t.SetAttribute("type", "prey")
t.SetColor(color.RGBA{100, 0, 100, 0})
sm.Steps = 0
sm.Energy = 25
}
func (sm *Prey) Run(env *core.Environment, t *core.Turtle) {
sm.Steps++
sm.Energy--
if sm.Steps >= 20 || sm.Energy == 0 {
t.SetAttribute("status", "dead")
return
}
t.Wiggle()
t.Drop(env, 1, "scent")
t.Step(env)
if env.HasValue(t.Pos()) {
env.TakeValue(t.Pos())
sm.Energy += 1
}
}
func main() {
experiment := new(experiments.Experiment)
experiment.InitializeExperiment()
experiment.InitEnvironment(func(env *core.Environment) {
for i := 0; i < 600; i++ {
x := rand.Intn(env.Width())
y := rand.Intn(env.Height())
env.PutValue(x, y)
}
})
experiment.InitNTurtles(func() core.Actor {
sm := new(Predator)
return sm
}, *numPred)
experiment.InitNTurtles(func() core.Actor {
sm := new(Prey)
return sm
}, *numPrey)
experiment.InitPheromone("scent", color.RGBA{0x80, 0xFF, 0x00, 0x00})
experiment.OnStep = func(env *core.Environment, turtles []*core.Turtle, step int) {
alive := 0
predalive := 0
// Grow Grass
x := rand.Intn(env.Width())
y := rand.Intn(env.Height())
env.PutValue(x, y)
for _, turtle := range turtles {
if turtle.GetAttribute("type") == "prey" && turtle.GetAttribute("status") != "dead" {
alive++
prey := turtle.GetActor().(*Prey)
if prey.Steps > 10 && prey.Energy > 5 && rand.Intn(5) == 1 {
experiment.InitNTurtles(func() core.Actor {
sm := new(Prey)
return sm
}, 1)
alive++
}
}
if turtle.GetAttribute("type") == "predator" && turtle.GetAttribute("status") != "dead" {
predalive++
if turtle.GetAttribute("type") == "predator" {
pred := turtle.GetActor().(*Predator)
if pred.Energy >= 80 && rand.Intn(16) == 1 {
experiment.InitNTurtles(func() core.Actor {
sm := new(Predator)
return sm
}, 1)
predalive++
}
}
}
}
if step%10 == 0 {
fmt.Printf("Prey Alive: %v | Pred Alive: %v \n", alive, predalive)
}
}
experiment.Run()
}

View File

@ -7,7 +7,7 @@ import (
"math"
"os"
"strconv"
//"strconv"
// "strconv"
)
type Graphics struct {
@ -87,16 +87,20 @@ func (g *Graphics) Render(env *core.Environment, turtles []*core.Turtle) {
}
}
g.renderer.SetDrawColor(0xF3, 0x81, 0, 0x00)
for _, t := range turtles {
if t.GetAttribute("status") == "dead" {
continue
}
x, y := t.Pos()
col := t.GetColor()
g.renderer.SetDrawColor(col.R, col.G, col.B, col.A)
g.DrawTileColor(int32(x), int32(y))
t.Run(env)
}
// TODO: Move this into an environment specification
for name := range g.colorMap {
env.Evaporate(0.99, name)
env.Evaporate(0.95, name)
}
g.renderer.Present()