package core import ( "math/rand" ) type Turtle struct { xpos, ypos int heading int actor Actor } type NilActor struct { } func (NilActor) Setup(*Environment, *Turtle) { } func (NilActor) Run(*Environment, *Turtle) { } func NewTurtle(env *Environment, actor Actor) *Turtle { for { turtle := new(Turtle) turtle.xpos = rand.Intn(env.width) turtle.ypos = rand.Intn(env.height) turtle.actor = actor if env.Check(turtle.xpos, turtle.ypos) == false { actor.Setup(env, turtle) env.Occupy(turtle.xpos, turtle.ypos) turtle.setRandomHeading() return turtle } } } func (t *Turtle) Pos() (int, int) { return t.xpos, t.ypos } var headings = [][]int{{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}} func (t *Turtle) setRandomHeading() { t.heading = rand.Intn(8) } func (t *Turtle) SetXY(x, y int) { t.xpos = x t.ypos = y } func (t *Turtle) Wiggle() { wiggle := rand.Intn(3) - 1 h := (t.heading + wiggle) % 8 if h < 0 { h = 7 } t.heading = h } func (t *Turtle) TurnAround() { t.heading = (t.heading + 4) % 8 } 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, 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) 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 > as && as0 > as1 { t.heading = h0 } else if as1 > as && as1 > as0 { t.heading = h1 } } func (t *Turtle) Step(env *Environment) bool { dx := headings[t.heading][0] dy := headings[t.heading][1] ox := t.xpos oy := t.ypos env.Leave(ox, oy) t.xpos = (t.xpos + dx) % (env.width) if t.xpos < 0 { t.xpos = env.width - 1 } t.ypos = (t.ypos + dy) % (env.height) if t.ypos < 0 { t.ypos = env.height - 1 } success := true if env.Check(t.xpos, t.ypos) == true { t.xpos = ox t.ypos = oy success = false } env.Occupy(t.xpos, t.ypos) return success } // Run the turtle program func (t *Turtle) Run(env *Environment) { t.actor.Run(env, t) }