From 864d6eae6b586d41b0b5a6461ba1b2d2f07fca64 Mon Sep 17 00:00:00 2001
From: playX <adel.prokurov@gmail.com>
Date: Fri, 22 Oct 2021 22:03:19 +0300
Subject: [PATCH] js: codegen & vlib fixes, replace the Game of Life CLI
 example (#12272)

---
 examples/game_of_life/life.v    | 90 +++++++++++++++++++++++++++------
 vlib/os/os.js.v                 |  6 ++-
 vlib/rand/constants/constants.v |  2 +-
 vlib/time/time.js.v             |  6 +++
 vlib/v/gen/js/builtin_types.v   |  4 +-
 vlib/v/gen/js/js.v              | 22 ++++++--
 6 files changed, 107 insertions(+), 23 deletions(-)

diff --git a/examples/game_of_life/life.v b/examples/game_of_life/life.v
index 0f3b7c7ef0..037a5303c4 100644
--- a/examples/game_of_life/life.v
+++ b/examples/game_of_life/life.v
@@ -1,25 +1,85 @@
-module main
-
+import term
+import rand
 import time
-import automaton
 
-fn print_automaton(a &automaton.Automaton) {
-	for y := 1; y < a.field.maxy; y++ {
-		mut s := '    '
-		for x := 1; x < a.field.maxx; x++ {
-			cell := a.field.get(x, y)
-			s += if cell == 1 { '@' } else { '.' }
-		}
-		println(s)
+const (
+	cell     = '█'
+	nothing  = ' '
+	switches = {
+		cell:    nothing
+		nothing: cell
 	}
-	println('')
+	transformers = [nothing, cell]
+)
+
+struct Game {
+mut:
+	grid [][]string
+}
+
+fn (g Game) get_surrounding_alive_count(x int, y int) int {
+	mut count := 0
+	for i := x - 1; i <= x + 1; i++ {
+		for j := y - 1; j <= y + 1; j++ {
+			if (i != x || j != y) && i >= 0 && j >= 0 && i < g.grid.len && j < g.grid[x].len {
+				if g.grid[i][j] == cell {
+					count++
+				}
+			}
+		}
+	}
+	return count
+}
+
+fn (mut g Game) evolve() {
+	mut temp_grid := [][]string{}
+	for x in 0 .. g.grid.len {
+		temp_grid << []string{}
+		for y in 0 .. g.grid[x].len {
+			count := g.get_surrounding_alive_count(x, y)
+			if count == 3 || ((g.grid[x][y] == cell) && count == 2) {
+				temp_grid[x] << cell
+			} else {
+				temp_grid[x] << nothing
+			}
+		}
+	}
+
+	g.grid = temp_grid
+}
+
+fn (mut g Game) display() {
+	for y in 0 .. g.grid[0].len {
+		mut line := ''
+		for x in 0 .. g.grid.len {
+			line += g.grid[x][y]
+		}
+		println(line)
+	}
+}
+
+fn new_game() Game {
+	w, h := term.get_terminal_size()
+	mut grid := [][]string{}
+	for x in 0 .. w {
+		grid << []string{}
+		for _ in 0 .. h {
+			is_live := rand.f64() > 0.82
+			icon := transformers[int(is_live)]
+			grid[x] << icon
+		}
+	}
+	return Game{grid}
 }
 
 fn main() {
-	mut a := automaton.gun()
+	mut g := new_game()
+
+	g.display()
 	for {
-		a.update()
-		print_automaton(a)
+		g.evolve()
+		term.erase_clear()
+		g.display()
 		time.sleep(100 * time.millisecond)
 	}
 }
diff --git a/vlib/os/os.js.v b/vlib/os/os.js.v
index bdcfa556dc..c9e130d254 100644
--- a/vlib/os/os.js.v
+++ b/vlib/os/os.js.v
@@ -10,8 +10,10 @@ pub const (
 	args           = []string{}
 )
 
-$if js_node {
-	#$process.argv.forEach(function(val,index) { os__args.arr[index] = new string(val); })
+fn init() {
+	$if js_node {
+		#$process.argv.forEach(function(val,index) { os__args.arr[index] = new string(val); })
+	}
 }
 
 // real_path returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved.
diff --git a/vlib/rand/constants/constants.v b/vlib/rand/constants/constants.v
index 76fe211dab..c70d3e4895 100644
--- a/vlib/rand/constants/constants.v
+++ b/vlib/rand/constants/constants.v
@@ -4,7 +4,7 @@ module constants
 pub const (
 	lower_mask     = u64(0x00000000FFFFFFFF)
 	max_u32        = 0xFFFFFFFF
-	max_u64        = 0xFFFFFFFFFFFFFFFF
+	max_u64        = u64(0xFFFFFFFFFFFFFFFF)
 	max_u32_as_f32 = f32(max_u32) + 1
 	max_u64_as_f64 = f64(max_u64) + 1
 	u31_mask       = u32(0x7FFFFFFF)
diff --git a/vlib/time/time.js.v b/vlib/time/time.js.v
index 9d08e08aa5..f6c552d9ef 100644
--- a/vlib/time/time.js.v
+++ b/vlib/time/time.js.v
@@ -36,3 +36,9 @@ pub fn (t Time) local() Time {
 	// if it is not we should try to use Intl for getting local time.
 	return t
 }
+
+pub fn sleep(dur Duration) {
+	#let now = new Date().getTime()
+	#let toWait = BigInt(dur.val) / BigInt(time__millisecond)
+	#while (new Date().getTime() < now + Number(toWait)) {}
+}
diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v
index c3085be652..d24cdfff48 100644
--- a/vlib/v/gen/js/builtin_types.v
+++ b/vlib/v/gen/js/builtin_types.v
@@ -325,7 +325,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
 					typ_name: typ_name
 					default_value: 'new Number(0)'
 					// mask <=32 bit numbers with 0xffffffff
-					constructor: 'this.val = Number(val) & 0xffffffff'
+					constructor: 'this.val = Math.round(Number(val)) & 0xffffffff'
 					value_of: 'Number(this.val)'
 					to_string: 'this.valueOf().toString()'
 					eq: 'new bool(self.valueOf() === other.valueOf())'
@@ -359,7 +359,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
 				g.gen_builtin_prototype(
 					typ_name: typ_name
 					default_value: 'new Number(0)'
-					constructor: 'if (typeof(val) == "string") { this.val = val.charCodeAt() } else if (val instanceof string) { this.val = val.str.charCodeAt(); } else { this.val =  val | 0 }'
+					constructor: 'if (typeof(val) == "string") { this.val = val.charCodeAt() } else if (val instanceof string) { this.val = val.str.charCodeAt(); } else { this.val =  Math.round(val) }'
 					value_of: 'this.val | 0'
 					to_string: 'new string(this.val + "")'
 					eq: 'new bool(self.valueOf() === other.valueOf())'
diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v
index 732d1dab04..de87665960 100644
--- a/vlib/v/gen/js/js.v
+++ b/vlib/v/gen/js/js.v
@@ -86,6 +86,7 @@ mut:
 	array_sort_fn          map[string]bool
 	wasm_export            map[string][]string
 	wasm_import            map[string][]string
+	init_global            map[string]map[string]ast.Expr // initializers for constants or globals, should be invoked before module init.
 }
 
 fn (mut g JsGen) write_tests_definitions() {
@@ -204,6 +205,11 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
 
 	for mod_name in g.table.modules {
 		g.writeln('// Initializations for module $mod_name')
+		for global, expr in g.init_global[mod_name] {
+			g.write('$global = ')
+			g.expr(expr)
+			g.writeln(';')
+		}
 		init_fn_name := '${mod_name}.init'
 		if initfn := g.table.find_fn(init_fn_name) {
 			if initfn.return_type == ast.void_type && initfn.params.len == 0 {
@@ -213,6 +219,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
 			}
 		}
 	}
+
 	if !g.pref.is_shared {
 		g.write('loadRoutine().then(_ => js_main());')
 	}
@@ -1214,7 +1221,7 @@ fn (mut g JsGen) gen_assert_stmt(orig_node ast.AssertStmt) {
 fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
 	if stmt.left.len > stmt.right.len {
 		// multi return
-		g.write('const [')
+		g.write('let [')
 		for i, left in stmt.left {
 			if !left.is_blank_ident() {
 				g.expr(left)
@@ -1469,8 +1476,17 @@ fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) {
 		if field.is_pub {
 			g.push_pub_var(field.name)
 		}
-		g.write('const ${g.js_name(field.name)} = ')
-		g.expr(field.expr)
+
+		if field.expr is ast.StringInterLiteral || field.expr is ast.StringLiteral
+			|| field.expr is ast.IntegerLiteral || field.expr is ast.FloatLiteral
+			|| field.expr is ast.BoolLiteral {
+			g.write('const ${g.js_name(field.name)} = ')
+			g.expr(field.expr)
+		} else {
+			g.write('let ${g.js_name(field.name)} = ')
+			g.write('undefined')
+			g.init_global[g.ns.name][g.js_name(field.name)] = field.expr
+		}
 		g.writeln(';')
 	}
 	g.writeln('')