421 lines
8.1 KiB
Go
421 lines
8.1 KiB
Go
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)
|
|
}
|