diff --git a/examples/flappylearning/game.v b/examples/flappylearning/game.v index 65c10625de..b9a816020d 100644 --- a/examples/flappylearning/game.v +++ b/examples/flappylearning/game.v @@ -1,4 +1,3 @@ - module main import gg @@ -7,26 +6,24 @@ import os import time import math import rand - import neuroevolution const ( - win_width = 500 - win_height = 512 + win_width = 500 + win_height = 512 timer_period = 24 // ms ) struct Bird { mut: - x f64 = 80 - y f64 = 250 - width f64 = 40 - height f64 = 30 - - alive bool = true - gravity f64 + x f64 = 80 + y f64 = 250 + width f64 = 40 + height f64 = 30 + alive bool = true + gravity f64 velocity f64 = 0.3 - jump f64 = -6 + jump f64 = -6 } fn (mut b Bird) flap() { @@ -43,12 +40,8 @@ fn (b Bird) is_dead(height f64, pipes []Pipe) bool { return true } for pipe in pipes { - if !( - b.x > pipe.x + pipe.width || - b.x + b.width < pipe.x || - b.y > pipe.y + pipe.height || - b.y + b.height < pipe.y - ) { + if !(b.x > pipe.x + pipe.width || + b.x + b.width < pipe.x || b.y > pipe.y + pipe.height || b.y + b.height < pipe.y) { return true } } @@ -57,11 +50,11 @@ fn (b Bird) is_dead(height f64, pipes []Pipe) bool { struct Pipe { mut: - x f64 = 80 - y f64 = 250 - width f64 = 40 + x f64 = 80 + y f64 = 250 + width f64 = 40 height f64 = 30 - speed f64 = 3 + speed f64 = 3 } fn (mut p Pipe) update() { @@ -74,28 +67,25 @@ fn (p Pipe) is_out() bool { struct App { mut: - gg &gg.Context - background gg.Image - bird gg.Image - pipetop gg.Image - pipebottom gg.Image - - pipes []Pipe - birds []Bird - score int - max_score int - width f64 = win_width - height f64 = win_height - spawn_interval f64 = 90 - interval f64 - - nv neuroevolution.Generations - gen []neuroevolution.Network - alives int - generation int - + gg &gg.Context + background gg.Image + bird gg.Image + pipetop gg.Image + pipebottom gg.Image + pipes []Pipe + birds []Bird + score int + max_score int + width f64 = win_width + height f64 = win_height + spawn_interval f64 = 90 + interval f64 + nv neuroevolution.Generations + gen []neuroevolution.Network + alives int + generation int background_speed f64 = 0.5 - background_x f64 + background_x f64 } fn (mut app App) start() { @@ -104,7 +94,6 @@ fn (mut app App) start() { app.pipes = [] app.birds = [] app.gen = app.nv.generate() - for _ in 0 .. app.gen.len { app.birds << Bird{} } @@ -118,14 +107,12 @@ fn (app &App) is_it_end() bool { return false } } - return true } fn (mut app App) update() { app.background_x += app.background_speed mut next_holl := f64(0) - if app.birds.len > 0 { for i := 0; i < app.pipes.len; i += 2 { if app.pipes[i].x + app.pipes[i].width > app.birds[0].x { @@ -134,8 +121,7 @@ fn (mut app App) update() { } } } - - for mut j, bird in app.birds { + for j, mut bird in app.birds { if bird.alive { inputs := [ bird.y / app.height, @@ -145,9 +131,7 @@ fn (mut app App) update() { if res[0] > 0.5 { bird.flap() } - bird.update() - if bird.is_dead(app.height, app.pipes) { bird.alive = false app.alives-- @@ -156,10 +140,8 @@ fn (mut app App) update() { app.start() } } - } } - for k := 0; k < app.pipes.len; k++ { app.pipes[k].update() if app.pipes[k].is_out() { @@ -167,44 +149,35 @@ fn (mut app App) update() { k-- } } - if app.interval == 0 { delta_bord := f64(50) 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{ x: app.width y: 0 height: holl_position } - app.pipes << Pipe{ x: app.width y: holl_position + pipe_holl height: app.height } } - app.interval++ - if app.interval == app.spawn_interval { app.interval = 0 } - app.score++ - app.max_score = if app.score > app.max_score { - app.score - } else { - app.max_score - } - + app.max_score = if app.score > app.max_score { app.score } else { app.max_score } } fn main() { mut app := &App{ gg: 0 } - app.gg = gg.new_context({ + app.gg = gg.new_context( bg_color: gx.white width: win_width height: win_height @@ -215,7 +188,7 @@ fn main() { user_data: app init_fn: init_images font_path: os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf') - }) + ) app.nv = neuroevolution.Generations{ population: 50 network: [2, 2, 1] @@ -248,26 +221,28 @@ fn frame(app &App) { fn (app &App) display() { 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)) - 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 { 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 { - 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 { 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 ,50, 'Max Score: $app.max_score') - 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, 25, 'Score: $app.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, 100, 'Alive: $app.alives / $app.nv.population') } fn (app &App) draw() { diff --git a/examples/flappylearning/modules/neuroevolution/neuronevolution.v b/examples/flappylearning/modules/neuroevolution/neuronevolution.v index a9422d1a3c..d81462475e 100644 --- a/examples/flappylearning/modules/neuroevolution/neuronevolution.v +++ b/examples/flappylearning/modules/neuroevolution/neuronevolution.v @@ -1,4 +1,3 @@ - module neuroevolution import rand @@ -19,7 +18,7 @@ fn round(a int, b f64) int { struct Neuron { mut: - value f64 + value f64 weights []f64 } @@ -30,7 +29,7 @@ fn (mut n Neuron) populate(nb int) { } struct Layer { - id int + id int mut: neurons []Neuron } @@ -49,12 +48,10 @@ mut: } fn (mut n Network) populate(network []int) { - assert network.len >= 2 input := network[0] hiddens := network.slice(1, network.len - 1) output := network[network.len - 1] - mut index := 0 mut previous_neurons := 0 mut input_layer := Layer{ @@ -62,7 +59,6 @@ fn (mut n Network) populate(network []int) { } input_layer.populate(input, previous_neurons) n.layers << input_layer - previous_neurons = input index++ for hidden in hiddens { @@ -74,7 +70,6 @@ fn (mut n Network) populate(network []int) { n.layers << hidden_layer index++ } - mut output_layer := Layer{ id: index } @@ -83,7 +78,6 @@ fn (mut n Network) populate(network []int) { } fn (n Network) get_save() Save { - mut save := Save{} for layer in n.layers { save.neurons << layer.neurons.len @@ -97,11 +91,9 @@ fn (n Network) get_save() Save { } fn (mut n Network) set_save(save Save) { - mut previous_neurons := 0 mut index := 0 mut index_weights := 0 - n.layers = [] for save_neuron in save.neurons { mut layer := Layer{ @@ -123,13 +115,10 @@ fn (mut n Network) set_save(save Save) { pub fn (mut n Network) compute(inputs []f64) []f64 { assert n.layers.len > 0 assert inputs.len == n.layers[0].neurons.len - for i, input in inputs { n.layers[0].neurons[i].value = input } - mut prev_layer := n.layers[0] - for i in 1 .. n.layers.len { for j, neuron in n.layers[i].neurons { mut sum := f64(0) @@ -140,13 +129,11 @@ pub fn (mut n Network) compute(inputs []f64) []f64 { } prev_layer = n.layers[i] } - mut outputs := []f64{} mut last_layer := n.layers[n.layers.len - 1] for neuron in last_layer.neurons { outputs << neuron.value } - return outputs } @@ -164,7 +151,7 @@ fn (s Save) clone() Save { } struct Genome { - score int + score int network Save } @@ -174,65 +161,48 @@ mut: } fn (mut g Generation) add_genome(genome Genome) { - mut i := 0 - for gg in g.genomes { if genome.score > gg.score { break } - i++ } - - g.genomes.insert(i, genome) + g.genomes.insert(i, genome) } fn (g1 Genome) breed(g2 Genome, nb_child int) []Save { mut datas := []Save{} - for _ in 0 .. nb_child { - mut data := g1.network.clone() - for i, weight in g2.network.weights { if rand.f64() <= 0.5 { data.weights[i] = weight } } - for i, _ in data.weights { if rand.f64() <= 0.1 { data.weights[i] += (rand.f64() * 2 - 1) * 0.5 } } - datas << data } - return datas } fn (g Generation) next(population int) []Save { - mut nexts := []Save{} - if population == 0 { return nexts } - keep := round(population, 0.2) - for i in 0 .. keep { if nexts.len < population { nexts << g.genomes[i].network.clone() } } - random := round(population, 0.2) - - for _ in 0 .. random { - + for _ in 0 .. random { if nexts.len < population { mut n := g.genomes[0].network.clone() for k, _ in n.weights { @@ -241,7 +211,6 @@ fn (g Generation) next(population int) []Save { nexts << n } } - mut max := 0 out: for { for i in 0 .. max { @@ -258,14 +227,13 @@ fn (g Generation) next(population int) []Save { max = 0 } } - return nexts } pub struct Generations { pub: - population int - network []int + population int + network []int mut: generations []Generation } @@ -277,7 +245,6 @@ fn (mut gs Generations) first() []Save { nn.populate(gs.network) out << nn.get_save() } - gs.generations << Generation{} return out } @@ -299,24 +266,16 @@ fn (mut gs Generations) restart() { } 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{} for save in saves { mut nn := Network{} nn.set_save(save) nns << nn } - if gs.generations.len >= 2 { gs.generations.delete(0) } - return nns } @@ -326,4 +285,3 @@ pub fn (mut gs Generations) network_score(network Network, score int) { network: network.get_save() }) } - diff --git a/vlib/gg/image.v b/vlib/gg/image.v index 250dd94f5e..d3d53a60d0 100644 --- a/vlib/gg/image.v +++ b/vlib/gg/image.v @@ -51,6 +51,31 @@ pub fn (mut ctx Context) create_image(file string) Image { 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 fn create_image(file string) Image { 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 y0 := f32(y) * 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.enable_texture() diff --git a/vlib/v/parser/for.v b/vlib/v/parser/for.v index d021d16d82..adfca6be5f 100644 --- a/vlib/v/parser/for.v +++ b/vlib/v/parser/for.v @@ -80,8 +80,8 @@ fn (mut p Parser) for_stmt() ast.Stmt { return for_c_stmt } else if p.peek_tok.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` - val_is_mut := p.tok.kind == .key_mut + // `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users` + mut val_is_mut := p.tok.kind == .key_mut if val_is_mut { p.next() } @@ -91,6 +91,11 @@ fn (mut p Parser) for_stmt() ast.Stmt { mut val_var_name := p.check_name() if p.tok.kind == .comma { 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 val_var_pos = p.tok.position() val_var_name = p.check_name()