forked from sarah/microworlds
Laying groundwork for sims with multiple pheremone trails
This commit is contained in:
parent
7857e2f56a
commit
6f714b5545
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,25 +34,30 @@ 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
|
||||
if amount > 0 {
|
||||
g.renderer.SetDrawColor(color[0]*uint8(amount), color[1]*uint8(amount), color[2]*uint8(amount), uint8(255))
|
||||
g.renderer.DrawPoint(int32(x), int32(y))
|
||||
}
|
||||
g.renderer.SetDrawColor(col, 0, col, uint8(255))
|
||||
g.renderer.DrawPoint(int32(x), int32(y))
|
||||
}
|
||||
|
||||
if env.HasValue(x, y) {
|
||||
|
@ -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()
|
||||
|
|
8
main.go
8
main.go
|
@ -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{}
|
||||
|
||||
|
|
Loading…
Reference in New Issue