parser: fix `for i, mut val in vals {; examples: vfmt flappybird

pull/7606/head
Alexander Medvednikov 2020-12-27 12:02:01 +01:00
parent bcdf3ca0cf
commit 0d43ff2453
4 changed files with 96 additions and 129 deletions

View File

@ -1,4 +1,3 @@
module main module main
import gg import gg
@ -7,26 +6,24 @@ import os
import time import time
import math import math
import rand import rand
import neuroevolution import neuroevolution
const ( const (
win_width = 500 win_width = 500
win_height = 512 win_height = 512
timer_period = 24 // ms timer_period = 24 // ms
) )
struct Bird { struct Bird {
mut: mut:
x f64 = 80 x f64 = 80
y f64 = 250 y f64 = 250
width f64 = 40 width f64 = 40
height f64 = 30 height f64 = 30
alive bool = true
alive bool = true gravity f64
gravity f64
velocity f64 = 0.3 velocity f64 = 0.3
jump f64 = -6 jump f64 = -6
} }
fn (mut b Bird) flap() { fn (mut b Bird) flap() {
@ -43,12 +40,8 @@ fn (b Bird) is_dead(height f64, pipes []Pipe) bool {
return true return true
} }
for pipe in pipes { for pipe in pipes {
if !( if !(b.x > pipe.x + pipe.width ||
b.x > pipe.x + pipe.width || b.x + b.width < pipe.x || b.y > pipe.y + pipe.height || b.y + b.height < pipe.y) {
b.x + b.width < pipe.x ||
b.y > pipe.y + pipe.height ||
b.y + b.height < pipe.y
) {
return true return true
} }
} }
@ -57,11 +50,11 @@ fn (b Bird) is_dead(height f64, pipes []Pipe) bool {
struct Pipe { struct Pipe {
mut: mut:
x f64 = 80 x f64 = 80
y f64 = 250 y f64 = 250
width f64 = 40 width f64 = 40
height f64 = 30 height f64 = 30
speed f64 = 3 speed f64 = 3
} }
fn (mut p Pipe) update() { fn (mut p Pipe) update() {
@ -74,28 +67,25 @@ fn (p Pipe) is_out() bool {
struct App { struct App {
mut: mut:
gg &gg.Context gg &gg.Context
background gg.Image background gg.Image
bird gg.Image bird gg.Image
pipetop gg.Image pipetop gg.Image
pipebottom gg.Image pipebottom gg.Image
pipes []Pipe
pipes []Pipe birds []Bird
birds []Bird score int
score int max_score int
max_score int width f64 = win_width
width f64 = win_width height f64 = win_height
height f64 = win_height spawn_interval f64 = 90
spawn_interval f64 = 90 interval f64
interval f64 nv neuroevolution.Generations
gen []neuroevolution.Network
nv neuroevolution.Generations alives int
gen []neuroevolution.Network generation int
alives int
generation int
background_speed f64 = 0.5 background_speed f64 = 0.5
background_x f64 background_x f64
} }
fn (mut app App) start() { fn (mut app App) start() {
@ -104,7 +94,6 @@ fn (mut app App) start() {
app.pipes = [] app.pipes = []
app.birds = [] app.birds = []
app.gen = app.nv.generate() app.gen = app.nv.generate()
for _ in 0 .. app.gen.len { for _ in 0 .. app.gen.len {
app.birds << Bird{} app.birds << Bird{}
} }
@ -118,14 +107,12 @@ fn (app &App) is_it_end() bool {
return false return false
} }
} }
return true return true
} }
fn (mut app App) update() { fn (mut app App) update() {
app.background_x += app.background_speed app.background_x += app.background_speed
mut next_holl := f64(0) mut next_holl := f64(0)
if app.birds.len > 0 { if app.birds.len > 0 {
for i := 0; i < app.pipes.len; i += 2 { for i := 0; i < app.pipes.len; i += 2 {
if app.pipes[i].x + app.pipes[i].width > app.birds[0].x { if app.pipes[i].x + app.pipes[i].width > app.birds[0].x {
@ -134,8 +121,7 @@ fn (mut app App) update() {
} }
} }
} }
for j, mut bird in app.birds {
for mut j, bird in app.birds {
if bird.alive { if bird.alive {
inputs := [ inputs := [
bird.y / app.height, bird.y / app.height,
@ -145,9 +131,7 @@ fn (mut app App) update() {
if res[0] > 0.5 { if res[0] > 0.5 {
bird.flap() bird.flap()
} }
bird.update() bird.update()
if bird.is_dead(app.height, app.pipes) { if bird.is_dead(app.height, app.pipes) {
bird.alive = false bird.alive = false
app.alives-- app.alives--
@ -156,10 +140,8 @@ fn (mut app App) update() {
app.start() app.start()
} }
} }
} }
} }
for k := 0; k < app.pipes.len; k++ { for k := 0; k < app.pipes.len; k++ {
app.pipes[k].update() app.pipes[k].update()
if app.pipes[k].is_out() { if app.pipes[k].is_out() {
@ -167,44 +149,35 @@ fn (mut app App) update() {
k-- k--
} }
} }
if app.interval == 0 { if app.interval == 0 {
delta_bord := f64(50) delta_bord := f64(50)
pipe_holl := f64(120) pipe_holl := f64(120)
holl_position := math.round(rand.f64() * (app.height - delta_bord * 2.0 - pipe_holl)) + delta_bord holl_position := math.round(rand.f64() *
(app.height - delta_bord * 2.0 - pipe_holl)) + delta_bord
app.pipes << Pipe{ app.pipes << Pipe{
x: app.width x: app.width
y: 0 y: 0
height: holl_position height: holl_position
} }
app.pipes << Pipe{ app.pipes << Pipe{
x: app.width x: app.width
y: holl_position + pipe_holl y: holl_position + pipe_holl
height: app.height height: app.height
} }
} }
app.interval++ app.interval++
if app.interval == app.spawn_interval { if app.interval == app.spawn_interval {
app.interval = 0 app.interval = 0
} }
app.score++ app.score++
app.max_score = if app.score > app.max_score { app.max_score = if app.score > app.max_score { app.score } else { app.max_score }
app.score
} else {
app.max_score
}
} }
fn main() { fn main() {
mut app := &App{ mut app := &App{
gg: 0 gg: 0
} }
app.gg = gg.new_context({ app.gg = gg.new_context(
bg_color: gx.white bg_color: gx.white
width: win_width width: win_width
height: win_height height: win_height
@ -215,7 +188,7 @@ fn main() {
user_data: app user_data: app
init_fn: init_images init_fn: init_images
font_path: os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf') font_path: os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf')
}) )
app.nv = neuroevolution.Generations{ app.nv = neuroevolution.Generations{
population: 50 population: 50
network: [2, 2, 1] network: [2, 2, 1]
@ -248,26 +221,28 @@ fn frame(app &App) {
fn (app &App) display() { fn (app &App) display() {
for i := 0; i < int(math.ceil(app.width / app.background.width) + 1.0); i++ { for i := 0; i < int(math.ceil(app.width / app.background.width) + 1.0); i++ {
background_x := i * app.background.width - math.floor(int(app.background_x) % int(app.background.width)) background_x := i * app.background.width - math.floor(int(app.background_x) % int(app.background.width))
app.gg.draw_image(f32(background_x), 0, app.background.width, app.background.height, app.background) app.gg.draw_image(f32(background_x), 0, app.background.width, app.background.height,
app.background)
} }
for i, pipe in app.pipes { for i, pipe in app.pipes {
if i % 2 == 0 { if i % 2 == 0 {
app.gg.draw_image(f32(pipe.x), f32(pipe.y + pipe.height - app.pipetop.height), app.pipetop.width, app.pipetop.height, app.pipetop) app.gg.draw_image(f32(pipe.x), f32(pipe.y + pipe.height - app.pipetop.height),
app.pipetop.width, app.pipetop.height, app.pipetop)
} else { } else {
app.gg.draw_image(f32(pipe.x), f32(pipe.y), app.pipebottom.width, app.pipebottom.height, app.pipebottom) app.gg.draw_image(f32(pipe.x), f32(pipe.y), app.pipebottom.width, app.pipebottom.height,
app.pipebottom)
} }
} }
for bird in app.birds { for bird in app.birds {
if bird.alive { if bird.alive {
app.gg.draw_image(f32(bird.x), f32(bird.y), app.bird.width, app.bird.height, app.bird) app.gg.draw_image(f32(bird.x), f32(bird.y), app.bird.width, app.bird.height,
app.bird)
} }
} }
app.gg.draw_text_def(10 ,25, 'Score: $app.score') app.gg.draw_text_def(10, 25, 'Score: $app.score')
app.gg.draw_text_def(10 ,50, 'Max Score: $app.max_score') app.gg.draw_text_def(10, 50, 'Max Score: $app.max_score')
app.gg.draw_text_def(10 ,75, 'Generation: $app.generation') app.gg.draw_text_def(10, 75, 'Generation: $app.generation')
app.gg.draw_text_def(10 ,100, 'Alive: $app.alives / $app.nv.population') app.gg.draw_text_def(10, 100, 'Alive: $app.alives / $app.nv.population')
} }
fn (app &App) draw() { fn (app &App) draw() {

View File

@ -1,4 +1,3 @@
module neuroevolution module neuroevolution
import rand import rand
@ -19,7 +18,7 @@ fn round(a int, b f64) int {
struct Neuron { struct Neuron {
mut: mut:
value f64 value f64
weights []f64 weights []f64
} }
@ -30,7 +29,7 @@ fn (mut n Neuron) populate(nb int) {
} }
struct Layer { struct Layer {
id int id int
mut: mut:
neurons []Neuron neurons []Neuron
} }
@ -49,12 +48,10 @@ mut:
} }
fn (mut n Network) populate(network []int) { fn (mut n Network) populate(network []int) {
assert network.len >= 2 assert network.len >= 2
input := network[0] input := network[0]
hiddens := network.slice(1, network.len - 1) hiddens := network.slice(1, network.len - 1)
output := network[network.len - 1] output := network[network.len - 1]
mut index := 0 mut index := 0
mut previous_neurons := 0 mut previous_neurons := 0
mut input_layer := Layer{ mut input_layer := Layer{
@ -62,7 +59,6 @@ fn (mut n Network) populate(network []int) {
} }
input_layer.populate(input, previous_neurons) input_layer.populate(input, previous_neurons)
n.layers << input_layer n.layers << input_layer
previous_neurons = input previous_neurons = input
index++ index++
for hidden in hiddens { for hidden in hiddens {
@ -74,7 +70,6 @@ fn (mut n Network) populate(network []int) {
n.layers << hidden_layer n.layers << hidden_layer
index++ index++
} }
mut output_layer := Layer{ mut output_layer := Layer{
id: index id: index
} }
@ -83,7 +78,6 @@ fn (mut n Network) populate(network []int) {
} }
fn (n Network) get_save() Save { fn (n Network) get_save() Save {
mut save := Save{} mut save := Save{}
for layer in n.layers { for layer in n.layers {
save.neurons << layer.neurons.len save.neurons << layer.neurons.len
@ -97,11 +91,9 @@ fn (n Network) get_save() Save {
} }
fn (mut n Network) set_save(save Save) { fn (mut n Network) set_save(save Save) {
mut previous_neurons := 0 mut previous_neurons := 0
mut index := 0 mut index := 0
mut index_weights := 0 mut index_weights := 0
n.layers = [] n.layers = []
for save_neuron in save.neurons { for save_neuron in save.neurons {
mut layer := Layer{ mut layer := Layer{
@ -123,13 +115,10 @@ fn (mut n Network) set_save(save Save) {
pub fn (mut n Network) compute(inputs []f64) []f64 { pub fn (mut n Network) compute(inputs []f64) []f64 {
assert n.layers.len > 0 assert n.layers.len > 0
assert inputs.len == n.layers[0].neurons.len assert inputs.len == n.layers[0].neurons.len
for i, input in inputs { for i, input in inputs {
n.layers[0].neurons[i].value = input n.layers[0].neurons[i].value = input
} }
mut prev_layer := n.layers[0] mut prev_layer := n.layers[0]
for i in 1 .. n.layers.len { for i in 1 .. n.layers.len {
for j, neuron in n.layers[i].neurons { for j, neuron in n.layers[i].neurons {
mut sum := f64(0) mut sum := f64(0)
@ -140,13 +129,11 @@ pub fn (mut n Network) compute(inputs []f64) []f64 {
} }
prev_layer = n.layers[i] prev_layer = n.layers[i]
} }
mut outputs := []f64{} mut outputs := []f64{}
mut last_layer := n.layers[n.layers.len - 1] mut last_layer := n.layers[n.layers.len - 1]
for neuron in last_layer.neurons { for neuron in last_layer.neurons {
outputs << neuron.value outputs << neuron.value
} }
return outputs return outputs
} }
@ -164,7 +151,7 @@ fn (s Save) clone() Save {
} }
struct Genome { struct Genome {
score int score int
network Save network Save
} }
@ -174,65 +161,48 @@ mut:
} }
fn (mut g Generation) add_genome(genome Genome) { fn (mut g Generation) add_genome(genome Genome) {
mut i := 0 mut i := 0
for gg in g.genomes { for gg in g.genomes {
if genome.score > gg.score { if genome.score > gg.score {
break break
} }
i++ i++
} }
g.genomes.insert(i, genome)
g.genomes.insert(i, genome)
} }
fn (g1 Genome) breed(g2 Genome, nb_child int) []Save { fn (g1 Genome) breed(g2 Genome, nb_child int) []Save {
mut datas := []Save{} mut datas := []Save{}
for _ in 0 .. nb_child { for _ in 0 .. nb_child {
mut data := g1.network.clone() mut data := g1.network.clone()
for i, weight in g2.network.weights { for i, weight in g2.network.weights {
if rand.f64() <= 0.5 { if rand.f64() <= 0.5 {
data.weights[i] = weight data.weights[i] = weight
} }
} }
for i, _ in data.weights { for i, _ in data.weights {
if rand.f64() <= 0.1 { if rand.f64() <= 0.1 {
data.weights[i] += (rand.f64() * 2 - 1) * 0.5 data.weights[i] += (rand.f64() * 2 - 1) * 0.5
} }
} }
datas << data datas << data
} }
return datas return datas
} }
fn (g Generation) next(population int) []Save { fn (g Generation) next(population int) []Save {
mut nexts := []Save{} mut nexts := []Save{}
if population == 0 { if population == 0 {
return nexts return nexts
} }
keep := round(population, 0.2) keep := round(population, 0.2)
for i in 0 .. keep { for i in 0 .. keep {
if nexts.len < population { if nexts.len < population {
nexts << g.genomes[i].network.clone() nexts << g.genomes[i].network.clone()
} }
} }
random := round(population, 0.2) random := round(population, 0.2)
for _ in 0 .. random {
for _ in 0 .. random {
if nexts.len < population { if nexts.len < population {
mut n := g.genomes[0].network.clone() mut n := g.genomes[0].network.clone()
for k, _ in n.weights { for k, _ in n.weights {
@ -241,7 +211,6 @@ fn (g Generation) next(population int) []Save {
nexts << n nexts << n
} }
} }
mut max := 0 mut max := 0
out: for { out: for {
for i in 0 .. max { for i in 0 .. max {
@ -258,14 +227,13 @@ fn (g Generation) next(population int) []Save {
max = 0 max = 0
} }
} }
return nexts return nexts
} }
pub struct Generations { pub struct Generations {
pub: pub:
population int population int
network []int network []int
mut: mut:
generations []Generation generations []Generation
} }
@ -277,7 +245,6 @@ fn (mut gs Generations) first() []Save {
nn.populate(gs.network) nn.populate(gs.network)
out << nn.get_save() out << nn.get_save()
} }
gs.generations << Generation{} gs.generations << Generation{}
return out return out
} }
@ -299,24 +266,16 @@ fn (mut gs Generations) restart() {
} }
pub fn (mut gs Generations) generate() []Network { pub fn (mut gs Generations) generate() []Network {
saves := if gs.generations.len == 0 { gs.first() } else { gs.next() }
saves := if gs.generations.len == 0 {
gs.first()
} else {
gs.next()
}
mut nns := []Network{} mut nns := []Network{}
for save in saves { for save in saves {
mut nn := Network{} mut nn := Network{}
nn.set_save(save) nn.set_save(save)
nns << nn nns << nn
} }
if gs.generations.len >= 2 { if gs.generations.len >= 2 {
gs.generations.delete(0) gs.generations.delete(0)
} }
return nns return nns
} }
@ -326,4 +285,3 @@ pub fn (mut gs Generations) network_score(network Network, score int) {
network: network.get_save() network: network.get_save()
}) })
} }

View File

@ -51,6 +51,31 @@ pub fn (mut ctx Context) create_image(file string) Image {
return img return img
} }
// TODO copypasta
pub fn (mut ctx Context) create_image_with_size(file string, width int, height int) Image {
if !C.sg_isvalid() {
// Sokol is not initialized yet, add stbi object to a queue/cache
// ctx.image_queue << file
stb_img := stbi.load(file) or { return Image{} }
img := Image{
width: width
height: height
nr_channels: stb_img.nr_channels
ok: false
data: stb_img.data
ext: stb_img.ext
path: file
id: ctx.image_cache.len
}
ctx.image_cache << img
return img
}
mut img := create_image(file)
img.id = ctx.image_cache.len
ctx.image_cache << img
return img
}
// TODO remove this // TODO remove this
fn create_image(file string) Image { fn create_image(file string) Image {
if !os.exists(file) { if !os.exists(file) {
@ -127,7 +152,11 @@ pub fn (ctx &Context) draw_image(x f32, y f32, width f32, height f32, img_ &Imag
x0 := f32(x) * ctx.scale x0 := f32(x) * ctx.scale
y0 := f32(y) * ctx.scale y0 := f32(y) * ctx.scale
x1 := f32(x + width) * ctx.scale x1 := f32(x + width) * ctx.scale
y1 := f32(y + height) * ctx.scale mut y1 := f32(y + height) * ctx.scale
if height == 0 {
scale := f32(img.width) / f32(width)
y1 = f32(y + int(f32(img.height) / scale)) * ctx.scale
}
// //
sgl.load_pipeline(ctx.timage_pip) sgl.load_pipeline(ctx.timage_pip)
sgl.enable_texture() sgl.enable_texture()

View File

@ -80,8 +80,8 @@ fn (mut p Parser) for_stmt() ast.Stmt {
return for_c_stmt return for_c_stmt
} else if p.peek_tok.kind in [.key_in, .comma] || } else if p.peek_tok.kind in [.key_in, .comma] ||
(p.tok.kind == .key_mut && p.peek_tok2.kind in [.key_in, .comma]) { (p.tok.kind == .key_mut && p.peek_tok2.kind in [.key_in, .comma]) {
// `for i in vals`, `for i in start .. end` // `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users`
val_is_mut := p.tok.kind == .key_mut mut val_is_mut := p.tok.kind == .key_mut
if val_is_mut { if val_is_mut {
p.next() p.next()
} }
@ -91,6 +91,11 @@ fn (mut p Parser) for_stmt() ast.Stmt {
mut val_var_name := p.check_name() mut val_var_name := p.check_name()
if p.tok.kind == .comma { if p.tok.kind == .comma {
p.next() p.next()
if p.tok.kind == .key_mut {
// `for i, mut user in users {`
p.next()
val_is_mut = true
}
key_var_name = val_var_name key_var_name = val_var_name
val_var_pos = p.tok.position() val_var_pos = p.tok.position()
val_var_name = p.check_name() val_var_name = p.check_name()