Compare commits

..

No commits in common. 'master' and 'master' have entirely different histories.

  1. 2
      .gitignore
  2. 35
      actors/ant.go
  3. 20
      actors/slimemold.go
  4. 38
      actors/woodchips.go
  5. 96
      core/environment.go
  6. 291
      core/turtle.go
  7. 376
      experiments/evolution/soccer.go
  8. 160
      experiments/experiment.go
  9. 107
      experiments/flocking/flocking.go
  10. 69
      experiments/fractal/fractal.go
  11. 96
      experiments/isolate/main.go
  12. 142
      experiments/lightning-bugs/lightning-bugs.go
  13. 43
      experiments/maze/main.go
  14. 5
      experiments/predatorprey/README.md
  15. BIN
      experiments/predatorprey/oscillations.png
  16. 207
      experiments/predatorprey/predprey.go
  17. 53
      experiments/slimemold/main.go
  18. 58
      experiments/slimemold/plasmoidiam/main.go
  19. 63
      experiments/slush/main.go
  20. 198
      experiments/snowball/main.go
  21. 368
      experiments/swarm/main.go
  22. 90
      experiments/turtles-and-frogs/main.go
  23. 73
      experiments/woodchips/main.go
  24. 20
      go.mod
  25. 34
      go.sum
  26. 115
      graphics/graphics.go
  27. 84
      graphics/plotting.go
  28. 120
      main.go
  29. 85
      models/neoplasmodium.go
  30. 82
      models/plasmodium.go

2
.gitignore vendored

@ -1,10 +1,8 @@
/vendor/
# ---> Go
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*csv
# Folders
_obj

@ -0,0 +1,35 @@
package actors
import (
"git.openprivacy.ca/sarah/microworlds/core"
)
type Ant struct {
SniffDistance int
Carrying bool
DropSize float32
}
func (a *Ant) Setup(env *core.Environment, t *core.Turtle) {
//t.SetXY(150,150)
}
func (a *Ant) Run(env *core.Environment, t *core.Turtle) {
if a.Carrying == false {
if env.HasValue(t.Pos()) {
env.TakeValue(t.Pos())
a.Carrying = true
a.DropSize = 100
t.TurnAround()
} else {
t.Wiggle()
t.FollowGradient(env, a.SniffDistance, 5, "food")
}
t.Step(env)
} else if a.Carrying == true {
a.DropSize -= 0.6
t.Drop(env, a.DropSize, "food")
t.Wiggle()
t.Step(env)
}
}

@ -0,0 +1,20 @@
package actors
import (
"git.openprivacy.ca/sarah/microworlds/core"
)
type SlimeMold struct {
SniffDistance int
}
func (sm *SlimeMold) Setup(env *core.Environment, t *core.Turtle) {
// Do nothing
}
func (sm *SlimeMold) Run(env *core.Environment, t *core.Turtle) {
t.Wiggle()
t.FollowGradient(env, sm.SniffDistance, 2, "trail")
t.Step(env)
t.Drop(env, 1, "trail")
}

@ -0,0 +1,38 @@
package actors
import (
"git.openprivacy.ca/sarah/microworlds/core"
)
type WoodChips struct {
SniffDistance int
Carrying bool
}
func (a *WoodChips) Setup(env *core.Environment, t *core.Turtle) {
//t.SetXY(150,150)
}
func (a *WoodChips) Run(env *core.Environment, t *core.Turtle) {
if a.Carrying {
if env.HasValue(t.Pos()) {
for {
t.Wiggle()
t.Step(env)
if !env.HasValue(t.Pos()) {
env.PutValue(t.Pos())
a.Carrying = false
break
}
}
}
} else {
if env.HasValue(t.Pos()) {
env.TakeValue(t.Pos())
a.Carrying = true
t.TurnAround()
}
}
t.Wiggle()
t.Step(env)
}

@ -1,61 +1,20 @@
package core
import (
"github.com/foolusion/quadtree"
)
type Environment struct {
width, height int
state map[string][][]float32
value [][]bool
col [][]*Turtle
Step int
quadtree *quadtree.QuadTree
col [][]bool
}
func (e *Environment) Width() int {
return e.width
}
func (e *Environment) GetNearestNeighbours(bounding *quadtree.AABB, num int) (turtles []*Turtle) {
points := e.quadtree.SearchArea(bounding)
// fmt.Printf("Found X neighbours %v\n",len(points))
for _, point := range points {
if len(turtles) < num+1 {
x, y := point.X, point.Y
if e.Get(int(x), int(y)) != nil { // WHY DOES THIS HAPPEN?!?!
turtles = append(turtles, e.Get(int(x), int(y)))
}
}
}
return
}
func (e *Environment) InsertIntoQuadTree(turtle *Turtle) {
e.quadtree.Insert(quadtree.NewXY(float64(turtle.xpos), float64(turtle.ypos)))
}
func (e *Environment) ResetQuadtree() {
center := quadtree.NewXY(150, 150)
area := quadtree.NewAABB(*center, *center)
e.quadtree = quadtree.New(*area, 10)
}
func (e *Environment) Height() int {
return e.height
}
func (e *Environment) GetPheromones() []string {
keys := make([]string, len(e.state))
i := 0
for k := range e.state {
keys[i] = k
i++
}
return keys
}
func (e *Environment) InitPheromone(name string) {
state := make([][]float32, e.width)
for x := range state {
@ -70,9 +29,9 @@ func NewEnvironment(width int, height int) *Environment {
env.height = height
env.state = make(map[string][][]float32)
env.col = make([][]*Turtle, width)
env.col = make([][]bool, width)
for x := range env.col {
env.col[x] = make([]*Turtle, height)
env.col[x] = make([]bool, height)
}
env.value = make([][]bool, width)
@ -95,21 +54,16 @@ func (e *Environment) Mark(pheromone string, x, y int, amount float32) {
}
}
func (e Environment) Occupy(turtle *Turtle, x, y int) {
e.col[x][y] = turtle
}
func (e Environment) Get(x, y int) *Turtle {
return e.col[x][y]
func (e Environment) Occupy(x, y int) {
e.col[x][y] = true
}
func (e Environment) Check(x, y int) bool {
x, y = e.normXY(x, y)
return e.col[x][y] != nil
return e.col[x][y]
}
func (e Environment) Leave(x, y int) {
e.col[x][y] = nil
e.col[x][y] = false
}
func (e Environment) HasValue(x, y int) bool {
@ -145,48 +99,22 @@ func (e *Environment) normXY(x int, y int) (int, int) {
}
func (e *Environment) Evaporate(rate float32, pheromone string) {
for x := 0; x < e.width; x++ {
for y := 0; y < e.height; y++ {
e.state[pheromone][x][y] = e.state[pheromone][x][y] * rate
}
}
}
func (e *Environment) EvaporateAndDiffuse(rate float32, pheromone string) {
pheromoneprev := pheromone + "prev"
//log.Debugf("Evap")
e.state[pheromoneprev] = make([][]float32, e.width)
for x := range e.state[pheromoneprev] {
e.state[pheromoneprev][x] = make([]float32, e.height)
for y := 0; y < e.height; y++ {
e.state[pheromoneprev][x][y] = e.state[pheromone][x][y] * rate
}
}
pheromoneMap := e.state[pheromoneprev]
for x := 0; x < e.width; x++ {
for y := 0; y < e.height; y++ {
amount := e.state[pheromoneprev][x][y]
totalAmount := amount + e.sniffNormalized(x-1, y-1, pheromoneMap) + e.sniffNormalized(x, y-1, pheromoneMap) + e.sniffNormalized(x+1, y-1, pheromoneMap) + e.sniffNormalized(x-1, y, pheromoneMap) + e.sniffNormalized(x+1, y, pheromoneMap)
totalAmount += e.sniffNormalized(x-1, y+1, pheromoneMap) + e.sniffNormalized(x, y+1, pheromoneMap) + e.sniffNormalized(x+1, y+1, pheromoneMap)
e.state[pheromone][x][y] = totalAmount / 9
}
_, exists := e.state[pheromone]
if !exists {
e.InitPheromone(pheromone)
}
}
func (e *Environment) Diffuse(pheromone string) {
pheromoneprev := pheromone + "prev"
e.state[pheromoneprev] = make([][]float32, e.width)
for x := range e.state[pheromoneprev] {
e.state[pheromoneprev][x] = make([]float32, e.height)
}
for x := 0; x < e.width; x++ {
for y := 0; y < e.height; y++ {
e.state[pheromoneprev][x][y] = e.state[pheromone][x][y]
e.state[pheromoneprev][x][y] = e.state[pheromone][x][y] * rate
}
}

@ -1,18 +1,13 @@
package core
import (
"image/color"
"math"
"math/rand"
)
type Turtle struct {
xpos, ypos int
heading int
actor Actor
width, height int
col color.RGBA
atts map[string]string
xpos, ypos int
heading int
actor Actor
}
type NilActor struct {
@ -24,40 +19,20 @@ func (NilActor) Setup(*Environment, *Turtle) {
func (NilActor) Run(*Environment, *Turtle) {
}
func (t *Turtle) GetActor() Actor {
return t.actor
}
func (t *Turtle) Heading() int {
return t.heading
}
func NewTurtle(env *Environment, actor Actor) *Turtle {
for i := 0; i < 10; i++ {
for {
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 && env.HasValue(turtle.xpos, turtle.ypos) == false {
turtle.setRandomHeading()
if env.Check(turtle.xpos, turtle.ypos) == false {
actor.Setup(env, turtle)
env.Occupy(turtle, turtle.xpos, turtle.ypos)
env.Occupy(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) {
@ -71,19 +46,6 @@ func (t *Turtle) setRandomHeading() {
}
func (t *Turtle) SetXY(x, y int) {
if x < 0 {
x = (t.width - 1)
} else if x >= t.width {
x = x % (t.width)
}
if y < 0 {
y = (t.height - 1)
} else if y >= t.height {
y = y % (t.height)
}
t.xpos = x
t.ypos = y
}
@ -97,18 +59,6 @@ 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) SetHeading(heading int) {
t.heading = heading
}
func (t *Turtle) TurnAround() {
t.heading = (t.heading + 4) % 8
}
@ -117,98 +67,6 @@ func (t *Turtle) Drop(env *Environment, amount float32, pheromone string) {
env.Mark(pheromone, t.xpos, t.ypos, amount)
}
func (t *Turtle) AmountAll(env *Environment, distance int, pheromone string) float32 {
total := float32(0)
for i := 0; i < 8; i++ {
dx0 := headings[i][0] * distance
dy0 := headings[i][1] * distance
x0 := (t.xpos + dx0)
y0 := (t.ypos + dy0)
total += env.SniffNormalized(x0, y0, pheromone)
}
return total
}
func (t *Turtle) Amount(env *Environment, distance int, pheromone string) float32 {
h0 := t.heading - 1
if h0 < 0 {
h0 = 7
}
dx0 := headings[h0][0] * distance
dy0 := headings[h0][1] * distance
x0 := (t.xpos + dx0)
y0 := (t.ypos + dy0)
dx := headings[t.heading][0] * distance
dy := headings[t.heading][1] * distance
x := (t.xpos + dx)
y := (t.ypos + dy)
h1 := (t.heading + 1) % 8
dx1 := headings[h1][0] * distance
dy1 := headings[h1][1] * distance
x1 := (t.xpos + dx1)
y1 := (t.ypos + dy1)
as0 := env.SniffNormalized(x0, y0, pheromone)
as := env.SniffNormalized(x, y, pheromone)
as1 := env.SniffNormalized(x1, y1, pheromone)
return as0 + as + as1
}
func (t *Turtle) Near(env *Environment, distance int, threshold float32, pheromone string) bool {
h0 := t.heading - 1
if h0 < 0 {
h0 = 7
}
dx0 := headings[h0][0] * distance
dy0 := headings[h0][1] * distance
x0 := (t.xpos + dx0)
y0 := (t.ypos + dy0)
dx := headings[t.heading][0] * distance
dy := headings[t.heading][1] * distance
x := (t.xpos + dx)
y := (t.ypos + dy)
h1 := (t.heading + 1) % 8
dx1 := headings[h1][0] * distance
dy1 := headings[h1][1] * distance
x1 := (t.xpos + dx1)
y1 := (t.ypos + dy1)
as0 := env.SniffNormalized(x0, y0, pheromone)
if as0 < threshold {
as0 = 0
}
as := env.SniffNormalized(x, y, pheromone)
if as < threshold {
as = 0
}
as1 := env.SniffNormalized(x1, y1, pheromone)
if as1 < threshold {
as1 = 0
}
if as0 == 0 && as == 0 && as1 == 0 {
return false
} else {
return true
}
}
func (t *Turtle) FollowGradient(env *Environment, distance int, threshold float32, pheromone string) {
h0 := t.heading - 1
@ -255,139 +113,6 @@ func (t *Turtle) FollowGradient(env *Environment, distance int, threshold float3
}
}
func (t *Turtle) RejectGradient(env *Environment, distance int, pheromone string) {
h0 := t.heading - 1
if h0 < 0 {
h0 = 7
}
dx0 := headings[h0][0] * distance
dy0 := headings[h0][1] * distance
x0 := (t.xpos + dx0)
y0 := (t.ypos + dy0)
dx := headings[t.heading][0] * distance
dy := headings[t.heading][1] * distance
x := (t.xpos + dx)
y := (t.ypos + dy)
h1 := (t.heading + 1) % 8
dx1 := headings[h1][0] * distance
dy1 := headings[h1][1] * distance
x1 := (t.xpos + dx1)
y1 := (t.ypos + dy1)
as0 := env.SniffNormalized(x0, y0, pheromone)
as := env.SniffNormalized(x, y, pheromone)
as1 := env.SniffNormalized(x1, y1, pheromone)
if as0 > as && as0 > as1 {
t.heading = h0
} else if as1 > as && as1 > as0 {
t.heading = h1
}
}
func (t *Turtle) AvoidAverageGradient(env *Environment, distance int, threshold float32, pheromone string) {
h0 := t.heading - 1
if h0 < 0 {
h0 = 7
}
dx0 := headings[h0][0] * distance
dy0 := headings[h0][1] * distance
x0 := (t.xpos + dx0)
y0 := (t.ypos + dy0)
dx := headings[t.heading][0] * distance
dy := headings[t.heading][1] * distance
x := (t.xpos + dx)
y := (t.ypos + dy)
h1 := (t.heading + 1) % 8
dx1 := headings[h1][0] * distance
dy1 := headings[h1][1] * distance
x1 := (t.xpos + dx1)
y1 := (t.ypos + dy1)
as0 := env.SniffNormalized(x0, y0, pheromone)
as := env.SniffNormalized(x, y, pheromone)
as1 := env.SniffNormalized(x1, y1, pheromone)
avg := float64((1 * as0) + (2 * as) + (3*as1)/(as0+as+as1))
heading := math.Round(avg)
if heading < 1 && as0 > threshold {
t.heading = h1
} else if heading > 2 && as1 > threshold {
t.heading = h0
}
}
func (t *Turtle) FollowAverageGradient(env *Environment, distance int, threshold float32, pheromone string) {
h0 := t.heading - 1
if h0 < 0 {
h0 = 7
}
dx0 := headings[h0][0] * distance
dy0 := headings[h0][1] * distance
x0 := (t.xpos + dx0)
y0 := (t.ypos + dy0)
dx := headings[t.heading][0] * distance
dy := headings[t.heading][1] * distance
x := (t.xpos + dx)
y := (t.ypos + dy)
h1 := (t.heading + 1) % 8
dx1 := headings[h1][0] * distance
dy1 := headings[h1][1] * distance
x1 := (t.xpos + dx1)
y1 := (t.ypos + dy1)
as0 := env.SniffNormalized(x0, y0, pheromone)
as := env.SniffNormalized(x, y, pheromone)
as1 := env.SniffNormalized(x1, y1, pheromone)
avg := float64((1 * as0) + (2 * as) + (3*as1)/(as0+as+as1))
heading := math.Round(avg)
if heading < 1 && as0 > threshold {
t.heading = h0
} else if heading > 2 && as1 > threshold {
t.heading = h1
}
}
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]
@ -410,7 +135,7 @@ func (t *Turtle) Step(env *Environment) bool {
t.ypos = oy
success = false
}
env.Occupy(t, t.xpos, t.ypos)
env.Occupy(t.xpos, t.ypos)
return success
}

@ -1,376 +0,0 @@
package main
import (
"fmt"
"git.openprivacy.ca/sarah/microworlds/core"
"git.openprivacy.ca/sarah/microworlds/experiments"
"github.com/faiface/pixel/pixelgl"
"github.com/wcharczuk/go-chart"
"image/color"
"math/rand"
"os"
"os/signal"
"runtime/pprof"
"strconv"
)
type Player struct {
dna [40]int
Team int
}
func (player *Player) Setup(env *core.Environment, t *core.Turtle) {
for i := 0; i < len(player.dna); i++ {
player.dna[i] = rand.Intn(NumGenes)
}
t.SetAttribute("team", strconv.Itoa(player.Team))
if player.Team == 1 {
t.SetColor(color.RGBA{0xFF, 0x00, 0x00, 0xFF})
} else {
t.SetColor(color.RGBA{0x00, 0x00, 0xFF, 0xFF})
}
}
const (
Wiggle = iota
FollowMyTeam
FollowOtherTeam
FollowBall
Drop
RetreatMyTeam
RetreatOtherTeam
RetreatBall
TurnAround
HeadTowardsOurGoal
HeadTowardsOtherGoal
)
var NumGenes = 11
func (player *Player) Run(env *core.Environment, t *core.Turtle) {
MyTeam := strconv.Itoa(player.Team)
OtherTeam := "1"
if player.Team == 1 {
OtherTeam = "2"
}
ahead := t.Check(env)
if ahead != nil {
ball, ok := ahead.GetActor().(*Ball)
if ok {
ahead.SetHeading(t.Heading())
if rand.Intn(5) == 0 {
ahead.Wiggle()
}
ahead.Step(env)
ball.Check(env, ahead)
}
}
t.Step(env)
for i := 0; i < len(player.dna); i++ {
switch player.dna[i] {
case TurnAround:
t.TurnAround()
case Wiggle:
t.Wiggle()
case FollowMyTeam:
if i+2 < len(player.dna) {
t.FollowGradient(env, player.dna[i+1], float32(player.dna[i+2]), MyTeam)
i += 2
}
case FollowOtherTeam:
if i+2 < len(player.dna) {
t.FollowGradient(env, player.dna[i+1], float32(player.dna[i+2]), OtherTeam)
i += 2
}
case FollowBall:
t.FollowGradient(env, 1, 0, "ball")
case Drop:
if i+1 < len(player.dna) {
t.Drop(env, float32(player.dna[i+1]), strconv.Itoa(player.Team))
}
case RetreatMyTeam:
if i+2 < len(player.dna) {
t.AvoidAverageGradient(env, player.dna[i+1], float32(player.dna[i+2]), MyTeam)
i += 2
}
case RetreatOtherTeam:
if i+2 < len(player.dna) {
t.AvoidAverageGradient(env, player.dna[i+1], float32(player.dna[i+2]), OtherTeam)
i += 2
}
case RetreatBall:
if i+2 < len(player.dna) {
t.AvoidAverageGradient(env, player.dna[i+1], float32(player.dna[i+2]), "ball")
i += 2
}
case HeadTowardsOurGoal:
x, _ := t.Pos()
if player.Team == 1 {
if x > 1 {
t.SetHeading(7)
}
} else {
if x < 299 {
t.SetHeading(3)
}
}
case HeadTowardsOtherGoal:
x, _ := t.Pos()
if player.Team == 2 {
if x > 1 {
t.SetHeading(7)
}
} else {
if x < 299 {
t.SetHeading(3)
}
}
}
}
}
func (player *Player) Mutate() {
mutatePoint := rand.Intn(len(player.dna))
fmt.Printf("\t\t Mutating %v at %v\n", player.Team, mutatePoint)
player.dna[mutatePoint] = rand.Intn(NumGenes)
}
func (player *Player) Clone(parentA *Player, parentB *Player) {
fmt.Printf("\t\tCrossing Over team %v with:%v %v\n", player.Team, parentA.dna, parentB.dna)
crossoverPoint := rand.Intn(len(player.dna))
copy(player.dna[0:crossoverPoint], parentA.dna[0:crossoverPoint])
copy(player.dna[crossoverPoint:], parentB.dna[crossoverPoint:])
}
type Ball struct {
Team1Score int
Team2Score int
}
func (b *Ball) Setup(env *core.Environment, t *core.Turtle) {
t.SetXY(50, 50)
t.SetColor(color.RGBA{0xFF, 0xFF, 0xFF, 0xFF})
}
func (b *Ball) Run(env *core.Environment, t *core.Turtle) {
b.Check(env, t)
t.Drop(env, 10, "ball")
}
func (b *Ball) Check(env *core.Environment, t *core.Turtle) {
x, _ := t.Pos()
if x > 98 {
fmt.Printf("\t\tTeam 1 Scored!!! %v\n", env.Step)
b.Reset(env, t)
b.Team1Score++
} else if x < 1 {
fmt.Printf("\t\tTeam 2 Scored!!! %v\n", env.Step)
b.Reset(env, t)
b.Team2Score++
}
}
func (b *Ball) Reset(env *core.Environment, t *core.Turtle) {
x, y := t.Pos()
t.SetHeading(rand.Intn(8))
env.Leave(x, y)
t.SetXY(50, 50)
env.Occupy(t, 50, 50)
}
func mainrun() {
ball := new(Ball)
experiment := new(experiments.Experiment)
experiment.InitializeExperiment()
experiment.InitNTurtles(func() core.Actor {
player := new(Player)
player.Team = 1
return player
}, 25)
experiment.InitNTurtles(func() core.Actor {
player := new(Player)
player.Team = 2
return player
}, 25)
experiment.InitNTurtles(func() core.Actor {
return ball
}, 1)
experiment.InitPheromone("1", color.RGBA{0xFF, 0x00, 0x00, 0x00})
experiment.InitPheromone("2", color.RGBA{0x00, 0x00, 0xFF, 0x00})
experiment.InitPheromone("ball", color.RGBA{0xff, 0xff, 0xff, 0xff})
x := []float64{-2}
team1 := []float64{0}
team2 := []float64{1}
var graph chart.Chart
experiment.AddPlot("Goals Scored", func(environment *core.Environment, turtles []*core.Turtle) *chart.Chart {
x = append(x, float64(environment.Step))
team1 = append(team1, float64(ball.Team1Score))
team2 = append(team2, float64(ball.Team2Score))
graph = chart.Chart{
Background: chart.Style{
Padding: chart.Box{
Top: 50,
},
},
XAxis: chart.XAxis{Name: "Time Step", NameStyle: chart.Style{Show: true}, Style: chart.Style{Show: true, TextRotationDegrees: 90}, ValueFormatter: func(v interface{}) string {
return fmt.Sprintf("%d", int(v.(float64)))
}},
YAxis: chart.YAxis{Name: "Goals Scored", NameStyle: chart.Style{Show: true}, Style: chart.Style{Show: true}, ValueFormatter: func(v interface{}) string {
return fmt.Sprintf("%d", int(v.(float64)))
}},
Series: []chart.Series{
chart.ContinuousSeries{
XValues: x,
YValues: team1,
},
chart.ContinuousSeries{
XValues: x,
YValues: team2,
},
},
}
return &graph
})
gamelengths := []int{}
gamewins := []int{}
gamescores := []int{}
lastGameStart := 0
experiment.OnStep = func(environment *core.Environment, turtles []*core.Turtle, i int) {
environment.EvaporateAndDiffuse(0.95, "ball")
environment.EvaporateAndDiffuse(0.95, "1")
environment.EvaporateAndDiffuse(0.95, "2")
if environment.Step-lastGameStart > 4000 {
fmt.Printf("Time!\n")
if ball.Team1Score == ball.Team2Score {
fmt.Printf("Draw!\n")
gamelengths = append(gamelengths, environment.Step-lastGameStart)
gamewins = append(gamewins, 0)
gamescores = append(gamescores, ball.Team1Score, ball.Team2Score)
lastGameStart = environment.Step
ball.Team2Score = 0
ball.Team1Score = 0
red, blue := GetTeams(turtles)
for _, r := range red {
player := r.GetActor().(*Player)
player.Mutate()
player.Mutate()
player.Mutate()
player.Mutate()
player.Mutate()
}
for _, b := range blue {
player := b.GetActor().(*Player)
player.Mutate()
player.Mutate()
player.Mutate()
player.Mutate()
player.Mutate()
}
for i, lengths := range gamelengths {
fmt.Printf("Generation: %v, %v time steps Team %v Won (%v - %v)\n", i, lengths, gamewins[i], gamescores[i*2], gamescores[(i*2)+1])
}
} else {
ball.Team1Score++
ball.Team2Score++
}
}
if ball.Team1Score >= 3 && ball.Team2Score < 3 {
fmt.Printf("Team 1 Win")
red, blue := GetTeams(turtles)
for _, b := range blue {
player := b.GetActor().(*Player)
parentA := rand.Intn(len(red))
parentB := rand.Intn(len(red))
player.Clone(red[parentA].GetActor().(*Player), red[parentB].GetActor().(*Player))
player.Mutate()
}
adjust := 0
if environment.Step-lastGameStart > 4000 {
adjust = (environment.Step - lastGameStart) - 4000
}
gamelengths = append(gamelengths, environment.Step-lastGameStart)
gamewins = append(gamewins, 1)
gamescores = append(gamescores, ball.Team1Score-adjust, ball.Team2Score-adjust)
lastGameStart = environment.Step
for i, lengths := range gamelengths {
fmt.Printf("Generation: %v, %v time steps Team %v Won (%v - %v)\n", i, lengths, gamewins[i], gamescores[i*2], gamescores[(i*2)+1])
}
ball.Team2Score = 0
ball.Team1Score = 0
} else if ball.Team2Score >= 3 && ball.Team1Score < 3 {
fmt.Printf("Team 2 Win")
red, blue := GetTeams(turtles)
for _, r := range red {
player := r.GetActor().(*Player)
parentA := rand.Intn(len(blue))
parentB := rand.Intn(len(blue))
player.Clone(blue[parentA].GetActor().(*Player), blue[parentB].GetActor().(*Player))
player.Mutate()
}
adjust := 0
if environment.Step-lastGameStart > 4000 {
adjust = (environment.Step - lastGameStart) - 4000
}
gamelengths = append(gamelengths, environment.Step-lastGameStart)
gamewins = append(gamewins, 2)
gamescores = append(gamescores, ball.Team1Score-adjust, ball.Team2Score-adjust)
lastGameStart = environment.Step
ball.Team2Score = 0
ball.Team1Score = 0
for i, lengths := range gamelengths {
fmt.Printf("Generation: %v, %v time steps Team %v Won (%v - %v)\n", i, lengths, gamewins[i], gamescores[i*2], gamescores[(i*2)+1])
}
}
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for sig := range c {
fmt.Printf("Got Signal %v", sig)
pprof.StopCPUProfile()
os.Exit(0)
}
}()
experiment.Run()
}
func main() {
pixelgl.Run(mainrun)
}
func GetTeams(turtles []*core.Turtle) (redTeam, blueTeam []*core.Turtle) {
for _, turtle := range turtles {
if turtle.GetAttribute("team") == "1" {
redTeam = append(redTeam, turtle)
} else if turtle.GetAttribute("team") == "2" {
blueTeam = append(blueTeam, turtle)
}
}
return
}

@ -1,160 +0,0 @@
package experiments
import (
"flag"
"git.openprivacy.ca/sarah/microworlds/core"
"git.openprivacy.ca/sarah/microworlds/graphics"
"github.com/veandco/go-sdl2/sdl"
"github.com/wcharczuk/go-chart"
"image/color"
"log"
"math/rand"
"os"
"runtime/pprof"
"sync"
"time"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var width = flag.Int("width", 300, "width of environment")
var height = flag.Int("height", 300, "height of environment")
var pxsize = flag.Int("pxsize", 1, "pixels per tile edge")
var numTurtles = flag.Int("numTurtles", 5000, "number of turtles")
type Experiment struct {
env *core.Environment
turtles []*core.Turtle
graphics *graphics.Graphics
initializedTurtles int
pheromones []string
OnStep func(*core.Environment, []*core.Turtle, int)
plots []*graphics.Plot
running bool
}
func (e *Experiment) InitializeExperiment() {
// We don't need real randomness
rand.Seed(time.Now().Unix())
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
}
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
panic(err)
}
e.OnStep = func(env *core.Environment, turtles []*core.Turtle, i int) {
for _, name := range e.pheromones {
env.EvaporateAndDiffuse(0.95, name)
}
}
e.env = core.NewEnvironment(*width, *height)
e.turtles = make([]*core.Turtle, 0)
e.graphics = graphics.NewGraphics(int32(*width), int32(*height), int32(*pxsize))
}
func (e *Experiment) GetNumTurtles() int {
return (*numTurtles)
}
func (e *Experiment) InitPheromone(name string, col color.Color) {
e.env.InitPheromone(name)
r, g, b, a := col.RGBA()
e.pheromones = append(e.pheromones, name)
e.graphics.ColorPheromone(name, [4]uint8{uint8(r), uint8(g), uint8(b), uint8(a)})
}
func (e *Experiment) InitEnvironment(f func(environment *core.Environment)) {
f(e.env)
}
func (e *Experiment) InitNTurtles(f func() core.Actor, num int) {
numSuccess := 0
for i := e.initializedTurtles; i < e.initializedTurtles+num; i++ {
t := core.NewTurtle(e.env, f())
if t != nil {
e.turtles = append(e.turtles, t)
numSuccess++
}
}
e.initializedTurtles += numSuccess
}
func (e *Experiment) InitTurtles(f func() core.Actor) {
e.InitNTurtles(f, (*numTurtles))
}
func (e *Experiment) AddPlot(title string, plotfunc func(environment *core.Environment, turtles []*core.Turtle) *chart.Chart) {
plot := graphics.NewPlot(title, int32(*width), int32(*height), int32(*pxsize))
plot.GeneratePlot = plotfunc
go plot.RenderAsync()
e.plots = append(e.plots, plot)
}
func (e *Experiment) Stop() {
e.running = false
}
func (e *Experiment) Restart() {
e.running = false
}
func (e *Experiment) Run() {
wait := sync.WaitGroup{}
e.running = true
wait.Add(1)
e.env.ResetQuadtree()
go func() {
step := 0
for e.running {
e.env.Step = step
e.graphics.Render(e.env, e.turtles)
e.OnStep(e.env, e.turtles, step)
for _, plot := range e.plots {
plot.Render(e.env, e.turtles)
}
newTurtles := make([]*core.Turtle, 0)
for _, t := range e.turtles {
t.Run(e.env)
if t.GetAttribute("status") != "dead" {
newTurtles = append(newTurtles, t)
} else {
e.env.Leave(t.Pos()) // Dead turtles occupy no space
}
}
e.turtles = newTurtles
e.env.ResetQuadtree()
for _, t := range e.turtles {
e.env.InsertIntoQuadTree(t)
}
step++
}
wait.Done()
}()
wait.Add(1)
for e.running {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) {
case *sdl.QuitEvent:
e.running = false
break
}
}
}
wait.Done()
wait.Wait()
}

@ -1,107 +0,0 @@
package main
import (
"flag"
"git.openprivacy.ca/sarah/microworlds/core"
"git.openprivacy.ca/sarah/microworlds/experiments"
"github.com/faiface/pixel/pixelgl"
"github.com/foolusion/quadtree"
"image/color"
"math/rand"
"time"
)
var sniffDistance = flag.Int("sniffDistance", 3, "the distance a turtle can detect pheromone levels from")
var W = 1
var envmap = [][]int{
{W, W, W, W, W, W, W, W, W, W, W, W, W, W, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, W, 0, 0, W, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, W, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, W, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W},
{W, W, W, W, W, W, W, W, W, W, W, W, W, W, W},
}
type Bird struct {
}
func (sm *Bird) Setup(env *core.Environment, t *core.Turtle) {
t.SetColor(color.RGBA{100, 255, 10, 0})
t.SetHeading(rand.Intn(8 ))
}
var searchtree = quadtree.XY{4, 4}
func (sm *Bird) Run(env *core.Environment, t *core.Turtle) {
//t.Wiggle()
t.FollowAverageGradient(env, 50,0.1, "bird")
t.RejectGradient(env, 0, "bird")
if !t.Step(env) {
//t.TurnAround()
t.Wiggle()
t.Wiggle()
t.Wiggle()
}
x, y := t.Pos()
center := quadtree.NewXY(float64(x), float64(y))
neighbours := env.GetNearestNeighbours(quadtree.NewAABB(*center, searchtree), 3)
if len(neighbours) > 2 {
//t.SetHeading(avgHead % 8)
// t.Wiggle()
} else {
t.Wiggle()
}
t.Drop(env, 1, "bird")
}
func mainrun() {
experiment := new(experiments.Experiment)
experiment.InitializeExperiment()
t := new(core.Turtle)
t.SetColor(color.RGBA{255, 0, 0, 255})
experiment.InitEnvironment(func(environment *core.Environment) {
// Create 2 food piles
for x := 0; x < 300; x++ {
for y := 0; y < 300; y++ {
if envmap[y/20][x/20] == 1 {
//if ((x %20 < 10) && (y% 20<10)) || x < 30 || y < 30 || y > 270 || x > 270 || (x <100 && y>200) || ((x>150 && x < 300) && (y > 150 && y<175)) || ((x>150 && x < 180) && (y > 100 && y<175)){
environment.Occupy(t, x, y)
environment.PutValue(x,y)
}
}
}
})
experiment.InitNTurtles(func() core.Actor {
sm := new(Bird)
return sm
},1000)
experiment.InitPheromone("bird", color.RGBA{0x80, 0xaa, 0x00, 0x00})
experiment.OnStep = func(environment *core.Environment, turtles []*core.Turtle, i int) {
environment.EvaporateAndDiffuse(0.90, "bird")
}
experiment.Run()
}
func main() {
rand.Seed(time.Now().UnixNano())
pixelgl.Run(mainrun)
}

@ -1,69 +0,0 @@
package main
import (
"flag"
"git.openprivacy.ca/sarah/microworlds/core"
"git.openprivacy.ca/sarah/microworlds/experiments"
"github.com/faiface/pixel/pixelgl"
"image/color"
"math"
)
var sniffDistance = flag.Int("sniffDistance", 3, "the distance a turtle can detect pheromone levels from")
var defensiveDecentralization = flag.Bool("defensiveDecentralization", false, "if true, slime molds will break up if the concentration is too great")
type SlimeMold struct {
SniffDistance int
Age int
Stuck bool
}
func (sm *SlimeMold) Setup(env *core.Environment, t *core.Turtle) {
t.SetColor(color.RGBA{100, 255, 10, 0})
}
func (sm *SlimeMold) Run(env *core.Environment, t *core.Turtle) {
frac := float64(sm.Age) / math.Min(float64(env.Step+1), 255)
col := uint8(256 * frac)
if env.Step < 100 {
t.SetColor(color.RGBA{col, 0, col / 2, 0xf2})
} else {
t.SetColor(color.RGBA{col, 0, col, col})
}
if sm.Stuck == false {
sm.Age++
//t.Wiggle()
//t.Wiggle()
t.Wiggle()
t.Step(env)
c := t.Check(env)
if c != nil {
s := c.GetActor().(*SlimeMold)
if s.Stuck == true {
sm.Stuck = true
}
}
}
}
<