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 } type NilActor struct { } 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++ { 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() actor.Setup(env, turtle) env.Occupy(turtle, turtle.xpos, turtle.ypos) 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) { 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) { 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 } 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) 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 } 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 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) 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] 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, t.xpos, t.ypos) return success } // Run the turtle program func (t *Turtle) Run(env *Environment) { t.actor.Run(env, t) }