216 lines
5.3 KiB
Go
216 lines
5.3 KiB
Go
package core
|
|
|
|
import (
|
|
"github.com/foolusion/quadtree"
|
|
)
|
|
|
|
type Environment struct {
|
|
width, height int
|
|
state map[string][][]float32
|
|
value [][]bool
|
|
col [][]*Turtle
|
|
Step int
|
|
quadtree *quadtree.QuadTree
|
|
}
|
|
|
|
func (e *Environment) Width() int {
|
|
return e.width
|
|
}
|
|
|
|
func (e *Environment) GetNearestNeighbours(bounding *quadtree.AABB, num int) (turtles []*Turtle) {
|
|
points := e.quadtree.SearchArea(bounding)
|
|
// fmt.Printf("Found X neighbours %v\n",len(points))
|
|
for _, point := range points {
|
|
if len(turtles) < num+1 {
|
|
x, y := point.X, point.Y
|
|
|
|
if e.Get(int(x), int(y)) != nil { // WHY DOES THIS HAPPEN?!?!
|
|
turtles = append(turtles, e.Get(int(x), int(y)))
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (e *Environment) InsertIntoQuadTree(turtle *Turtle) {
|
|
e.quadtree.Insert(quadtree.NewXY(float64(turtle.xpos), float64(turtle.ypos)))
|
|
}
|
|
|
|
func (e *Environment) ResetQuadtree() {
|
|
center := quadtree.NewXY(150, 150)
|
|
area := quadtree.NewAABB(*center, *center)
|
|
e.quadtree = quadtree.New(*area, 10)
|
|
}
|
|
|
|
func (e *Environment) Height() int {
|
|
return e.height
|
|
}
|
|
|
|
func (e *Environment) GetPheromones() []string {
|
|
keys := make([]string, len(e.state))
|
|
i := 0
|
|
for k := range e.state {
|
|
keys[i] = k
|
|
i++
|
|
}
|
|
return keys
|
|
}
|
|
|
|
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(map[string][][]float32)
|
|
|
|
env.col = make([][]*Turtle, width)
|
|
for x := range env.col {
|
|
env.col[x] = make([]*Turtle, height)
|
|
}
|
|
|
|
env.value = make([][]bool, width)
|
|
for x := range env.value {
|
|
env.value[x] = make([]bool, height)
|
|
}
|
|
return env
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
func (e Environment) Occupy(turtle *Turtle, x, y int) {
|
|
e.col[x][y] = turtle
|
|
}
|
|
|
|
func (e Environment) Get(x, y int) *Turtle {
|
|
return e.col[x][y]
|
|
}
|
|
|
|
func (e Environment) Check(x, y int) bool {
|
|
x, y = e.normXY(x, y)
|
|
return e.col[x][y] != nil
|
|
}
|
|
|
|
func (e Environment) Leave(x, y int) {
|
|
e.col[x][y] = nil
|
|
}
|
|
|
|
func (e Environment) HasValue(x, y int) bool {
|
|
return e.value[x][y]
|
|
}
|
|
|
|
func (e Environment) PutValue(x, y int) {
|
|
e.value[x][y] = true
|
|
}
|
|
|
|
func (e Environment) TakeValue(x, y int) {
|
|
e.value[x][y] = false
|
|
}
|
|
|
|
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) {
|
|
if x < 0 {
|
|
x = (e.width - 1)
|
|
} else if x >= e.width {
|
|
x = x % (e.width)
|
|
}
|
|
|
|
if y < 0 {
|
|
y = (e.height - 1)
|
|
} else if y >= e.height {
|
|
y = y % (e.height)
|
|
}
|
|
|
|
return x, y
|
|
}
|
|
|
|
func (e *Environment) Evaporate(rate float32, pheromone string) {
|
|
for x := 0; x < e.width; x++ {
|
|
for y := 0; y < e.height; y++ {
|
|
e.state[pheromone][x][y] = e.state[pheromone][x][y] * rate
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *Environment) EvaporateAndDiffuse(rate float32, pheromone string) {
|
|
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 y := 0; y < e.height; y++ {
|
|
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.state[pheromoneprev][x][y]
|
|
|
|
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[pheromone][x][y] = totalAmount / 9
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *Environment) Diffuse(pheromone string) {
|
|
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.state[pheromoneprev][x][y] = e.state[pheromone][x][y]
|
|
}
|
|
}
|
|
|
|
pheromoneMap := e.state[pheromoneprev]
|
|
for x := 0; x < e.width; x++ {
|
|
for y := 0; y < e.height; y++ {
|
|
amount := e.state[pheromoneprev][x][y]
|
|
|
|
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[pheromone][x][y] = totalAmount / 9
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 pheromonemap[x][y]
|
|
}
|
|
|
|
func (e *Environment) SniffNormalized(x int, y int, pheromone string) float32 {
|
|
x, y = e.normXY(x, y)
|
|
return e.state[pheromone][x][y]
|
|
}
|