luanette 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/pyrandom.mjs ADDED
@@ -0,0 +1,50 @@
1
+ // Python's random.Random(seed).random(), exactly: the Mersenne Twister, seeded as Python seeds it
2
+ // from an int (init_by_array). check.py's scripted input draws taps from it, and the command-line
3
+ // tool feeds a cart the same input.
4
+ export function pyRandom(seed) {
5
+ const N = 624, mt = new Uint32Array(N);
6
+ let mti = N + 1;
7
+ const init = (s) => {
8
+ mt[0] = s >>> 0;
9
+ for (mti = 1; mti < N; mti++) {
10
+ const p = mt[mti - 1] ^ (mt[mti - 1] >>> 30);
11
+ mt[mti] = (Math.imul(1812433253, p) + mti) >>> 0;
12
+ }
13
+ };
14
+ init(19650218);
15
+ const key = [seed >>> 0];
16
+ let i = 1, j = 0;
17
+ for (let k = Math.max(N, key.length); k; k--) {
18
+ const p = mt[i - 1] ^ (mt[i - 1] >>> 30);
19
+ mt[i] = ((mt[i] ^ Math.imul(p, 1664525)) + key[j] + j) >>> 0;
20
+ i++; j++;
21
+ if (i >= N) { mt[0] = mt[N - 1]; i = 1; }
22
+ if (j >= key.length) j = 0;
23
+ }
24
+ for (let k = N - 1; k; k--) {
25
+ const p = mt[i - 1] ^ (mt[i - 1] >>> 30);
26
+ mt[i] = ((mt[i] ^ Math.imul(p, 1566083941)) - i) >>> 0;
27
+ i++;
28
+ if (i >= N) { mt[0] = mt[N - 1]; i = 1; }
29
+ }
30
+ mt[0] = 0x80000000;
31
+ const next = () => {
32
+ if (mti >= N) {
33
+ for (let kk = 0; kk < N; kk++) {
34
+ const y = (mt[kk] & 0x80000000) | (mt[(kk + 1) % N] & 0x7fffffff);
35
+ mt[kk] = mt[(kk + 397) % N] ^ (y >>> 1) ^ (y & 1 ? 0x9908b0df : 0);
36
+ }
37
+ mti = 0;
38
+ }
39
+ let y = mt[mti++];
40
+ y ^= y >>> 11;
41
+ y ^= (y << 7) & 0x9d2c5680;
42
+ y ^= (y << 15) & 0xefc60000;
43
+ y ^= y >>> 18;
44
+ return y >>> 0;
45
+ };
46
+ return () => {
47
+ const a = next() >>> 5, b = next() >>> 6;
48
+ return (a * 67108864 + b) / 9007199254740992;
49
+ };
50
+ }
@@ -0,0 +1,45 @@
1
+ -- title: Blank
2
+ -- by: you
3
+ -- about: a sprite to move: a place to start
4
+ -- api: 4
5
+ -- canvas: full
6
+
7
+ -- sprites (the playground's sprite editor writes this table)
8
+ SPRITES = {
9
+ [1] = "00777700 07777770 77177177 77777777 77777777 77177177 07711770 00777700", -- face
10
+ }
11
+
12
+ -- The whole screen: the 128x128 picture in the middle, and bands above and below it in the
13
+ -- full-color layer (hd) for the title and what to do. Drawn once; they stay.
14
+ local c = caps()
15
+ local W, H = c.w, c.h
16
+ local TOP, BOT = c.picture_y, c.picture_y + 384
17
+
18
+ local function bands(title, hint)
19
+ hd.scanlines(false)
20
+ hd.gradient(0, 0, W, TOP, 0x1b2440, 0x000000)
21
+ hd.text(title, W / 2, TOP / 2 - 16, 0xffffff, 24, "bold", "center")
22
+ hd.gradient(0, BOT, W, H - BOT, 0x000000, 0x1b2440)
23
+ hd.text(hint, W / 2, BOT + 22, 0x9aa4b8, 14, "mono", "center")
24
+ end
25
+
26
+ local x, y = 60, 60 -- the sprite's top left, in picture pixels (0 to 127)
27
+
28
+ function _init()
29
+ fps(60, 10) -- 60 while touched, 10 when nothing moves: a still screen costs nothing
30
+ bands("BLANK", "drag or arrow keys to move")
31
+ log("started") -- shows in the playground's Console
32
+ end
33
+
34
+ function _update()
35
+ if btn(0) then x = x - 1 end
36
+ if btn(1) then x = x + 1 end
37
+ if btn(2) then y = y - 1 end
38
+ if btn(3) then y = y + 1 end
39
+ x, y = mid(0, x, 120), mid(0, y, 120)
40
+ end
41
+
42
+ function _draw()
43
+ cls(1)
44
+ spr(1, x, y)
45
+ end
@@ -0,0 +1,95 @@
1
+ -- title: Platformer
2
+ -- by: you
3
+ -- about: jump between platforms and pick up the coins
4
+ -- api: 4
5
+ -- canvas: full
6
+
7
+ -- sprites (the playground's sprite editor writes this table)
8
+ SPRITES = {
9
+ [1] = "00888800 08888880 08f1f180 08ffff80 00888800 08888880 08800880 08800880", -- player
10
+ [2] = "00aaaa00 0aa99aa0 aa9aa9aa aa9aa9aa aa9aa9aa aa9aa9aa 0aa99aa0 00aaaa00", -- coin
11
+ }
12
+ -- sounds (the playground's sound editor writes this table)
13
+ SOUNDS = {
14
+ [8] = "03 0000 18050 1f051 24045", -- jump
15
+ [9] = "03 0000 2f160 34165", -- coin
16
+ [10] = "06 0000 1f050 18053", -- fall
17
+ }
18
+
19
+ -- The whole screen: the 128x128 picture in the middle, and bands above and below it in the
20
+ -- full-color layer (hd) for the title and what to do. Drawn once; they stay.
21
+ local c = caps()
22
+ local W, H = c.w, c.h
23
+ local TOP, BOT = c.picture_y, c.picture_y + 384
24
+
25
+ local function bands(title, hint)
26
+ hd.scanlines(false)
27
+ hd.gradient(0, 0, W, TOP, 0x1b2440, 0x000000)
28
+ hd.text(title, W / 2, TOP / 2 - 16, 0xffffff, 24, "bold", "center")
29
+ hd.gradient(0, BOT, W, H - BOT, 0x000000, 0x1b2440)
30
+ hd.text(hint, W / 2, BOT + 22, 0x9aa4b8, 14, "mono", "center")
31
+ end
32
+
33
+ local GRAVITY, JUMP, SPEED = 0.25, -3.6, 1.4
34
+ local floors = { -- platforms: x, y (their top), w
35
+ {x = 0, y = 120, w = 128}, {x = 12, y = 96, w = 36}, {x = 72, y = 80, w = 40},
36
+ {x = 24, y = 58, w = 32}, {x = 80, y = 40, w = 36},
37
+ }
38
+ local me = {x = 8, y = 100, vx = 0, vy = 0, ground = false}
39
+ local coins, score, level = {}, 0, 1
40
+
41
+ local function new_coins()
42
+ coins = {}
43
+ for i = 2, #floors do
44
+ local f = floors[i]
45
+ coins[#coins + 1] = {x = f.x + flr(rnd(f.w - 8)), y = f.y - 12}
46
+ end
47
+ end
48
+
49
+ function _init()
50
+ bands("PLATFORMER", "drag to run · tap to jump")
51
+ new_coins()
52
+ end
53
+
54
+ function _update()
55
+ me.vx = (btn(0) and -SPEED or 0) + (btn(1) and SPEED or 0)
56
+ if btnp(4) and me.ground then
57
+ me.vy = JUMP
58
+ sfx(8)
59
+ end
60
+ me.vy = me.vy + GRAVITY
61
+ local was = me.y
62
+ me.x = mid(0, me.x + me.vx, 120)
63
+ me.y = me.y + me.vy
64
+ me.ground = false
65
+ for _, f in ipairs(floors) do -- land on a platform's top on the way down
66
+ if me.vy >= 0 and was + 8 <= f.y and me.y + 8 >= f.y and me.x + 6 > f.x and me.x + 2 < f.x + f.w then
67
+ me.y, me.vy, me.ground = f.y - 8, 0, true
68
+ end
69
+ end
70
+ for i = #coins, 1, -1 do
71
+ local k = coins[i]
72
+ if abs(me.x - k.x) < 7 and abs(me.y - k.y) < 7 then
73
+ table.remove(coins, i)
74
+ score = score + 1
75
+ sfx(9)
76
+ end
77
+ end
78
+ if #coins == 0 then
79
+ level = level + 1
80
+ log("level", level, "score", score)
81
+ new_coins()
82
+ end
83
+ if me.y > 128 then -- fell off: back to the start
84
+ me.x, me.y, me.vy = 8, 100, 0
85
+ sfx(10)
86
+ end
87
+ end
88
+
89
+ function _draw()
90
+ cls(12)
91
+ for _, f in ipairs(floors) do rectfill(f.x, f.y, f.x + f.w - 1, f.y + 3, 3) end
92
+ for _, k in ipairs(coins) do spr(2, k.x, k.y) end
93
+ spr(1, me.x, me.y, me.vx < 0)
94
+ print("score " .. score, 2, 2, 7)
95
+ end
@@ -0,0 +1,96 @@
1
+ -- title: Puzzle
2
+ -- by: you
3
+ -- about: lights out: switch every light off
4
+ -- api: 4
5
+ -- canvas: full
6
+
7
+ -- sprites (the playground's sprite editor writes this table)
8
+ SPRITES = {
9
+ [1] = "000a0000 000a0000 0aaaaa00 00aaa000 00a0a000 0a000a00 00000000 00000000", -- star
10
+ }
11
+ -- sounds (the playground's sound editor writes this table)
12
+ SOUNDS = {
13
+ [8] = "02 0000 28345", -- click
14
+ [9] = "06 0000 24150 28150 2b150 30155", -- solved
15
+ }
16
+
17
+ -- The whole screen: the 128x128 picture in the middle, and bands above and below it in the
18
+ -- full-color layer (hd) for the title and what to do. Drawn once; they stay.
19
+ local c = caps()
20
+ local W, H = c.w, c.h
21
+ local TOP, BOT = c.picture_y, c.picture_y + 384
22
+
23
+ local function bands(title, hint)
24
+ hd.scanlines(false)
25
+ hd.gradient(0, 0, W, TOP, 0x1b2440, 0x000000)
26
+ hd.text(title, W / 2, TOP / 2 - 16, 0xffffff, 24, "bold", "center")
27
+ hd.gradient(0, BOT, W, H - BOT, 0x000000, 0x1b2440)
28
+ hd.text(hint, W / 2, BOT + 22, 0x9aa4b8, 14, "mono", "center")
29
+ end
30
+
31
+ -- Lights out: switching a light switches the four next to it too. Get them all off.
32
+ local N, CELL, X0, Y0 = 5, 22, 9, 14
33
+ local lit, cx, cy, moves, solved = {}, 3, 3, 0, false
34
+ local was_touching = false
35
+
36
+ local function flip(x, y)
37
+ for _, d in ipairs({{0, 0}, {1, 0}, {-1, 0}, {0, 1}, {0, -1}}) do
38
+ local i, j = x + d[1], y + d[2]
39
+ if i >= 1 and i <= N and j >= 1 and j <= N then lit[j][i] = not lit[j][i] end
40
+ end
41
+ end
42
+
43
+ local function new_puzzle() -- from all off, a few random presses: always solvable
44
+ for j = 1, N do lit[j] = {} for i = 1, N do lit[j][i] = false end end
45
+ for _ = 1, 8 do flip(flr(rnd(N)) + 1, flr(rnd(N)) + 1) end
46
+ moves, solved = 0, false
47
+ end
48
+
49
+ function _init()
50
+ fps(60, 10) -- 60 while touched, 10 when nothing moves
51
+ bands("PUZZLE", "tap a light · switch them all off")
52
+ new_puzzle()
53
+ end
54
+
55
+ function _update()
56
+ local tx, ty = touch()
57
+ if tx then -- a finger: the cursor goes where it is (a tap then presses, as A)
58
+ cx = mid(1, flr((tx - X0) / CELL) + 1, N)
59
+ cy = mid(1, flr((ty - Y0) / CELL) + 1, N)
60
+ elseif not was_touching then
61
+ if btnp(0) then cx = mid(1, cx - 1, N) end
62
+ if btnp(1) then cx = mid(1, cx + 1, N) end
63
+ if btnp(2) then cy = mid(1, cy - 1, N) end
64
+ if btnp(3) then cy = mid(1, cy + 1, N) end
65
+ end
66
+ was_touching = tx ~= nil
67
+ if btnp(4) then
68
+ if solved then new_puzzle() return end
69
+ flip(cx, cy)
70
+ moves = moves + 1
71
+ sfx(8)
72
+ local any = false
73
+ for j = 1, N do for i = 1, N do any = any or lit[j][i] end end
74
+ if not any then
75
+ solved = true
76
+ sfx(9)
77
+ log("solved in", moves, "moves")
78
+ end
79
+ end
80
+ end
81
+
82
+ function _draw()
83
+ cls(1)
84
+ for j = 1, N do
85
+ for i = 1, N do
86
+ local x, y = X0 + (i - 1) * CELL, Y0 + (j - 1) * CELL
87
+ rectfill(x + 1, y + 1, x + CELL - 2, y + CELL - 2, lit[j][i] and 10 or 5)
88
+ if i == cx and j == cy then rect(x, y, x + CELL - 1, y + CELL - 1, 7) end
89
+ end
90
+ end
91
+ print("moves " .. moves, 2, 2, 7)
92
+ if solved then
93
+ spr(1, 56, 4)
94
+ print("solved: tap for another", 18, 122, 11)
95
+ end
96
+ end
@@ -0,0 +1,108 @@
1
+ -- title: Shooter
2
+ -- by: you
3
+ -- about: steer the ship; it fires on its own
4
+ -- api: 4
5
+ -- canvas: full
6
+
7
+ -- sprites (the playground's sprite editor writes this table)
8
+ SPRITES = {
9
+ [1] = "00077000 00077000 0077c700 007cc700 077cc770 77777777 78077087 80000008", -- ship
10
+ [2] = "08000080 00800800 08888880 88188188 88888888 08088080 80000008 08000080", -- invader
11
+ }
12
+ -- sounds (the playground's sound editor writes this table)
13
+ SOUNDS = {
14
+ [8] = "01 0000 30223", -- shot
15
+ [9] = "04 0000 18770 0c755", -- boom
16
+ [10] = "06 0000 10460 0c455", -- hurt
17
+ }
18
+
19
+ -- The whole screen: the 128x128 picture in the middle, and bands above and below it in the
20
+ -- full-color layer (hd) for the title and what to do. Drawn once; they stay.
21
+ local c = caps()
22
+ local W, H = c.w, c.h
23
+ local TOP, BOT = c.picture_y, c.picture_y + 384
24
+
25
+ local function bands(title, hint)
26
+ hd.scanlines(false)
27
+ hd.gradient(0, 0, W, TOP, 0x1b2440, 0x000000)
28
+ hd.text(title, W / 2, TOP / 2 - 16, 0xffffff, 24, "bold", "center")
29
+ hd.gradient(0, BOT, W, H - BOT, 0x000000, 0x1b2440)
30
+ hd.text(hint, W / 2, BOT + 22, 0x9aa4b8, 14, "mono", "center")
31
+ end
32
+
33
+ local ship = {x = 60}
34
+ local shots, foes, sparks = {}, {}, {}
35
+ local score, lives, t = 0, 3, 0
36
+
37
+ function _init()
38
+ bands("SHOOTER", "drag to steer · tap to start again")
39
+ end
40
+
41
+ local function hit(f) -- an enemy goes up in sparks
42
+ for i = 1, 8 do
43
+ sparks[#sparks + 1] = {x = f.x + 4, y = f.y + 4, vx = rnd(2) - 1, vy = rnd(2) - 1, life = 15}
44
+ end
45
+ sfx(9)
46
+ end
47
+
48
+ function _update()
49
+ t = t + 1
50
+ if lives == 0 then
51
+ if btnp(4) then score, lives, foes, shots = 0, 3, {}, {} end
52
+ return
53
+ end
54
+ local tx = touch()
55
+ if tx then ship.x = tx - 4 end
56
+ if btn(0) then ship.x = ship.x - 2 end
57
+ if btn(1) then ship.x = ship.x + 2 end
58
+ ship.x = mid(0, ship.x, 120)
59
+ if t % 8 == 0 then
60
+ shots[#shots + 1] = {x = ship.x + 3, y = 112}
61
+ sfx(8)
62
+ end
63
+ if t % 30 == 0 then foes[#foes + 1] = {x = flr(rnd(120)), y = -8, phase = rnd(1)} end
64
+ for i = #shots, 1, -1 do
65
+ local s = shots[i]
66
+ s.y = s.y - 4
67
+ if s.y < -4 then table.remove(shots, i) end
68
+ end
69
+ for i = #foes, 1, -1 do
70
+ local f = foes[i]
71
+ f.y = f.y + 0.6 + score / 200
72
+ f.x = f.x + sin(t / 60 + f.phase) * 0.5
73
+ local gone = false
74
+ for j = #shots, 1, -1 do
75
+ local s = shots[j]
76
+ if s.x >= f.x and s.x <= f.x + 7 and s.y >= f.y and s.y <= f.y + 7 then
77
+ table.remove(shots, j)
78
+ hit(f)
79
+ score = score + 1
80
+ gone = true
81
+ break
82
+ end
83
+ end
84
+ if not gone and (f.y > 128 or (f.y > 108 and abs(f.x - ship.x) < 7)) then
85
+ lives = lives - 1
86
+ sfx(10)
87
+ log("lost a life at score", score)
88
+ gone = true
89
+ end
90
+ if gone then table.remove(foes, i) end
91
+ end
92
+ for i = #sparks, 1, -1 do
93
+ local p = sparks[i]
94
+ p.x, p.y, p.life = p.x + p.vx, p.y + p.vy, p.life - 1
95
+ if p.life <= 0 then table.remove(sparks, i) end
96
+ end
97
+ end
98
+
99
+ function _draw()
100
+ cls(0)
101
+ for _, s in ipairs(shots) do rectfill(s.x, s.y, s.x + 1, s.y + 3, 10) end
102
+ for _, f in ipairs(foes) do spr(2, f.x, f.y) end
103
+ for _, p in ipairs(sparks) do pset(p.x, p.y, p.life > 8 and 10 or 9) end
104
+ if lives > 0 then spr(1, ship.x, 116) end
105
+ print("score " .. score, 2, 2, 7)
106
+ print("lives " .. lives, 96, 2, 8)
107
+ if lives == 0 then print("game over: tap", 36, 60, 7) end
108
+ end