1
0
Fork 0

Laying groundwork for sims with multiple pheremone trails

This commit is contained in:
Sarah Jamie Lewis 2019-08-03 10:46:48 -07:00
parent 7857e2f56a
commit 6f714b5545
7 changed files with 85 additions and 51 deletions

View File

@ -23,12 +23,12 @@ func (a *Ant) Run(env *core.Environment, t *core.Turtle) {
t.TurnAround()
} else {
t.Wiggle()
t.FollowGradient(env, a.SniffDistance, 5)
t.FollowGradient(env, a.SniffDistance, 5, "food")
}
t.Step(env)
} else if a.Carrying == true {
a.DropSize -= 0.6
t.Drop(env, a.DropSize)
t.Drop(env, a.DropSize, "food")
t.Wiggle()
t.Step(env)
}

View File

@ -14,7 +14,7 @@ func (sm *SlimeMold) Setup(env *core.Environment, t *core.Turtle) {
func (sm *SlimeMold) Run(env *core.Environment, t *core.Turtle) {
t.Wiggle()
t.FollowGradient(env, sm.SniffDistance, 2)
t.FollowGradient(env, sm.SniffDistance, 2, "trail")
t.Step(env)
t.Drop(env, 1)
t.Drop(env, 1, "trail")
}

View File

@ -2,8 +2,7 @@ package core
type Environment struct {
width, height int
state [][]float32
pstate [][]float32
state map[string][][]float32
value [][]bool
col [][]bool
}
@ -16,14 +15,20 @@ func (e *Environment) Height() int {
return e.height
}
func (e *Environment) InitPheromone(name string) {
state := make([][]float32, e.width)
for x := range state {
state[x] = make([]float32, e.height)
}
e.state[name] = state
}
func NewEnvironment(width int, height int) *Environment {
env := new(Environment)
env.width = width
env.height = height
env.state = make([][]float32, width)
for x := range env.state {
env.state[x] = make([]float32, height)
}
env.state = make(map[string][][]float32)
env.col = make([][]bool, width)
for x := range env.col {
env.col[x] = make([]bool, height)
@ -36,10 +41,16 @@ func NewEnvironment(width int, height int) *Environment {
return env
}
func (e *Environment) Mark(x, y int, amount float32) {
e.state[x][y] = e.state[x][y] + amount
if e.state[x][y] > 255 {
e.state[x][y] = 255
func (e *Environment) Mark(pheromone string, x, y int, amount float32) {
_, exists := e.state[pheromone]
if !exists {
e.InitPheromone(pheromone)
}
e.state[pheromone][x][y] = e.state[pheromone][x][y] + amount
if e.state[pheromone][x][y] > 255 {
e.state[pheromone][x][y] = 255
}
}
@ -67,8 +78,8 @@ func (e Environment) TakeValue(x, y int) {
e.value[x][y] = false
}
func (e Environment) Sniff(x, y int) float32 {
return e.state[x][y]
func (e Environment) Sniff(pheromone string, x, y int) float32 {
return e.state[pheromone][x][y]
}
func (e *Environment) normXY(x int, y int) (int, int) {
@ -87,37 +98,46 @@ func (e *Environment) normXY(x int, y int) (int, int) {
return x, y
}
func (e *Environment) Evaporate(rate float32) {
func (e *Environment) Evaporate(rate float32, pheromone string) {
//log.Debugf("Evap")
e.pstate = make([][]float32, e.width)
for x := range e.pstate {
e.pstate[x] = make([]float32, e.height)
_, exists := e.state[pheromone]
if !exists {
e.InitPheromone(pheromone)
}
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.pstate[x][y] = e.state[x][y] * rate
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.pstate[x][y]
amount := e.state[pheromoneprev][x][y]
totalAmount := amount + e.NormalizeSniff(x-1, y-1) + e.NormalizeSniff(x, y-1) + e.NormalizeSniff(x+1, y-1) + e.NormalizeSniff(x-1, y) + e.NormalizeSniff(x+1, y)
totalAmount += e.NormalizeSniff(x-1, y+1) + e.NormalizeSniff(x, y+1) + e.NormalizeSniff(x+1, y+1)
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[x][y] = totalAmount / 9
e.state[pheromone][x][y] = totalAmount / 9
}
}
}
func (e *Environment) SniffNormalized(x int, y int) float32 {
// Internal optimaization to avoid slow map access
func (e *Environment) sniffNormalized(x int, y int, pheromonemap [][]float32) float32 {
x, y = e.normXY(x, y)
return e.state[x][y]
return pheromonemap[x][y]
}
func (e *Environment) NormalizeSniff(x int, y int) float32 {
func (e *Environment) SniffNormalized(x int, y int, pheromone string) float32 {
x, y = e.normXY(x, y)
return e.pstate[x][y]
return e.state[pheromone][x][y]
}

View File

@ -6,19 +6,19 @@ import (
func TestEnvironment_Mark(t *testing.T) {
env := NewEnvironment(3, 3)
env.Mark(1, 1, 9)
env.Evaporate(0.9)
env.Mark("test", 1, 1, 9)
env.Evaporate(0.9, "test")
for x := 0; x < 3; x++ {
for y := 0; y < 3; y++ {
t.Logf("mark(%d,%d) = %f", x, y, env.Sniff(x, y))
t.Logf("mark(%d,%d) = %f", x, y, env.Sniff("test", x, y))
}
}
t.Logf("\n")
env.Evaporate(0.9)
env.Evaporate(0.9, "test")
for x := 0; x < 3; x++ {
for y := 0; y < 3; y++ {
t.Logf("mark(%d,%d) = %f", x, y, env.Sniff(x, y))
t.Logf("mark(%d,%d) = %f", x, y, env.Sniff("test", x, y))
}
}
}

View File

@ -63,11 +63,11 @@ func (t *Turtle) TurnAround() {
t.heading = (t.heading + 4) % 8
}
func (t *Turtle) Drop(env *Environment, amount float32) {
env.Mark(t.xpos, t.ypos, amount)
func (t *Turtle) Drop(env *Environment, amount float32, pheromone string) {
env.Mark(pheromone, t.xpos, t.ypos, amount)
}
func (t *Turtle) FollowGradient(env *Environment, distance int, threshold float32) {
func (t *Turtle) FollowGradient(env *Environment, distance int, threshold float32, pheromone string) {
h0 := t.heading - 1
if h0 < 0 {
@ -93,15 +93,15 @@ func (t *Turtle) FollowGradient(env *Environment, distance int, threshold float3
x1 := (t.xpos + dx1)
y1 := (t.ypos + dy1)
as0 := env.SniffNormalized(x0, y0)
as0 := env.SniffNormalized(x0, y0, pheromone)
if as0 < threshold {
as0 = 0
}
as := env.SniffNormalized(x, y)
as := env.SniffNormalized(x, y, pheromone)
if as < threshold {
as = 0
}
as1 := env.SniffNormalized(x1, y1)
as1 := env.SniffNormalized(x1, y1, pheromone)
if as1 < threshold {
as1 = 0
}

View File

@ -14,6 +14,7 @@ type Graphics struct {
renderer *sdl.Renderer
width, height int32
t int
colorMap map[string][4]uint8
}
func NewGraphics(width, height int32) *Graphics {
@ -33,26 +34,31 @@ func NewGraphics(width, height int32) *Graphics {
fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err)
panic(err)
}
graphics.colorMap = make(map[string][4]uint8)
graphics.renderer = renderer
return graphics
}
func (g *Graphics) ColorPheromone(name string, color [4]uint8) {
g.colorMap[name] = color
}
func (g *Graphics) Render(env *core.Environment, turtles []*core.Turtle) {
g.renderer.SetDrawColor(0x00, 0x00, 0x00, 0x00)
g.renderer.FillRect(&sdl.Rect{0, 0, 600, 600})
g.renderer.FillRect(&sdl.Rect{X: 0, Y: 0, W: 600, H: 600})
for x := 0; x < int(g.width); x++ {
for y := 0; y < int(g.height); y++ {
amount := math.Min(float64(env.Sniff(x, y)), 255)
for name, color := range g.colorMap {
amount := math.Min(float64(env.Sniff(name, x, y)), 255)
if amount > 0 {
col := uint8(amount * 0x81)
if col > 0x81 {
col = 0x81
}
g.renderer.SetDrawColor(col, 0, col, uint8(255))
g.renderer.SetDrawColor(color[0]*uint8(amount), color[1]*uint8(amount), color[2]*uint8(amount), uint8(255))
g.renderer.DrawPoint(int32(x), int32(y))
}
}
if env.HasValue(x, y) {
g.renderer.SetDrawColor(255, 255, 255, uint8(255))
@ -67,7 +73,11 @@ func (g *Graphics) Render(env *core.Environment, turtles []*core.Turtle) {
g.renderer.DrawPoint(int32(x), int32(y))
t.Run(env)
}
env.Evaporate(0.95)
// TODO: Move this into an environment specification
for name := range g.colorMap {
env.Evaporate(0.95, name)
}
g.renderer.Present()
g.window.UpdateSurface()

View File

@ -44,6 +44,7 @@ func main() {
env := core.NewEnvironment(*width, *height)
turtles := make([]*core.Turtle, *numTurtles)
g := graphics.NewGraphics(int32(*width), int32(*height))
switch *model {
@ -62,6 +63,8 @@ func main() {
for i := 0; i < *numTurtles; i++ {
turtles[i] = core.NewTurtle(env, &actors.Ant{SniffDistance: 3, Carrying: false})
}
env.InitPheromone("food")
g.ColorPheromone("food", [4]uint8{0x81, 0x81, 0x12, 0x00})
case "woodchips":
for x := 0; x < *numTurtles; x++ {
env.PutValue(rand.Intn(*width), rand.Intn(*height))
@ -79,14 +82,15 @@ func main() {
for i := 0; i < *numTurtles; i++ {
turtles[i] = core.NewTurtle(env, &actors.SlimeMold{SniffDistance: 5})
}
env.InitPheromone("trail")
g.ColorPheromone("trail", [4]uint8{0x81, 0, 0x81, 0x00})
default:
for i := 0; i < *numTurtles; i++ {
turtles[i] = core.NewTurtle(env, &actors.SlimeMold{SniffDistance: 20})
}
}
g := graphics.NewGraphics(int32(*width), int32(*height))
running := true
wait := sync.WaitGroup{}