377 lines
9.2 KiB
Go
377 lines
9.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"git.openprivacy.ca/sarah/microworlds/core"
|
|
"git.openprivacy.ca/sarah/microworlds/experiments"
|
|
"github.com/faiface/pixel/pixelgl"
|
|
"github.com/wcharczuk/go-chart"
|
|
"image/color"
|
|
"math/rand"
|
|
"os"
|
|
"os/signal"
|
|
"runtime/pprof"
|
|
"strconv"
|
|
)
|
|
|
|
type Player struct {
|
|
dna [40]int
|
|
Team int
|
|
}
|
|
|
|
func (player *Player) Setup(env *core.Environment, t *core.Turtle) {
|
|
for i := 0; i < len(player.dna); i++ {
|
|
player.dna[i] = rand.Intn(NumGenes)
|
|
}
|
|
t.SetAttribute("team", strconv.Itoa(player.Team))
|
|
if player.Team == 1 {
|
|
t.SetColor(color.RGBA{0xFF, 0x00, 0x00, 0xFF})
|
|
} else {
|
|
t.SetColor(color.RGBA{0x00, 0x00, 0xFF, 0xFF})
|
|
}
|
|
|
|
}
|
|
|
|
const (
|
|
Wiggle = iota
|
|
FollowMyTeam
|
|
FollowOtherTeam
|
|
FollowBall
|
|
Drop
|
|
RetreatMyTeam
|
|
RetreatOtherTeam
|
|
RetreatBall
|
|
TurnAround
|
|
HeadTowardsOurGoal
|
|
HeadTowardsOtherGoal
|
|
)
|
|
|
|
var NumGenes = 11
|
|
|
|
func (player *Player) Run(env *core.Environment, t *core.Turtle) {
|
|
|
|
MyTeam := strconv.Itoa(player.Team)
|
|
OtherTeam := "1"
|
|
if player.Team == 1 {
|
|
OtherTeam = "2"
|
|
}
|
|
|
|
ahead := t.Check(env)
|
|
if ahead != nil {
|
|
ball, ok := ahead.GetActor().(*Ball)
|
|
if ok {
|
|
ahead.SetHeading(t.Heading())
|
|
if rand.Intn(5) == 0 {
|
|
ahead.Wiggle()
|
|
}
|
|
ahead.Step(env)
|
|
ball.Check(env, ahead)
|
|
}
|
|
}
|
|
t.Step(env)
|
|
|
|
for i := 0; i < len(player.dna); i++ {
|
|
switch player.dna[i] {
|
|
case TurnAround:
|
|
t.TurnAround()
|
|
case Wiggle:
|
|
t.Wiggle()
|
|
case FollowMyTeam:
|
|
if i+2 < len(player.dna) {
|
|
t.FollowGradient(env, player.dna[i+1], float32(player.dna[i+2]), MyTeam)
|
|
i += 2
|
|
}
|
|
case FollowOtherTeam:
|
|
if i+2 < len(player.dna) {
|
|
t.FollowGradient(env, player.dna[i+1], float32(player.dna[i+2]), OtherTeam)
|
|
i += 2
|
|
}
|
|
case FollowBall:
|
|
t.FollowGradient(env, 1, 0, "ball")
|
|
case Drop:
|
|
if i+1 < len(player.dna) {
|
|
t.Drop(env, float32(player.dna[i+1]), strconv.Itoa(player.Team))
|
|
}
|
|
case RetreatMyTeam:
|
|
if i+2 < len(player.dna) {
|
|
t.AvoidAverageGradient(env, player.dna[i+1], float32(player.dna[i+2]), MyTeam)
|
|
i += 2
|
|
}
|
|
case RetreatOtherTeam:
|
|
if i+2 < len(player.dna) {
|
|
t.AvoidAverageGradient(env, player.dna[i+1], float32(player.dna[i+2]), OtherTeam)
|
|
i += 2
|
|
}
|
|
case RetreatBall:
|
|
if i+2 < len(player.dna) {
|
|
t.AvoidAverageGradient(env, player.dna[i+1], float32(player.dna[i+2]), "ball")
|
|
i += 2
|
|
}
|
|
case HeadTowardsOurGoal:
|
|
x, _ := t.Pos()
|
|
if player.Team == 1 {
|
|
if x > 1 {
|
|
t.SetHeading(7)
|
|
}
|
|
} else {
|
|
if x < 299 {
|
|
t.SetHeading(3)
|
|
}
|
|
}
|
|
case HeadTowardsOtherGoal:
|
|
x, _ := t.Pos()
|
|
if player.Team == 2 {
|
|
if x > 1 {
|
|
t.SetHeading(7)
|
|
}
|
|
} else {
|
|
if x < 299 {
|
|
t.SetHeading(3)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (player *Player) Mutate() {
|
|
mutatePoint := rand.Intn(len(player.dna))
|
|
fmt.Printf("\t\t Mutating %v at %v\n", player.Team, mutatePoint)
|
|
player.dna[mutatePoint] = rand.Intn(NumGenes)
|
|
}
|
|
|
|
func (player *Player) Clone(parentA *Player, parentB *Player) {
|
|
fmt.Printf("\t\tCrossing Over team %v with:%v %v\n", player.Team, parentA.dna, parentB.dna)
|
|
crossoverPoint := rand.Intn(len(player.dna))
|
|
copy(player.dna[0:crossoverPoint], parentA.dna[0:crossoverPoint])
|
|
copy(player.dna[crossoverPoint:], parentB.dna[crossoverPoint:])
|
|
}
|
|
|
|
type Ball struct {
|
|
Team1Score int
|
|
Team2Score int
|
|
}
|
|
|
|
func (b *Ball) Setup(env *core.Environment, t *core.Turtle) {
|
|
t.SetXY(50, 50)
|
|
t.SetColor(color.RGBA{0xFF, 0xFF, 0xFF, 0xFF})
|
|
}
|
|
|
|
func (b *Ball) Run(env *core.Environment, t *core.Turtle) {
|
|
b.Check(env, t)
|
|
t.Drop(env, 10, "ball")
|
|
}
|
|
|
|
func (b *Ball) Check(env *core.Environment, t *core.Turtle) {
|
|
x, _ := t.Pos()
|
|
if x > 98 {
|
|
fmt.Printf("\t\tTeam 1 Scored!!! %v\n", env.Step)
|
|
b.Reset(env, t)
|
|
b.Team1Score++
|
|
} else if x < 1 {
|
|
fmt.Printf("\t\tTeam 2 Scored!!! %v\n", env.Step)
|
|
b.Reset(env, t)
|
|
b.Team2Score++
|
|
}
|
|
}
|
|
|
|
func (b *Ball) Reset(env *core.Environment, t *core.Turtle) {
|
|
x, y := t.Pos()
|
|
t.SetHeading(rand.Intn(8))
|
|
env.Leave(x, y)
|
|
t.SetXY(50, 50)
|
|
env.Occupy(t, 50, 50)
|
|
}
|
|
|
|
func mainrun() {
|
|
ball := new(Ball)
|
|
experiment := new(experiments.Experiment)
|
|
experiment.InitializeExperiment()
|
|
experiment.InitNTurtles(func() core.Actor {
|
|
player := new(Player)
|
|
player.Team = 1
|
|
return player
|
|
}, 25)
|
|
experiment.InitNTurtles(func() core.Actor {
|
|
player := new(Player)
|
|
player.Team = 2
|
|
return player
|
|
}, 25)
|
|
experiment.InitNTurtles(func() core.Actor {
|
|
return ball
|
|
}, 1)
|
|
experiment.InitPheromone("1", color.RGBA{0xFF, 0x00, 0x00, 0x00})
|
|
experiment.InitPheromone("2", color.RGBA{0x00, 0x00, 0xFF, 0x00})
|
|
experiment.InitPheromone("ball", color.RGBA{0xff, 0xff, 0xff, 0xff})
|
|
|
|
x := []float64{-2}
|
|
team1 := []float64{0}
|
|
team2 := []float64{1}
|
|
|
|
var graph chart.Chart
|
|
experiment.AddPlot("Goals Scored", func(environment *core.Environment, turtles []*core.Turtle) *chart.Chart {
|
|
x = append(x, float64(environment.Step))
|
|
team1 = append(team1, float64(ball.Team1Score))
|
|
team2 = append(team2, float64(ball.Team2Score))
|
|
graph = chart.Chart{
|
|
Background: chart.Style{
|
|
Padding: chart.Box{
|
|
Top: 50,
|
|
},
|
|
},
|
|
XAxis: chart.XAxis{Name: "Time Step", NameStyle: chart.Style{Show: true}, Style: chart.Style{Show: true, TextRotationDegrees: 90}, ValueFormatter: func(v interface{}) string {
|
|
return fmt.Sprintf("%d", int(v.(float64)))
|
|
}},
|
|
YAxis: chart.YAxis{Name: "Goals Scored", NameStyle: chart.Style{Show: true}, Style: chart.Style{Show: true}, ValueFormatter: func(v interface{}) string {
|
|
return fmt.Sprintf("%d", int(v.(float64)))
|
|
}},
|
|
Series: []chart.Series{
|
|
chart.ContinuousSeries{
|
|
XValues: x,
|
|
YValues: team1,
|
|
},
|
|
chart.ContinuousSeries{
|
|
XValues: x,
|
|
YValues: team2,
|
|
},
|
|
},
|
|
}
|
|
return &graph
|
|
})
|
|
|
|
gamelengths := []int{}
|
|
gamewins := []int{}
|
|
gamescores := []int{}
|
|
lastGameStart := 0
|
|
|
|
experiment.OnStep = func(environment *core.Environment, turtles []*core.Turtle, i int) {
|
|
|
|
environment.EvaporateAndDiffuse(0.95, "ball")
|
|
environment.EvaporateAndDiffuse(0.95, "1")
|
|
environment.EvaporateAndDiffuse(0.95, "2")
|
|
|
|
if environment.Step-lastGameStart > 4000 {
|
|
fmt.Printf("Time!\n")
|
|
|
|
if ball.Team1Score == ball.Team2Score {
|
|
fmt.Printf("Draw!\n")
|
|
gamelengths = append(gamelengths, environment.Step-lastGameStart)
|
|
gamewins = append(gamewins, 0)
|
|
gamescores = append(gamescores, ball.Team1Score, ball.Team2Score)
|
|
lastGameStart = environment.Step
|
|
|
|
ball.Team2Score = 0
|
|
ball.Team1Score = 0
|
|
|
|
red, blue := GetTeams(turtles)
|
|
for _, r := range red {
|
|
player := r.GetActor().(*Player)
|
|
player.Mutate()
|
|
player.Mutate()
|
|
player.Mutate()
|
|
player.Mutate()
|
|
player.Mutate()
|
|
}
|
|
for _, b := range blue {
|
|
player := b.GetActor().(*Player)
|
|
player.Mutate()
|
|
player.Mutate()
|
|
player.Mutate()
|
|
player.Mutate()
|
|
player.Mutate()
|
|
|
|
}
|
|
for i, lengths := range gamelengths {
|
|
fmt.Printf("Generation: %v, %v time steps Team %v Won (%v - %v)\n", i, lengths, gamewins[i], gamescores[i*2], gamescores[(i*2)+1])
|
|
}
|
|
} else {
|
|
ball.Team1Score++
|
|
ball.Team2Score++
|
|
}
|
|
}
|
|
|
|
if ball.Team1Score >= 3 && ball.Team2Score < 3 {
|
|
fmt.Printf("Team 1 Win")
|
|
red, blue := GetTeams(turtles)
|
|
for _, b := range blue {
|
|
player := b.GetActor().(*Player)
|
|
parentA := rand.Intn(len(red))
|
|
parentB := rand.Intn(len(red))
|
|
player.Clone(red[parentA].GetActor().(*Player), red[parentB].GetActor().(*Player))
|
|
player.Mutate()
|
|
}
|
|
adjust := 0
|
|
if environment.Step-lastGameStart > 4000 {
|
|
adjust = (environment.Step - lastGameStart) - 4000
|
|
}
|
|
|
|
gamelengths = append(gamelengths, environment.Step-lastGameStart)
|
|
gamewins = append(gamewins, 1)
|
|
gamescores = append(gamescores, ball.Team1Score-adjust, ball.Team2Score-adjust)
|
|
lastGameStart = environment.Step
|
|
|
|
for i, lengths := range gamelengths {
|
|
fmt.Printf("Generation: %v, %v time steps Team %v Won (%v - %v)\n", i, lengths, gamewins[i], gamescores[i*2], gamescores[(i*2)+1])
|
|
}
|
|
|
|
ball.Team2Score = 0
|
|
ball.Team1Score = 0
|
|
|
|
} else if ball.Team2Score >= 3 && ball.Team1Score < 3 {
|
|
fmt.Printf("Team 2 Win")
|
|
red, blue := GetTeams(turtles)
|
|
for _, r := range red {
|
|
player := r.GetActor().(*Player)
|
|
parentA := rand.Intn(len(blue))
|
|
parentB := rand.Intn(len(blue))
|
|
player.Clone(blue[parentA].GetActor().(*Player), blue[parentB].GetActor().(*Player))
|
|
player.Mutate()
|
|
}
|
|
|
|
adjust := 0
|
|
if environment.Step-lastGameStart > 4000 {
|
|
adjust = (environment.Step - lastGameStart) - 4000
|
|
}
|
|
|
|
gamelengths = append(gamelengths, environment.Step-lastGameStart)
|
|
gamewins = append(gamewins, 2)
|
|
gamescores = append(gamescores, ball.Team1Score-adjust, ball.Team2Score-adjust)
|
|
lastGameStart = environment.Step
|
|
|
|
ball.Team2Score = 0
|
|
ball.Team1Score = 0
|
|
|
|
for i, lengths := range gamelengths {
|
|
fmt.Printf("Generation: %v, %v time steps Team %v Won (%v - %v)\n", i, lengths, gamewins[i], gamescores[i*2], gamescores[(i*2)+1])
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt)
|
|
go func() {
|
|
for sig := range c {
|
|
fmt.Printf("Got Signal %v", sig)
|
|
pprof.StopCPUProfile()
|
|
os.Exit(0)
|
|
}
|
|
}()
|
|
|
|
experiment.Run()
|
|
}
|
|
|
|
func main() {
|
|
pixelgl.Run(mainrun)
|
|
}
|
|
|
|
func GetTeams(turtles []*core.Turtle) (redTeam, blueTeam []*core.Turtle) {
|
|
for _, turtle := range turtles {
|
|
if turtle.GetAttribute("team") == "1" {
|
|
redTeam = append(redTeam, turtle)
|
|
} else if turtle.GetAttribute("team") == "2" {
|
|
blueTeam = append(blueTeam, turtle)
|
|
}
|
|
}
|
|
return
|
|
}
|