Skip to content

Commit 6775105

Browse files
committedSep 27, 2019
Gophercon
1 parent cb779e3 commit 6775105

19 files changed

+224
-105
lines changed
 

‎.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
flappy-bird-neural-network
21
.vscode
2+
flappy-bird
3+
neuraldump_ia.json
4+
neuraldump_iahard.json

‎components/bird.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func NewBird(x float64, sprite *pixel.Sprite) *Bird {
4444
Points: 0,
4545
Dead: false,
4646
Ghost: false,
47-
GhostCountdown: ghostCountdown,
47+
GhostCountdown: 0,
4848
}
4949
}
5050

@@ -101,7 +101,7 @@ func (b *Bird) UseGhost() {
101101
b.GhostCountdown = ghostCountdown
102102
b.Ghost = true
103103

104-
time.AfterFunc(2*time.Second, func() {
104+
time.AfterFunc(3*time.Second, func() {
105105
b.Ghost = false
106106
})
107107
}

‎components/sprites.go

+21-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package components
22

33
import (
44
"image"
5+
"log"
56
"os"
7+
"path/filepath"
68

79
"github.com/faiface/pixel"
810
)
@@ -12,74 +14,81 @@ var Sprites = loadSprites()
1214

1315
// LoadSprites load all imagens and return a sprite to each one
1416
func loadSprites() map[string]*pixel.Sprite {
17+
dir, err := os.Executable()
18+
if err != nil {
19+
log.Fatal(err)
20+
}
21+
22+
dir = filepath.Dir(dir)
23+
1524
result := make(map[string]*pixel.Sprite)
16-
picBackground, err := loadPicture("./assets/background.png")
25+
picBackground, err := loadPicture(dir + "/assets/background.png")
1726
if err != nil {
1827
panic(err)
1928
}
2029
result["background"] = pixel.NewSprite(picBackground, picBackground.Bounds())
2130

22-
picFloor, err := loadPicture("./assets/floor.png")
31+
picFloor, err := loadPicture(dir + "/assets/floor.png")
2332
if err != nil {
2433
panic(err)
2534
}
2635
result["floor"] = pixel.NewSprite(picFloor, picFloor.Bounds())
2736

28-
picBird, err := loadPicture("./assets/bird10.png")
37+
picBird, err := loadPicture(dir + "/assets/bird10.png")
2938
if err != nil {
3039
panic(err)
3140
}
3241
result["bird10"] = pixel.NewSprite(picBird, picBird.Bounds())
3342

34-
picBird, err = loadPicture("./assets/bird11.png")
43+
picBird, err = loadPicture(dir + "/assets/bird11.png")
3544
if err != nil {
3645
panic(err)
3746
}
3847
result["bird11"] = pixel.NewSprite(picBird, picBird.Bounds())
3948

40-
picBird, err = loadPicture("./assets/bird12.png")
49+
picBird, err = loadPicture(dir + "/assets/bird12.png")
4150
if err != nil {
4251
panic(err)
4352
}
4453
result["bird12"] = pixel.NewSprite(picBird, picBird.Bounds())
4554

46-
picBird, err = loadPicture("./assets/bird13.png")
55+
picBird, err = loadPicture(dir + "/assets/bird13.png")
4756
if err != nil {
4857
panic(err)
4958
}
5059
result["bird13"] = pixel.NewSprite(picBird, picBird.Bounds())
5160

52-
picBird, err = loadPicture("./assets/bird14.png")
61+
picBird, err = loadPicture(dir + "/assets/bird14.png")
5362
if err != nil {
5463
panic(err)
5564
}
5665
result["bird14"] = pixel.NewSprite(picBird, picBird.Bounds())
5766

58-
picBird, err = loadPicture("./assets/bird15.png")
67+
picBird, err = loadPicture(dir + "/assets/bird15.png")
5968
if err != nil {
6069
panic(err)
6170
}
6271
result["bird15"] = pixel.NewSprite(picBird, picBird.Bounds())
6372

64-
picBird, err = loadPicture("./assets/bird16.png")
73+
picBird, err = loadPicture(dir + "/assets/bird16.png")
6574
if err != nil {
6675
panic(err)
6776
}
6877
result["bird16"] = pixel.NewSprite(picBird, picBird.Bounds())
6978

70-
picUp, err := loadPicture("./assets/pipeup.png")
79+
picUp, err := loadPicture(dir + "/assets/pipeup.png")
7180
if err != nil {
7281
panic(err)
7382
}
7483
result["pipeUp"] = pixel.NewSprite(picUp, picUp.Bounds())
7584

76-
picDown, err := loadPicture("./assets/pipedown.png")
85+
picDown, err := loadPicture(dir + "/assets/pipedown.png")
7786
if err != nil {
7887
panic(err)
7988
}
8089
result["pipeDown"] = pixel.NewSprite(picDown, picDown.Bounds())
8190

82-
pic, err := loadPicture("./assets/wall.png")
91+
pic, err := loadPicture(dir + "/assets/wall.png")
8392
if err != nil {
8493
panic(err)
8594
}

‎components/text.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"fmt"
55
"image/color"
66
"io/ioutil"
7+
"log"
78
"os"
9+
"path/filepath"
810

911
"github.com/faiface/pixel"
1012
"github.com/faiface/pixel/pixelgl"
@@ -29,7 +31,14 @@ func CreateTextLine(txt string, color color.RGBA) Text {
2931

3032
// WriteText draw the text on the window
3133
func WriteText(txt []Text, color color.RGBA, win *pixelgl.Window, mat pixel.Matrix) {
32-
face, err := loadTTF("./assets/font.ttf", 14)
34+
dir, err := os.Executable()
35+
if err != nil {
36+
log.Fatal(err)
37+
}
38+
39+
dir = filepath.Dir(dir)
40+
41+
face, err := loadTTF(dir+"/assets/font.ttf", 14)
3342
if err != nil {
3443
panic(err)
3544
}

‎components/window.go

-9
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,6 @@ func CreateWindow(t string) (*pixelgl.Window, error) {
2828
return pixelgl.NewWindow(wConfig)
2929
}
3030

31-
// Max returns the larger of x or y.
32-
func Max(x, y float64) float64 {
33-
if x < y {
34-
return y
35-
}
36-
37-
return x
38-
}
39-
4031
// Min returns the larger of x or y.
4132
func Min(x, y float64) float64 {
4233
if x > y {

‎flappy-bird-neural-network

6.08 MB
Binary file not shown.

‎flappy_bird

6.08 MB
Binary file not shown.

‎go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/andersondalmina/flappy-bird-neural-network
22

3-
go 1.12
3+
go 1.13
44

55
require (
66
github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 // indirect

‎makefile

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Go parameters
2+
GOCMD=go
3+
GOBUILD=$(GOCMD) build
4+
GOCLEAN=$(GOCMD) clean
5+
GOTEST=$(GOCMD) test
6+
GOGET=$(GOCMD) get
7+
BINARY_NAME=flappy_bird
8+
BINARY_UNIX=$(BINARY_NAME)_unix
9+
BINARY_MACOS=$(BINARY_NAME)_macos
10+
11+
all: test build
12+
13+
build:
14+
$(GOGET)
15+
$(GOBUILD) -o $(BINARY_NAME) -v
16+
17+
test:
18+
$(GOTEST) -v ./...
19+
20+
clean:
21+
$(GOCLEAN)
22+
rm -f $(BINARY_NAME)
23+
rm -f $(BINARY_UNIX)
24+
rm -f $(BINARY_MACOS)
25+
26+
run:
27+
$(GOBUILD) -o $(BINARY_NAME) -v ./...
28+
./$(BINARY_NAME)
29+
30+
31+
# Cross compilation
32+
build-linux:
33+
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_UNIX) -v
34+
35+
build-macos:
36+
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(BINARY_MACOS) -v

‎neuralnetwork/layer.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ func (l *Layer) Neurons() []*Neuron {
2727
return l.neurons
2828
}
2929

30-
// Run predicts all neurons
31-
func (l *Layer) Run(inputs []float64) []float64 {
30+
// Predict all neurons of the layer
31+
func (l *Layer) Predict(inputs []float64) []float64 {
3232
r := make([]float64, len(l.neurons))
3333
for i, n := range l.neurons {
3434
r[i] = n.Predict(inputs)

‎neuralnetwork/neuralnetwork.go

+34-14
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ func NewNeuralNetwork(c Config) *NeuralNetwork {
3535
// Predict computes a forward pass and returns a prediction
3636
func (n *NeuralNetwork) Predict(input []float64) []float64 {
3737
for _, l := range n.layers {
38-
input = l.Run(input)
38+
input = l.Predict(input)
3939
}
4040

41-
return input
41+
result := make([]float64, len(input))
42+
43+
for i, value := range input {
44+
result[i] = relu(value)
45+
}
46+
47+
return result
4248
}
4349

4450
// Weights return the weights of all neurons of the network
@@ -55,11 +61,13 @@ func (n *NeuralNetwork) Weights() [][][]float64 {
5561
return res
5662
}
5763

58-
// SetWeights set the weight of each neuron
59-
func (n *NeuralNetwork) SetWeights(w [][][]float64) {
64+
// UpdateWeights set the weight of each neuron
65+
func (n *NeuralNetwork) UpdateWeights(w [][][]float64) {
66+
weights := ajustWeight(w)
67+
6068
for il, l := range n.layers {
6169
for in, ne := range l.neurons {
62-
ne.SetWeights(w[il][in])
70+
ne.SetWeights(weights[il][in])
6371
}
6472
}
6573
}
@@ -84,25 +92,37 @@ func (n *NeuralNetwork) ImportDump(filepath string) error {
8492
log.Fatalln(err)
8593
}
8694

87-
n.SetWeights(data)
95+
n.UpdateWeights(data)
8896

8997
return nil
9098
}
9199

92-
// AjustWeight make changes in weights
93-
func AjustWeight(bestWeights [][][]float64) [][][]float64 {
94-
var w [][][]float64
100+
// ajustWeight make changes in weights
101+
func ajustWeight(bestWeights [][][]float64) [][][]float64 {
102+
w := make([][][]float64, len(bestWeights))
103+
104+
for i := range bestWeights {
105+
w[i] = make([][]float64, len(bestWeights[i]))
95106

96-
for z := range bestWeights {
97-
for zz := range bestWeights[z] {
98-
for zzz := range bestWeights[z][zz] {
99-
w[z][zz][zzz] = bestWeights[z][zz][zzz]
107+
for ii := range bestWeights[i] {
108+
w[i][ii] = make([]float64, len(bestWeights[i][ii]))
109+
110+
for iii := range bestWeights[i][ii] {
111+
w[i][ii][iii] = bestWeights[i][ii][iii]
100112
if rand.Intn(4) == 0 {
101-
w[z][zz][zzz] += (rand.Float64()*2 - 1) * 100
113+
w[i][ii][iii] += (rand.Float64()*2 - 1) * 100
102114
}
103115
}
104116
}
105117
}
106118

107119
return w
108120
}
121+
122+
func relu(x float64) float64 {
123+
if x <= 0 {
124+
return 0
125+
}
126+
127+
return 1
128+
}

‎neuralnetwork/neuron_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package neuralnetwork
2+
3+
import (
4+
"math/rand"
5+
"testing"
6+
"time"
7+
)
8+
9+
func TestNewNeuron(t *testing.T) {
10+
src := rand.NewSource(time.Now().UTC().UnixNano())
11+
12+
n := NewNeuron(3, src)
13+
14+
if n.Inputs() != 3 {
15+
t.Errorf("Inputs Len must be %d, has %d", 3, n.Inputs())
16+
}
17+
}

‎persist/persist.go

+20-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"bytes"
55
"encoding/json"
66
"io"
7+
"log"
78
"os"
9+
"path/filepath"
810
"sync"
911
)
1012

@@ -23,12 +25,28 @@ func unmarshal(r io.Reader, v interface{}) error {
2325
return json.NewDecoder(r).Decode(v)
2426
}
2527

28+
// GetRootDir return root directory path
29+
func GetRootDir() string {
30+
dir, err := os.Executable()
31+
if err != nil {
32+
log.Fatal(err)
33+
}
34+
35+
return filepath.Dir(dir)
36+
}
37+
38+
// CheckFileExists verify if file exists
39+
func CheckFileExists(path string) error {
40+
_, err := os.Stat(GetRootDir() + "/" + path)
41+
return err
42+
}
43+
2644
// Save saves to the file
2745
func Save(path string, v interface{}) error {
2846
lock.Lock()
2947
defer lock.Unlock()
3048

31-
f, err := os.Create(path)
49+
f, err := os.Create(GetRootDir() + "/" + path)
3250
if err != nil {
3351
return err
3452
}
@@ -49,7 +67,7 @@ func Load(path string, v interface{}) error {
4967
lock.Lock()
5068
defer lock.Unlock()
5169

52-
f, err := os.Open(path)
70+
f, err := os.Open(GetRootDir() + "/" + path)
5371
if err != nil {
5472
return err
5573
}

‎scenes/game_over.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func CreateGameOverScene(p int64) Scene {
2222
return &s
2323
}
2424

25+
func (s *gameOverScene) Load() Scene {
26+
return s
27+
}
28+
2529
func (s *gameOverScene) Run(win *pixelgl.Window) Scene {
2630
win.Clear(colornames.Skyblue)
2731

@@ -40,7 +44,7 @@ func (s *gameOverScene) Run(win *pixelgl.Window) Scene {
4044
components.WriteText(text, colorMenu, win, mat)
4145

4246
if win.JustPressed(pixelgl.KeyEnter) {
43-
return CreateMenuScene()
47+
return CreateMenuScene().Load()
4448
}
4549

4650
return s

‎scenes/ia.go

+22-34
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,33 @@ import (
1313
)
1414

1515
// IndNumber number of individuals of a population
16-
const IndNumber = 100
16+
const IndNumber = 200
1717

1818
var bestWeights = make([][][]float64, 10)
1919
var weights = make([][][]float64, 10)
2020
var bestPoints int64
21+
var datafile string
2122

2223
type ia struct {
23-
pop *components.Population
24-
obstacles []components.Obstacle
24+
generation int64
25+
pop *components.Population
26+
obstacles []components.Obstacle
2527
}
2628

2729
// CreateIAScene create a scene when a machine plays
2830
func CreateIAScene(gn int64) Scene {
31+
return &ia{
32+
generation: gn,
33+
}
34+
}
35+
36+
func (s *ia) Load() Scene {
37+
datafile = "data/neuraldump_ia.json"
38+
2939
rand.Seed(time.Now().UTC().UnixNano())
3040

31-
s := ia{
32-
pop: components.CreateNewPopulation(gn),
33-
obstacles: make([]components.Obstacle, 4),
34-
}
41+
s.pop = components.CreateNewPopulation(s.generation)
42+
s.obstacles = make([]components.Obstacle, 4)
3543

3644
var n int64
3745
var t string
@@ -41,24 +49,12 @@ func CreateIAScene(gn int64) Scene {
4149
t = strconv.FormatInt(n, 10)
4250
neural := neuralnetwork.NewNeuralNetwork(neuralnetwork.Config{
4351
Inputs: 2,
44-
Layers: []int64{3, 1},
52+
Layers: []int64{10, 20, 1},
4553
})
4654
ind = components.NewIndividual(components.NewBird(components.BirdX-rand.Float64()*200, components.Sprites["bird1"+t]), neural)
4755

48-
if gn > 1 {
49-
weights = ind.Neural().Weights()
50-
for z := range bestWeights {
51-
for zz := range bestWeights[z] {
52-
for zzz := range bestWeights[z][zz] {
53-
weights[z][zz][zzz] = bestWeights[z][zz][zzz]
54-
if rand.Intn(4) == 0 {
55-
weights[z][zz][zzz] += (rand.Float64()*2 - 1) * 100
56-
}
57-
}
58-
}
59-
}
60-
61-
ind.Neural().SetWeights(weights)
56+
if s.generation > 1 {
57+
ind.Neural().UpdateWeights(bestWeights)
6258
}
6359

6460
s.pop.AddIndividual(ind)
@@ -68,7 +64,7 @@ func CreateIAScene(gn int64) Scene {
6864
s.obstacles[i] = components.NewPipe(components.WindowWidth+320*float64(i), (components.WindowHeight-components.PipeHeight*2)-rand.Float64()*10*components.PipeHeight)
6965
}
7066

71-
return &s
67+
return s
7268
}
7369

7470
func (s *ia) Run(win *pixelgl.Window) Scene {
@@ -104,7 +100,8 @@ func (s *ia) Run(win *pixelgl.Window) Scene {
104100

105101
ind.SetInputs(bInputs)
106102

107-
if Max(ind.Neural().Predict(bInputs)[0], 0) > 0 {
103+
result := ind.Neural().Predict(bInputs)
104+
if result[0] > 0 {
108105
bird.Jump()
109106
}
110107

@@ -135,7 +132,7 @@ func (s *ia) Run(win *pixelgl.Window) Scene {
135132
bestPoints = best.Bird().Points
136133
}
137134

138-
return CreateIAScene(s.pop.Generation() + 1)
135+
return CreateIAScene(s.pop.Generation() + 1).Load()
139136
}
140137

141138
return s
@@ -225,12 +222,3 @@ func CheckIndividualsDead(pop []*components.Individual) bool {
225222

226223
return true
227224
}
228-
229-
// Max returns the larger of x or y.
230-
func Max(x, y float64) float64 {
231-
if x < y {
232-
return y
233-
}
234-
235-
return x
236-
}

‎scenes/ia_hard.go

+39-23
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,49 @@ package scenes
22

33
import (
44
"math/rand"
5-
"os"
65
"strconv"
76
"time"
87

98
"github.com/andersondalmina/flappy-bird-neural-network/components"
109
"github.com/andersondalmina/flappy-bird-neural-network/neuralnetwork"
10+
"github.com/andersondalmina/flappy-bird-neural-network/persist"
1111
"github.com/faiface/pixel"
1212
"github.com/faiface/pixel/pixelgl"
1313
"golang.org/x/image/colornames"
1414
)
1515

16-
const datafile = "neuraldump_iahard.json"
17-
1816
type iaHard struct {
19-
pop *components.Population
20-
obstacles []components.Obstacle
17+
generation int64
18+
pop *components.Population
19+
obstacles []components.Obstacle
2120
}
2221

2322
// CreateIAHardScene create a scene when a machine plays
2423
func CreateIAHardScene(gn int64) Scene {
24+
return &iaHard{
25+
generation: gn,
26+
}
27+
}
28+
29+
func (s *iaHard) Load() Scene {
30+
datafile = "neuraldump_iahard.json"
31+
32+
var data [][][]float64
33+
var err error
34+
2535
rand.Seed(time.Now().UTC().UnixNano())
2636

2737
resetWallTime()
2838

29-
s := iaHard{
30-
pop: components.CreateNewPopulation(gn),
31-
obstacles: make([]components.Obstacle, 4),
39+
s.pop = components.CreateNewPopulation(s.generation)
40+
s.obstacles = make([]components.Obstacle, 4)
41+
42+
err = persist.CheckFileExists(datafile)
43+
if err == nil {
44+
err = persist.Load(datafile, &data)
45+
if err != nil {
46+
panic(err)
47+
}
3248
}
3349

3450
var n int64
@@ -38,27 +54,28 @@ func CreateIAHardScene(gn int64) Scene {
3854
n = rand.Int63n(4) + 1
3955
t = strconv.FormatInt(n, 10)
4056
neural := neuralnetwork.NewNeuralNetwork(neuralnetwork.Config{
41-
Inputs: 4,
42-
Layers: []int64{4, 20, 2},
57+
Inputs: 3,
58+
Layers: []int64{10, 20, 1},
4359
})
4460
ind = components.NewIndividual(components.NewBird(components.BirdX-rand.Float64()*200, components.Sprites["bird1"+t]), neural)
4561

46-
_, err := os.Stat(datafile)
47-
if gn == 1 && err == nil {
48-
ind.Neural().ImportDump(datafile)
62+
if s.generation == 1 {
63+
ind.Neural().UpdateWeights(data)
4964

50-
} else if gn > 1 {
51-
ind.Neural().SetWeights(neuralnetwork.AjustWeight(ind.Neural().Weights()))
65+
} else if s.generation > 1 {
66+
ind.Neural().UpdateWeights(ind.Neural().Weights())
5267
}
5368

5469
s.pop.AddIndividual(ind)
5570
}
5671

57-
for i := 0; i < 4; i++ {
72+
for i := 0; i < 3; i++ {
5873
s.obstacles[i] = components.NewPipe(components.WindowWidth+320*float64(i), (components.WindowHeight-components.PipeHeight*2)-rand.Float64()*10*components.PipeHeight)
5974
}
6075

61-
return &s
76+
s.obstacles[3] = components.NewWall(components.WindowWidth + 320*3)
77+
78+
return s
6279
}
6380

6481
func (s *iaHard) Run(win *pixelgl.Window) Scene {
@@ -90,20 +107,21 @@ func (s *iaHard) Run(win *pixelgl.Window) Scene {
90107
continue
91108
}
92109

93-
bInputs = make([]float64, 4)
110+
bInputs = make([]float64, 3)
94111
np = s.getBirdNextObstacle(bird)
95112
bInputs[0] = np.GetX() - bird.X
96113
bInputs[1] = np.GetY() - components.PipeHeight - bird.Y
97114
bInputs[2] = np.GetType()
98-
bInputs[3] = float64(bird.GhostCountdown)
99115

100116
ind.SetInputs(bInputs)
101117

102118
result = ind.Neural().Predict(bInputs)
103119

104120
if result[0] > 0 {
105121
bird.Jump()
106-
} else if result[1] > 0 {
122+
}
123+
124+
if result[1] > 0 {
107125
bird.UseGhost()
108126
}
109127

@@ -130,17 +148,15 @@ func (s *iaHard) Run(win *pixelgl.Window) Scene {
130148
}
131149
}
132150

133-
// if best.Bird().Points > bestPoints {
134151
bestWeights = best.Neural().Weights()
135152
bestPoints = best.Bird().Points
136-
// }
137153

138154
err := best.Neural().Dump(datafile)
139155
if err != nil {
140156
panic(err)
141157
}
142158

143-
return CreateIAHardScene(s.pop.Generation() + 1)
159+
return CreateIAHardScene(s.pop.Generation() + 1).Load()
144160
}
145161

146162
return s

‎scenes/menu.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ func CreateMenuScene() Scene {
4949
}
5050
}
5151

52+
func (s *menu) Load() Scene {
53+
return s
54+
}
55+
5256
func (s *menu) Run(win *pixelgl.Window) Scene {
5357
win.Clear(colornames.Skyblue)
5458

@@ -70,7 +74,7 @@ func (s *menu) Run(win *pixelgl.Window) Scene {
7074
components.WriteText(text, colorMenu, win, mat)
7175

7276
if win.JustPressed(pixelgl.KeyEnter) {
73-
return s.options[s.getOptionHover()].goTo
77+
return s.options[s.getOptionHover()].goTo.Load()
7478
}
7579

7680
if win.JustPressed(pixelgl.KeyDown) {

‎scenes/scene.go

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
// Scene is a interface for scenes
1010
type Scene interface {
11+
Load() Scene
1112
Run(win *pixelgl.Window) Scene
1213
}
1314

‎scenes/single_player.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ func CreateSinglePlayerScene() Scene {
3232
status: true,
3333
}
3434

35+
return &s
36+
}
37+
38+
func (s *singlePlayer) Load() Scene {
3539
s.bird = components.NewBird(components.BirdX, components.Sprites["bird10"])
3640

3741
for i := 0.0; i < 4; i++ {
3842
s.obstacles = append(s.obstacles, components.NewPipe(components.WindowWidth+320*i, (components.WindowHeight-components.PipeHeight*2)-rand.Float64()*10*components.PipeHeight))
3943
}
4044

41-
return &s
45+
return s
4246
}
4347

4448
func (s *singlePlayer) Run(win *pixelgl.Window) Scene {
@@ -66,7 +70,7 @@ func (s *singlePlayer) Run(win *pixelgl.Window) Scene {
6670
drawFloor(win)
6771

6872
if s.checkCrash() {
69-
return CreateGameOverScene(s.bird.Points)
73+
return CreateGameOverScene(s.bird.Points).Load()
7074
}
7175

7276
return s

0 commit comments

Comments
 (0)
Please sign in to comment.