|
@@ -6,11 +6,13 @@ import (
|
|
|
math "math"
|
|
|
"math/rand"
|
|
|
"net"
|
|
|
+ "sort"
|
|
|
"time"
|
|
|
|
|
|
"gonum.org/v1/gonum/mat"
|
|
|
|
|
|
genetic "../genetic"
|
|
|
+ neuralnetwork "../neuralnetwork"
|
|
|
grpc "google.golang.org/grpc"
|
|
|
)
|
|
|
|
|
@@ -49,14 +51,74 @@ func NewSnakeSimulator() (s *SnakeSimulator) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
-func (s *SnakeSimulator) Verify(population *genetic.Population) (results []*genetic.IndividalResult) {
|
|
|
+func (s *SnakeSimulator) Verify(population *genetic.Population) (fitnesses []*genetic.IndividalFitness) {
|
|
|
s.stats.Generation++
|
|
|
s.statsUpdateQueue <- true
|
|
|
s.field.GenerateNextFood()
|
|
|
- results = make([]*genetic.IndividalResult, len(population.Networks))
|
|
|
+ fitnesses = make([]*genetic.IndividalFitness, len(population.Networks))
|
|
|
for index, inidividual := range population.Networks {
|
|
|
s.stats.Individual = uint32(index)
|
|
|
s.statsUpdateQueue <- true
|
|
|
+
|
|
|
+ s.runSnake(inidividual, false)
|
|
|
+ fitnesses[index] = &genetic.IndividalFitness{
|
|
|
+ // Fitness: float64(len(s.snake.Points)-2) * float64(s.stats.Move),
|
|
|
+ Fitness: float64(s.stats.Move),
|
|
|
+ Index: index,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //This is duplication of crossbreedPopulation functionality.
|
|
|
+ sort.Slice(fitnesses, func(i, j int) bool {
|
|
|
+ return fitnesses[i].Fitness > fitnesses[j].Fitness //Descent order best will be on top, worst in the bottom
|
|
|
+ })
|
|
|
+
|
|
|
+ //Best snake showtime!
|
|
|
+ prevSpeed := s.speed
|
|
|
+ s.speed = 2
|
|
|
+ s.runSnake(population.Networks[fitnesses[0].Index], false)
|
|
|
+ s.speed = prevSpeed
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *SnakeSimulator) runSnake(inidividual *neuralnetwork.NeuralNetwork, randomStart bool) {
|
|
|
+ if randomStart {
|
|
|
+ rand.Seed(time.Now().UnixNano())
|
|
|
+ switch rand.Uint32() % 4 {
|
|
|
+ case 1:
|
|
|
+ s.snake = &Snake{
|
|
|
+ Points: []*Point{
|
|
|
+ &Point{X: 20, Y: 20},
|
|
|
+ &Point{X: 21, Y: 20},
|
|
|
+ &Point{X: 22, Y: 20},
|
|
|
+ },
|
|
|
+ }
|
|
|
+ case 2:
|
|
|
+ s.snake = &Snake{
|
|
|
+ Points: []*Point{
|
|
|
+ &Point{X: 22, Y: 20},
|
|
|
+ &Point{X: 21, Y: 20},
|
|
|
+ &Point{X: 20, Y: 20},
|
|
|
+ },
|
|
|
+ }
|
|
|
+ case 3:
|
|
|
+ s.snake = &Snake{
|
|
|
+ Points: []*Point{
|
|
|
+ &Point{X: 20, Y: 20},
|
|
|
+ &Point{X: 20, Y: 21},
|
|
|
+ &Point{X: 20, Y: 22},
|
|
|
+ },
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ s.snake = &Snake{
|
|
|
+ Points: []*Point{
|
|
|
+ &Point{X: 20, Y: 22},
|
|
|
+ &Point{X: 20, Y: 21},
|
|
|
+ &Point{X: 20, Y: 20},
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
s.snake = &Snake{
|
|
|
Points: []*Point{
|
|
|
&Point{X: 19, Y: 20},
|
|
@@ -64,122 +126,79 @@ func (s *SnakeSimulator) Verify(population *genetic.Population) (results []*gene
|
|
|
&Point{X: 21, Y: 20},
|
|
|
},
|
|
|
}
|
|
|
- // rand.Seed(time.Now().UnixNano())
|
|
|
- // switch rand.Uint32() % 4 {
|
|
|
- // case 1:
|
|
|
- // s.snake = &Snake{
|
|
|
- // Points: []*Point{
|
|
|
- // &Point{X: 20, Y: 20},
|
|
|
- // &Point{X: 21, Y: 20},
|
|
|
- // &Point{X: 22, Y: 20},
|
|
|
- // },
|
|
|
- // }
|
|
|
- // case 2:
|
|
|
- // s.snake = &Snake{
|
|
|
- // Points: []*Point{
|
|
|
- // &Point{X: 22, Y: 20},
|
|
|
- // &Point{X: 21, Y: 20},
|
|
|
- // &Point{X: 20, Y: 20},
|
|
|
- // },
|
|
|
- // }
|
|
|
- // case 3:
|
|
|
- // s.snake = &Snake{
|
|
|
- // Points: []*Point{
|
|
|
- // &Point{X: 20, Y: 20},
|
|
|
- // &Point{X: 20, Y: 21},
|
|
|
- // &Point{X: 20, Y: 22},
|
|
|
- // },
|
|
|
- // }
|
|
|
- // default:
|
|
|
- // s.snake = &Snake{
|
|
|
- // Points: []*Point{
|
|
|
- // &Point{X: 20, Y: 22},
|
|
|
- // &Point{X: 20, Y: 21},
|
|
|
- // &Point{X: 20, Y: 20},
|
|
|
- // },
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- i := 0
|
|
|
- s.stats.Move = 0
|
|
|
- for i < 300 {
|
|
|
- if s.speed > 0 {
|
|
|
- s.statsUpdateQueue <- true
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- //Read speed from client
|
|
|
- select {
|
|
|
- case newSpeed := <-s.speedQueue:
|
|
|
- fmt.Printf("Apply new speed: %v\n", newSpeed)
|
|
|
- if newSpeed < 10 {
|
|
|
- if newSpeed > 0 {
|
|
|
- s.speed = newSpeed
|
|
|
- } else {
|
|
|
- s.speed = 0
|
|
|
- }
|
|
|
- }
|
|
|
- default:
|
|
|
- }
|
|
|
- i++
|
|
|
- if s.speed > 0 {
|
|
|
- s.snakeUpdateQueue <- true
|
|
|
- }
|
|
|
- direction, _ := inidividual.Predict(mat.NewDense(inidividual.Sizes[0], 1, s.GetHeadState()))
|
|
|
- newHead := s.snake.NewHead(Direction(direction + 1))
|
|
|
-
|
|
|
- if selfCollisionIndex := s.snake.SelfCollision(newHead); selfCollisionIndex > 0 {
|
|
|
- if selfCollisionIndex == 1 {
|
|
|
- // switch Direction(direction + 1) {
|
|
|
- // case Direction_Up:
|
|
|
- // newHead = s.snake.NewHead(Direction_Down)
|
|
|
- // case Direction_Down:
|
|
|
- // newHead = s.snake.NewHead(Direction_Up)
|
|
|
- // case Direction_Left:
|
|
|
- // newHead = s.snake.NewHead(Direction_Right)
|
|
|
- // default:
|
|
|
- // newHead = s.snake.NewHead(Direction_Left)
|
|
|
- // }
|
|
|
- continue
|
|
|
+ i := 0
|
|
|
+ s.stats.Move = 0
|
|
|
+ for i < 300 {
|
|
|
+ if s.speed > 0 {
|
|
|
+ s.statsUpdateQueue <- true
|
|
|
+ }
|
|
|
+
|
|
|
+ //Read speed from client
|
|
|
+ select {
|
|
|
+ case newSpeed := <-s.speedQueue:
|
|
|
+ fmt.Printf("Apply new speed: %v\n", newSpeed)
|
|
|
+ if newSpeed < 10 {
|
|
|
+ if newSpeed > 0 {
|
|
|
+ s.speed = newSpeed
|
|
|
} else {
|
|
|
- fmt.Printf("Game over self collision\n")
|
|
|
- break
|
|
|
+ s.speed = 0
|
|
|
}
|
|
|
}
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ if s.speed > 0 {
|
|
|
+ s.snakeUpdateQueue <- true
|
|
|
+ }
|
|
|
+ direction, _ := inidividual.Predict(mat.NewDense(inidividual.Sizes[0], 1, s.GetHeadState()))
|
|
|
+ newHead := s.snake.NewHead(Direction(direction + 1))
|
|
|
|
|
|
- if newHead.X == s.field.Food.X && newHead.Y == s.field.Food.Y {
|
|
|
- s.snake.Feed(newHead)
|
|
|
- s.field.GenerateNextFood()
|
|
|
- if s.speed > 0 {
|
|
|
- s.fieldUpdateQueue <- true
|
|
|
- }
|
|
|
- } else if newHead.X >= s.field.Width || newHead.Y >= s.field.Height {
|
|
|
- // fmt.Printf("Game over\n")
|
|
|
- // time.Sleep(1000 * time.Millisecond)
|
|
|
- break
|
|
|
+ if selfCollisionIndex := s.snake.SelfCollision(newHead); selfCollisionIndex > 0 {
|
|
|
+ if selfCollisionIndex == 1 {
|
|
|
+ // switch Direction(direction + 1) {
|
|
|
+ // case Direction_Up:
|
|
|
+ // newHead = s.snake.NewHead(Direction_Down)
|
|
|
+ // case Direction_Down:
|
|
|
+ // newHead = s.snake.NewHead(Direction_Up)
|
|
|
+ // case Direction_Left:
|
|
|
+ // newHead = s.snake.NewHead(Direction_Right)
|
|
|
+ // default:
|
|
|
+ // newHead = s.snake.NewHead(Direction_Left)
|
|
|
+ // }
|
|
|
+ continue
|
|
|
} else {
|
|
|
- s.snake.Move(newHead)
|
|
|
+ fmt.Printf("Game over self collision\n")
|
|
|
+ break
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ if newHead.X == s.field.Food.X && newHead.Y == s.field.Food.Y {
|
|
|
+ s.snake.Feed(newHead)
|
|
|
+ s.field.GenerateNextFood()
|
|
|
if s.speed > 0 {
|
|
|
- time.Sleep(100 / time.Duration(s.speed) * time.Millisecond)
|
|
|
+ s.fieldUpdateQueue <- true
|
|
|
}
|
|
|
- s.stats.Move++
|
|
|
+ } else if newHead.X >= s.field.Width || newHead.Y >= s.field.Height {
|
|
|
+ // fmt.Printf("Game over\n")
|
|
|
+ // time.Sleep(1000 * time.Millisecond)
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ s.snake.Move(newHead)
|
|
|
}
|
|
|
-
|
|
|
- results[index] = &genetic.IndividalResult{
|
|
|
- // Result: float64(len(s.snake.Points)-2) * float64(s.stats.Move),
|
|
|
- Result: float64(s.stats.Move),
|
|
|
- Index: index,
|
|
|
+ if s.speed > 0 {
|
|
|
+ time.Sleep(100 / time.Duration(s.speed) * time.Millisecond)
|
|
|
}
|
|
|
+ s.stats.Move++
|
|
|
}
|
|
|
-
|
|
|
- return
|
|
|
}
|
|
|
|
|
|
func (s *SnakeSimulator) GetHeadState() []float64 {
|
|
|
headX := float64(s.snake.Points[0].X)
|
|
|
headY := float64(s.snake.Points[0].Y)
|
|
|
- // tailX := float64(s.snake.Points[len(s.snake.Points)-1].X)
|
|
|
- // tailY := float64(s.snake.Points[len(s.snake.Points)-1].Y)
|
|
|
+ tailX := float64(s.snake.Points[len(s.snake.Points)-1].X)
|
|
|
+ tailY := float64(s.snake.Points[len(s.snake.Points)-1].Y)
|
|
|
// prevX := float64(s.snake.Points[1].X)
|
|
|
// prevY := float64(s.snake.Points[1].Y)
|
|
|
foodX := float64(s.field.Food.X)
|
|
@@ -188,44 +207,15 @@ func (s *SnakeSimulator) GetHeadState() []float64 {
|
|
|
height := float64(s.field.Height)
|
|
|
diag := float64(width) * math.Sqrt2
|
|
|
|
|
|
- // tBack := float64(0.0)
|
|
|
- // bBack := float64(0.0)
|
|
|
- // lBack := float64(0.0)
|
|
|
- // rBack := float64(0.0)
|
|
|
- // if prevX == headX {
|
|
|
- // if prevY > headY {
|
|
|
- // bBack = 1.0
|
|
|
- // } else {
|
|
|
- // tBack = 1.0
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // if prevY == headY {
|
|
|
- // if prevX > headX {
|
|
|
- // lBack = 1.0
|
|
|
- // } else {
|
|
|
- // rBack = 1.0
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
lWall := headX
|
|
|
rWall := (width - headX)
|
|
|
tWall := headY
|
|
|
bWall := (height - headY)
|
|
|
+
|
|
|
lFood := float64(width)
|
|
|
rFood := float64(width)
|
|
|
tFood := float64(height)
|
|
|
bFood := float64(height)
|
|
|
-
|
|
|
- // tlFood := float64(diag)
|
|
|
- // trFood := float64(diag)
|
|
|
- // blFood := float64(diag)
|
|
|
- // brFood := float64(diag)
|
|
|
- tlWall := float64(0)
|
|
|
- trWall := float64(0)
|
|
|
- blWall := float64(0)
|
|
|
- brWall := float64(0)
|
|
|
-
|
|
|
if headX == foodX {
|
|
|
tFood = headY - foodY
|
|
|
if tFood < 0 {
|
|
@@ -248,22 +238,30 @@ func (s *SnakeSimulator) GetHeadState() []float64 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // if math.Abs(foodY-headY) == math.Abs(foodX-headX) {
|
|
|
- // if foodX > headX {
|
|
|
- // if foodY > headY {
|
|
|
- // trFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
- // } else {
|
|
|
- // brFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
- // }
|
|
|
- // } else {
|
|
|
- // if foodY > headY {
|
|
|
- // tlFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
- // } else {
|
|
|
- // blFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
+ tlFood := float64(diag)
|
|
|
+ trFood := float64(diag)
|
|
|
+ blFood := float64(diag)
|
|
|
+ brFood := float64(diag)
|
|
|
+ if math.Abs(foodY-headY) == math.Abs(foodX-headX) {
|
|
|
+ if foodX > headX {
|
|
|
+ if foodY > headY {
|
|
|
+ trFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
+ } else {
|
|
|
+ brFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if foodY > headY {
|
|
|
+ tlFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
+ } else {
|
|
|
+ blFood = math.Abs(foodX-headX) * math.Sqrt2
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ tlWall := float64(0)
|
|
|
+ trWall := float64(0)
|
|
|
+ blWall := float64(0)
|
|
|
+ brWall := float64(0)
|
|
|
if lWall > tWall {
|
|
|
tlWall = float64(tWall) * math.Sqrt2
|
|
|
} else {
|
|
@@ -312,68 +310,68 @@ func (s *SnakeSimulator) GetHeadState() []float64 {
|
|
|
brWall = float64(rWall) * math.Sqrt2
|
|
|
}
|
|
|
|
|
|
- // tTail := (headY - tailY)
|
|
|
- // if tTail < 0 {
|
|
|
- // tTail = height
|
|
|
- // }
|
|
|
- // bTail := (tailY - headY)
|
|
|
- // if bTail < 0 {
|
|
|
- // bTail = height
|
|
|
- // }
|
|
|
- // lTail := (headX - tailX)
|
|
|
- // if lTail < 0 {
|
|
|
- // tTail = width
|
|
|
- // }
|
|
|
- // rTail := (tailX - headX)
|
|
|
- // if lTail < 0 {
|
|
|
- // tTail = width
|
|
|
- // }
|
|
|
-
|
|
|
- // tlTail := float64(diag)
|
|
|
- // trTail := float64(diag)
|
|
|
- // blTail := float64(diag)
|
|
|
- // brTail := float64(diag)
|
|
|
- // if math.Abs(headY-tailY) == math.Abs(headX-tailX) {
|
|
|
- // if tailY > headY {
|
|
|
- // if tailX > headX {
|
|
|
- // trTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
- // } else {
|
|
|
- // tlTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
- // }
|
|
|
- // } else {
|
|
|
- // if tailX > headX {
|
|
|
- // brTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
- // } else {
|
|
|
- // blTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
+ tTail := (headY - tailY)
|
|
|
+ if tTail < 0 {
|
|
|
+ tTail = height
|
|
|
+ }
|
|
|
+ bTail := (tailY - headY)
|
|
|
+ if bTail < 0 {
|
|
|
+ bTail = height
|
|
|
+ }
|
|
|
+ lTail := (headX - tailX)
|
|
|
+ if lTail < 0 {
|
|
|
+ tTail = width
|
|
|
+ }
|
|
|
+ rTail := (tailX - headX)
|
|
|
+ if lTail < 0 {
|
|
|
+ tTail = width
|
|
|
+ }
|
|
|
+
|
|
|
+ tlTail := float64(diag)
|
|
|
+ trTail := float64(diag)
|
|
|
+ blTail := float64(diag)
|
|
|
+ brTail := float64(diag)
|
|
|
+ if math.Abs(headY-tailY) == math.Abs(headX-tailX) {
|
|
|
+ if tailY > headY {
|
|
|
+ if tailX > headX {
|
|
|
+ trTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
+ } else {
|
|
|
+ tlTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if tailX > headX {
|
|
|
+ brTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
+ } else {
|
|
|
+ blTail = math.Abs(tailX-headX) * math.Sqrt2
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return []float64{
|
|
|
lWall / width,
|
|
|
rWall / width,
|
|
|
tWall / height,
|
|
|
bWall / height,
|
|
|
- // (1.0 - lFood/width),
|
|
|
- // (1.0 - rFood/width),
|
|
|
- // (1.0 - tFood/height),
|
|
|
- // (1.0 - bFood/height),
|
|
|
+ (1.0 - lFood/width),
|
|
|
+ (1.0 - rFood/width),
|
|
|
+ (1.0 - tFood/height),
|
|
|
+ (1.0 - bFood/height),
|
|
|
tlWall / diag,
|
|
|
trWall / diag,
|
|
|
blWall / diag,
|
|
|
brWall / diag,
|
|
|
- // (1.0 - tlFood/diag),
|
|
|
- // (1.0 - trFood/diag),
|
|
|
- // (1.0 - blFood/diag),
|
|
|
- // (1.0 - brFood/diag),
|
|
|
- // tTail / height,
|
|
|
- // bTail / height,
|
|
|
- // lTail / width,
|
|
|
- // rTail / width,
|
|
|
- // tlTail / diag,
|
|
|
- // trTail / diag,
|
|
|
- // blTail / diag,
|
|
|
- // brTail / diag,
|
|
|
+ (1.0 - tlFood/diag),
|
|
|
+ (1.0 - trFood/diag),
|
|
|
+ (1.0 - blFood/diag),
|
|
|
+ (1.0 - brFood/diag),
|
|
|
+ tTail / height,
|
|
|
+ bTail / height,
|
|
|
+ lTail / width,
|
|
|
+ rTail / width,
|
|
|
+ tlTail / diag,
|
|
|
+ trTail / diag,
|
|
|
+ blTail / diag,
|
|
|
+ brTail / diag,
|
|
|
}
|
|
|
}
|
|
|
|