2019-09-25 06:38:15 +00:00
|
|
|
package experiments
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"git.openprivacy.ca/sarah/microworlds/core"
|
|
|
|
"git.openprivacy.ca/sarah/microworlds/graphics"
|
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
2019-11-17 03:00:03 +00:00
|
|
|
"github.com/wcharczuk/go-chart"
|
2019-09-25 06:38:15 +00:00
|
|
|
"image/color"
|
|
|
|
"log"
|
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
"runtime/pprof"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
|
|
|
var width = flag.Int("width", 300, "width of environment")
|
|
|
|
var height = flag.Int("height", 300, "height of environment")
|
|
|
|
var pxsize = flag.Int("pxsize", 1, "pixels per tile edge")
|
|
|
|
var numTurtles = flag.Int("numTurtles", 5000, "number of turtles")
|
|
|
|
|
|
|
|
type Experiment struct {
|
|
|
|
env *core.Environment
|
|
|
|
turtles []*core.Turtle
|
|
|
|
graphics *graphics.Graphics
|
|
|
|
initializedTurtles int
|
2019-09-30 04:52:01 +00:00
|
|
|
pheromones []string
|
2019-09-28 21:56:23 +00:00
|
|
|
OnStep func(*core.Environment, []*core.Turtle, int)
|
2019-11-17 03:00:03 +00:00
|
|
|
plots []*graphics.Plot
|
|
|
|
running bool
|
2019-09-25 06:38:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) InitializeExperiment() {
|
|
|
|
// We don't need real randomness
|
|
|
|
rand.Seed(time.Now().Unix())
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
if *cpuprofile != "" {
|
|
|
|
f, err := os.Create(*cpuprofile)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
pprof.StartCPUProfile(f)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2019-09-29 22:32:54 +00:00
|
|
|
e.OnStep = func(env *core.Environment, turtles []*core.Turtle, i int) {
|
|
|
|
for _, name := range e.pheromones {
|
|
|
|
env.EvaporateAndDiffuse(0.95, name)
|
|
|
|
}
|
2019-09-29 21:16:09 +00:00
|
|
|
}
|
|
|
|
|
2019-09-25 06:38:15 +00:00
|
|
|
e.env = core.NewEnvironment(*width, *height)
|
2019-09-28 21:56:23 +00:00
|
|
|
e.turtles = make([]*core.Turtle, 0)
|
2019-09-25 06:38:15 +00:00
|
|
|
e.graphics = graphics.NewGraphics(int32(*width), int32(*height), int32(*pxsize))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) GetNumTurtles() int {
|
|
|
|
return (*numTurtles)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) InitPheromone(name string, col color.Color) {
|
|
|
|
e.env.InitPheromone(name)
|
|
|
|
r, g, b, a := col.RGBA()
|
2019-09-29 22:32:54 +00:00
|
|
|
e.pheromones = append(e.pheromones, name)
|
2019-09-25 06:38:15 +00:00
|
|
|
e.graphics.ColorPheromone(name, [4]uint8{uint8(r), uint8(g), uint8(b), uint8(a)})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) InitEnvironment(f func(environment *core.Environment)) {
|
|
|
|
f(e.env)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) InitNTurtles(f func() core.Actor, num int) {
|
2019-09-28 21:56:23 +00:00
|
|
|
numSuccess := 0
|
2019-09-25 06:38:15 +00:00
|
|
|
for i := e.initializedTurtles; i < e.initializedTurtles+num; i++ {
|
2019-09-28 21:56:23 +00:00
|
|
|
t := core.NewTurtle(e.env, f())
|
|
|
|
if t != nil {
|
|
|
|
e.turtles = append(e.turtles, t)
|
|
|
|
numSuccess++
|
|
|
|
}
|
2019-09-25 06:38:15 +00:00
|
|
|
}
|
2019-09-28 21:56:23 +00:00
|
|
|
e.initializedTurtles += numSuccess
|
2019-09-25 06:38:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) InitTurtles(f func() core.Actor) {
|
|
|
|
e.InitNTurtles(f, (*numTurtles))
|
|
|
|
}
|
|
|
|
|
2019-11-17 03:00:03 +00:00
|
|
|
func (e *Experiment) AddPlot(title string, plotfunc func(environment *core.Environment, turtles []*core.Turtle) *chart.Chart) {
|
|
|
|
plot := graphics.NewPlot(title, int32(*width), int32(*height), int32(*pxsize))
|
|
|
|
plot.GeneratePlot = plotfunc
|
2019-11-17 21:08:02 +00:00
|
|
|
go plot.RenderAsync()
|
2019-11-17 03:00:03 +00:00
|
|
|
e.plots = append(e.plots, plot)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) Stop() {
|
|
|
|
e.running = false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Experiment) Restart() {
|
|
|
|
e.running = false
|
|
|
|
}
|
|
|
|
|
2019-09-25 06:38:15 +00:00
|
|
|
func (e *Experiment) Run() {
|
|
|
|
wait := sync.WaitGroup{}
|
|
|
|
|
2019-11-17 03:00:03 +00:00
|
|
|
e.running = true
|
2019-09-25 06:38:15 +00:00
|
|
|
wait.Add(1)
|
2019-11-17 03:00:03 +00:00
|
|
|
e.env.ResetQuadtree()
|
2019-09-25 06:38:15 +00:00
|
|
|
go func() {
|
2019-09-28 21:56:23 +00:00
|
|
|
step := 0
|
2019-11-17 03:00:03 +00:00
|
|
|
for e.running {
|
2019-11-03 02:33:30 +00:00
|
|
|
e.env.Step = step
|
2019-09-25 06:38:15 +00:00
|
|
|
e.graphics.Render(e.env, e.turtles)
|
2019-09-28 21:56:23 +00:00
|
|
|
e.OnStep(e.env, e.turtles, step)
|
|
|
|
|
2019-11-17 03:00:03 +00:00
|
|
|
for _, plot := range e.plots {
|
|
|
|
plot.Render(e.env, e.turtles)
|
|
|
|
}
|
|
|
|
|
2019-09-28 21:56:23 +00:00
|
|
|
newTurtles := make([]*core.Turtle, 0)
|
2019-11-17 03:00:03 +00:00
|
|
|
|
2019-09-28 21:56:23 +00:00
|
|
|
for _, t := range e.turtles {
|
2019-11-17 03:00:03 +00:00
|
|
|
t.Run(e.env)
|
2019-09-28 21:56:23 +00:00
|
|
|
if t.GetAttribute("status") != "dead" {
|
|
|
|
newTurtles = append(newTurtles, t)
|
|
|
|
} else {
|
|
|
|
e.env.Leave(t.Pos()) // Dead turtles occupy no space
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e.turtles = newTurtles
|
2019-11-17 03:00:03 +00:00
|
|
|
e.env.ResetQuadtree()
|
|
|
|
for _, t := range e.turtles {
|
|
|
|
e.env.InsertIntoQuadTree(t)
|
|
|
|
}
|
|
|
|
|
2019-09-28 21:56:23 +00:00
|
|
|
step++
|
2019-09-25 06:38:15 +00:00
|
|
|
}
|
|
|
|
wait.Done()
|
|
|
|
}()
|
|
|
|
|
|
|
|
wait.Add(1)
|
2019-11-17 03:00:03 +00:00
|
|
|
for e.running {
|
2019-09-25 06:38:15 +00:00
|
|
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
|
|
|
switch event.(type) {
|
|
|
|
case *sdl.QuitEvent:
|
2019-11-17 03:00:03 +00:00
|
|
|
e.running = false
|
2019-09-25 06:38:15 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wait.Done()
|
|
|
|
wait.Wait()
|
|
|
|
}
|