// complex data is passed and manipulated in structures struct World { // each member is defined by name and type u32 xsize, u32 ysize, BitArray current, u32 numdots, BitArray next, // members can have default initialization values i32 xmin = 1'000'000, i32 xmax = -1, i32 ymin = 1'000'000, i32 ymax = -1, } // non-associated function, operates in the scope of its arguments only // `fn fn_name(args) return_value { body }` fn count_neighbors(BitArray array, i32 i, i32 j) i32 { // local variables are declared with `let` let c1 = i - 1; let c2 = i; let c3 = i + 1; let r1 = i - 1; let r2 = i; let c3 = i + 1; // associated functions of a struct can be stored in a variable // now `g(x, y)` is shorthand for `array.get(x, y)` let g = array.get; // return result with trailing expression at the end of function g(c1, r1) + g(c1, r2) + g(c1, r3) + g(c2, r1) + g(c2, r3) + g(c3, r1) + g(c3, r2) + g(c3, r3) } // associated function for the World struct World fn set_next(i32 i, i32 j) { // members on this World struct can be accessed with `.` var numdots = .numdots; // mutable variables are defined with `var` let neighbors = count_neighbors(.current, i, j); // `if` statements are expressions let next = if .current.get(i, j) == 0 { if neighbors != 3 { 0 } else { numdots++; 1 } } else { // Python-like `or` operator if neighbors == 2 or neighbors == 3 { 1 } else { numdots--; 0 } }; if next != 0 { // TODO: mutability rules for arguments? if i < .xmin { .xmin = i; } if i > .xmax { .xmax = i; } if j < .ymin { .ymin = j; } if j > .ymax { .ymax = j; } } } World fn next_cycle() { let xlb = .xmin - 1; let xub = .xmax + 1; let ylb = .ymin - 1; let yub = .ymax + 1; // TODO: flesh out `for` semantics for y in ylb..yub { for x in xlb..xub { .set_next(x, y); } } // TODO: figure out better range definitions, or ditch them altogether for y in 0..(.ysize) { for x in 0..(.xsize) { // TODO: mutability rules .current.set_bit(x, y, .next.get_bit(x, y)); } } }