For some reason I thought that Gemini would be good at generating levels of this kind, perhaps because it actually TOLD me it was super good at such tasks ...
Code:
' Code by me, levels by Gemini 3.1 Pro with Extended Thinking. I told Gemini to generate levels of
' increasing difficulty - number 16 should be super hard.
' Move with arrow keys, restart with esc key, skip level (number 15 is impossible) with S key.
#win32
visible vLevel
visible vTilesImage, vPlayerImage
set window "Sokoban", 256, 224, false, 3
set redraw off
levelIndex = 0
do
vLevel = LevelFromString(levels[levelIndex])
do
levelComplete = true
for y = 0 to 12 for x = 0 to 15
if vLevel.fg[x][y] levelComplete = vLevel.fg[x][y].Update() and levelComplete
next
if keydown(KEY_S, true) levelComplete = true
set color 0, 0, 0
cls
set color 255, 255, 255
for y = 0 to 12 for x = 0 to 15 draw image vTilesImage, x*16, y*16, vLevel.bg[x][y]
for y = 0 to 12 for x = 0 to 15 if vLevel.fg[x][y] vLevel.fg[x][y].Draw()
set color 255, 255, 255
set caret width(primary)/2, height(primary) - 14
center "LEVEL " + (levelIndex + 1)
redraw
fwait 60
until levelComplete or keydown(KEY_ESCAPE, true)
if levelComplete levelIndex = levelIndex + 1
until levelIndex = sizeof(levels)
set color 0, 0, 0
cls
set caret width(primary)/2, (height(primary) - fheight())/2
set color 255, 255, 255
center "GAME COMPLETED!"
redraw
wait 3000
end
function CreateAssets()
vTilesImage = createimage(64, 16)
set image grid vTilesImage, 4, 1
set image vTilesImage
set color 32, 32, 32
draw rect 0, 0, 16, 16, true
set color 48, 48, 48
for i = 0 to 63 draw pixel rnd(16), rnd(16)
set color 128, 128, 128
draw rect 16, 0, 16, 16, true
set color 208, 208, 208
for y = 0 to 3
draw line 16, y*4, 32, y*4
for x = 0 to 1 draw line 16 + x*8 + (y%2)*4, y*4, 16 + x*8 + (y%2)*4, y*4 + 4
next
set color 255, 255, 255
draw image vTilesImage, 32, 0, 0
set color 128, 64, 32
draw rect 32, 0, 16, 16, false
set color 192, 128, 96
draw rect 48, 0, 16, 16, true
set color 128, 96, 64
draw rect 48, 0, 16, 16, false
draw rect 51, 3, 10, 10, false
draw line 55, 4, 59, 8
draw line 51, 7, 55, 11
set image primary
vPlayerImage = createimage(16, 16)
set image vPlayerImage
set color 0, 0, 0, 0
cls true
set color 255, 208, 0
for y = 0 to 1 for x = 0 to 1 draw ellipse 7 + x, 7 + y, 7, 7, true
set color 64, 32, 0
draw line 4, 4, 4, 8
draw line 11, 4, 11, 8
draw line 5, 12, 10, 12
draw pixel 4, 11; draw pixel 11, 11
set image primary
endfunc
function LevelFromString(s)
level = [bg: fill(0, 16, 13), fg: fill(unset, 16, 13)]
for i = 0 to 207
y = floor(i/16); x = i%16
select mid(s, i)
case "#" level.bg[x][y] = 1
case "." level.bg[x][y] = 2
case "$" level.fg[x][y] = Block(vTilesImage, 3, x, y)
case "@" level.fg[x][y] = Player(x, y)
endsel
next
return level
endfunc
function Block(img, cel, x, y)
b = [img: img, cel: cel, x: x, y: y, dx: 0, dy: 0, spd: 1]
b.Move = function()
if this.dx > 0 this.dx = max(this.dx - this.spd, 0)
elseif this.dx < 0 this.dx = min(this.dx + this.spd, 0)
if this.dy > 0 this.dy = max(this.dy - this.spd, 0)
elseif this.dy < 0 this.dy = min(this.dy + this.spd, 0)
return this.dx or this.dy
endfunc
b.BeginMove = function(dx, dy)
' If you can figure out the purpose of "- (dx > 0)*this.spd" and "- (dy > 0)*this.spd", you
' may reward yourself with a cookie :)
this.dx = -dx*16 - (dx > 0)*this.spd
this.dy = -dy*16 - (dy > 0)*this.spd
vLevel.fg[this.x][this.y] = unset
this.x = this.x + dx; this.y = this.y + dy
vLevel.fg[this.x][this.y] = this
endfunc
b.Update = function()
return not this.Move() and vLevel.bg[this.x][this.y] = 2
endfunc
b.Draw = function()
set color 255, 255, 255
draw image this.img, this.x*16 + this.dx, this.y*16 + this.dy, this.cel
endfunc
return b
endfunc
function Player(x, y)
p = Block(vPlayerImage, 0, x, y)
p.Update = function()
if not this.Move()
dx = 0; dy = 0
if keydown(KEY_LEFT) dx = -1
elseif keydown(KEY_RIGHT) dx = 1
elseif keydown(KEY_UP) dy = -1
elseif keydown(KEY_DOWN) dy = 1
if dx or dy
newX = this.x + dx; newY = this.y + dy
if vLevel.fg[newX][newY]
if vLevel.bg[newX + dx][newY + dy] <> 1 and not vLevel.fg[newX + dx][newY + dy]
vLevel.fg[newX][newY].BeginMove(dx, dy)
this.BeginMove(dx, dy)
endif
elseif vLevel.bg[newX][newY] <> 1
this.BeginMove(dx, dy)
endif
endif
endif
return true
endfunc
return p
endfunc
Does anyone have the source code of an autostereogram generator written in n6? Would be fun to see if I can use the depth-map of an s3d rendering in n7 for these things.
' --- Build foreground trees with VARIED sizes (once, at startup) ---
' Deterministic per run. Add "randomize time()" above for a new forest each run.
visible trees = [] ; trees.num = 0
for i = 0 to 9
trees[i] = []
trees[i].x = i * 100 + rnd(0, 60) ' scattered across world 0..960
trees[i].w = 14 + rnd(0, 16) ' width varies 14-30
trees[i].h = 28 + rnd(0, 38) ' height varies 28-66
next
trees.num = 10
' ================= MAIN LOOP =================
do
' --- AUTO-SCROLL CAMERA (triangle wave / ping-pong) ---
' phase 0..0.5 : camera moves RIGHT (0 -> WORLD_W)
' phase 0.5..1 : camera moves LEFT (WORLD_W -> 0)
t = (clock() / 1000) % CYCLE
phase = t / CYCLE
if phase < 0.5 then
cam.x = WORLD_W * (phase * 2)
else
cam.x = WORLD_W * (2 - phase * 2)
endif
' LAYER 0: Night sky (fixed, depth 0 - never moves)
set color 10, 10, 30; draw rect 0, 0, 384, 224, true
' LAYER 1: BLINKING STARS (depth 0.5, wrapped with %)
' Alpha oscillates via sin(); each star gets its own phase offset
' so they twinkle independently instead of pulsing in sync
tw = clock() / 500
for s = 0 to 5
alpha = int(128 + 127 * sin(tw + s * 1.7))
set color 255, 255, 255, alpha
sx = int((150 + s * 73 - cam.x * 0.5) % 440)
sy = 14 + (s * 29) % 70 ' deterministic y scatter
sz = 2 + s % 2 ' sizes alternate 2px / 3px
draw rect sx, sy, sz, sz, true
next
set color 255, 255, 255, 255 ' restore full opacity
' LAYER 2: MOON with soft glow (depth 0.1, now draw ellipse)
moonX = int(300 - cam.x * 0.1)
set color 200, 200, 255, 40
draw ellipse moonX, 60, 26, 26, true ' faint glow halo
set color 200, 200, 255
draw ellipse moonX, 60, 16, 16, true ' moon body
set color 170, 170, 225
draw ellipse moonX - 5, 56, 4, 4, true ' craters
draw ellipse moonX + 6, 64, 3, 3, true
' LAYER 3: MOUNTAIN SILHOUETTES via sin() contours (depth 0.2)
' made of TWO blended sine frequencies = natural mountain outline
set color 40, 40, 60
for mx = 0 to 384
wx = mx + cam.x * 0.2 ' world x under this column
mh = 70 + 45 * sin(wx / 45) + 18 * sin(wx / 19)
draw rect mx, int(GROUND_Y - mh), 7, int(mh), true
next
' LAYER 4: TREELINE with varying canopy heights (depth 0.5)
' Same slicing trick, higher frequencies = bumpy treetops
set color 30, 50, 30
for tx = 0 to 384
wx = tx + cam.x * 0.5
th = 28 + 14 * sin(wx / 23) + 9 * sin(wx / 11 + 1)
draw rect tx, int(GROUND_Y - th), 11, int(th), true
next
' LAYER 5: FOREGROUND TREES, varied sizes (depth 0.8)
' Precomputed table + tiled copies every 1000px = infinite forest
set color 20, 30, 20
for j = -1 to 1
for i = 0 to trees.num - 1
fx = int(trees[i].x + j * 1000 - cam.x * 0.8)
if fx > -80 and fx < 460 then
draw rect fx, GROUND_Y - trees[i].h, trees[i].w, trees[i].h, true
endif
next
next
' LAYER 6: Ground = player layer (depth 1.0 - "real" world)
set color 60, 40, 20; draw rect int(-cam.x), GROUND_Y, 1500, 50, true
' cycle timer proof
set color 255, 255, 255
set caret 8, 8
'wln "Cycle : " + str(int(t)) + " / " + str(int(CYCLE)) + " sec"
wln "cam.x : " + str(int(cam.x))
wln "ESC to quit"
Ok. I asked Grok to create an animated graphical version of Hangman with categories and levels of difficulty.
A few minor tweaks (making sure some variables were global) and it ran without issue. Time taken from request to final tweaks... maybe 10 to 15 minutes...
for i = 0 to count-1
row = i / cols1
col = i % cols1
holes_x[i] = startX + col * (80 + 15)
holes_y[i] = startY + row * (80 + 15)
next
endfunc
function drawHole(x, y)
set color 40,40,40
draw rect x - 36, y - 36, 72, 72, true
endfunc
function drawMole(x, y)
set color 139, 90, 43
draw rect x-25, y-25, 50, 50, true
endfunc
function checkWhack(mx, my)
for i = 0 to holeCount - 1
dx = mx - holes_x[i]
dy = my - holes_y[i]
dist2 = dx * dx + dy * dy
if dist2 < 1600 then ' 40*40
if i = activeMoleIdx and activeMoleIdx >= 0 then
activeMoleIdx = -1
spawnExplosion(holes_x[i], holes_y[i])
score = score + 1
if score % SCORE_PER_LEVEL = 0 and level < MAX_LEVEL then
level = level + 1
createLayout(level)
elseif score % SCORE_PER_LEVEL = 0 and level = MAX_LEVEL then
set color 255,255,255
set caret 10,30
wln "MISSION ACCOMPLISHED"
redraw
temp = rln()
end
endif
' An AI training program in naalaa 7
' ----------------------------------
' Pass this program to Gemini to help it understand and use the language.
' Constants, visible in entire program but can't change values.
constant DIR_NONE = 0, DIR_LEFT = 1, DIR_RIGHT = 2, DIR_UP = 3, DIR_DOWN = 4
' Visible (global) variables, can be accessed in functions.
visible vObjects
' Use 'set window title, w, h, fullscreen, scale' to create a 320x240 pixels large window, visually
' upscaled by 2 for crispy pixels.
set window "Trainer", 320, 240, false, 2
' Turn off automatic redraw, meaning that we will have to call 'redraw' to manually copy the game's
' back buffer to the window.
set redraw off
' Create 100 moving objects and put them in vObjects.
vObjects = []
for i = 1 to 100
if rnd(2) = 0
obj = MovingRectangle(rnd(320), rnd(240), 16 + rnd(16), 16 + rnd(16))
else
obj = MovingEllipse(rnd(320), rnd(240), 16 + rnd(16), 16 + rnd(16))
endif
vObjects[sizeof(vObjects)] = obj
obj.SetDir(rnd(1, 4))
obj.SetSpeed(0.25 + rnd()*4.75)
next
' Control a crosshair.
crossHairX = 160
crossHairY = 120
' Define a polygon in the form [x0, y0, x1, y1 .. xn, yn], with its origin at (0, 0) that we can
' draw anywhere on the screen.
starPolygon = [0, -32, 8, -8, 32, 0, 8, 8, 0, 32, -8, 8, -32, 0, -8, -8]
starAngle = 0 ' The rotation angle of our star polygon.
starAngleSpeed = 4 ' The speed of rotation.
' Loop until user presses the escape key.
while not keydown(KEY_ESCAPE)
' Update objects.
if sizeof(vObjects)
for i = sizeof(vObjects) - 1 to 0
if not vObjects[i].Update()
free key vObjects, i
endif
next
endif
' Move crosshair.
if keydown(KEY_LEFT) crossHairX = max(crossHairX - 2, 0)
if keydown(KEY_RIGHT) crossHairX = min(crossHairX + 2, 319)
if keydown(KEY_UP) crossHairY = max(crossHairY - 2, 0)
if keydown(KEY_DOWN) crossHairY = min(crossHairY + 2, 240)
' Change rotation direction of the star with left click. Setting the second parameter for
' 'mousebutton' to true means that the function won't return true until the the mouse button has
' been released and pressed again.
if mousebutton(0, true) starAngleSpeed = -starAngleSpeed
' Update star angle. The modulo operator (%) performs mathematical modulo, meaning that it
' handles negative numbers correctly.
starAngle = (starAngle + starAngleSpeed)%360
' Clear screen black.
set color 0, 0, 0
cls
' 'foreach' doesn't guarantee any specific order, so the objects will seem to change z order
' when the vObjects shrinks. I'm using 'foreach' just to show how it works :)
foreach o in vObjects
o.Draw()
next
' Draw a transparent blue gradient at the top.
for y = 0 to 63
set color 32, 64, 255, 255*(63 - y)/64
draw line 0, y, 319, y
next
' Draw red crosshair.
set color 255, 0, 0
draw line crossHairX, crossHairY - 8, crossHairX, crossHairY + 8
draw line crossHairX - 8, crossHairY, crossHairX + 8, crossHairY
' Draw transparent star polygon at mouse cursor position using 'draw poly xform points, x, y,
' scaleX, scaleY, angle, pivotX, pivotY, filled'.
set color 255, 255, 255, 128
mx = mousex(); my = mousey()
draw poly xform starPolygon, mx, my, 1, 1, rad(starAngle), 0, 0, true
' 'draw poly points, filled' draws the polygon defined by 'points' without any
' transformations. So only the bottom left quarter of the star will be visible, at the top right
' corner of the window, when we use this command.
draw poly starPolygon, true
' Write some info at the bottom left corner using the default fixed size font (all characters
' are 8x16 pixels large).
set color 255, 255, 255
set caret 0, 208
' 'wln txt' writes a text string and jumps to a new line.
wln "Mouse position: " + mx + ", " + my
' 'write txt' writes a string without jumping to a new line. All numbers are floating points,
' use 'floor' or 'int' to remove the fractional part.
write "Star angle: " + floor(starAngle)
' Copy backbuffer to window and use 'fwait' to cap fps at 60.
redraw
fwait 60
wend
' Create and return a rectangle that can move.
function MovingRectangle(centerX, centerY, w, h)
' Create object and set properties.
mr = [dir: DIR_NONE,
x: centerX - w/2, y: centerY - h/2,
w: w, h: h,
dx: 0, dy: 0,
speed: 1, bouncesLeft: 5,
r: rnd(256), g: rnd(256), b: rnd(255)]
' Add some functions to object.
mr.SetDir = function(dir)
this.dir = dir
select dir
case DIR_LEFT
this.dx = -1; this.dy = 0
case DIR_RIGHT
this.dx = 1; this.dy = 0
case DIR_UP
this.dx = 0; this.dy = -1
case DIR_DOWN
this.dx = 0; this.dy = 1
default
this.dx = 0; this.dy = 0
endsel
endfunc
mr.Draw = function()
set color this.r, this.g, this.b
' Use 'draw rect x, y, w, h, filled' to draw a filled rectangle.
draw rect this.x, this.y, this.w, this.h, true
endfunc
return mr
endfunc
' Create and return a moving ellipse.
function MovingEllipse(x, y, w, h)
' Base on moving rectangle.
me = MovingRectangle(x, y, w, h)
' Replace the Draw function.
me.Draw = function()
set color this.r, this.g, this.b
' Use 'draw ellipse centerX, centerY, radiusX, radiusY, filled' to draw a filled ellipse.
draw ellipse this.x + this.w/2, this.y + this.h/2, this.w/2, this.h/2, true
endfunc
return me
endfunc
' Return true if the bounding rectangle of objects a and b overlaps. Tables/arrays are always passed
' by reference.
function Overlaps(a, b)
return a.x + a.w > b.x and a.x < b.x + b.w and a.y + a.h > b.y and a.y < b.y + b.h
endfunc
Then I told it to be "creative" and create two games. Here's the first one, where it re-used very much from the training program, still quite fun:
Code:
' Neon Star Eater - Commented Educational Version
' --------------------------------------------------
' 'constant' defines values that cannot change while the program runs.
' These are visible everywhere automatically.
constant DIR_NONE = 0, DIR_LEFT = 1, DIR_RIGHT = 2, DIR_UP = 3, DIR_DOWN = 4
constant STATE_PLAYING = 0, STATE_GAMEOVER = 1
' 'visible' declares global variables. Without this, variables created in
' the main loop or functions are local to that specific scope.
' Using a 'v' prefix is a great convention to easily identify them!
visible vEnemies
visible vFood
visible vScore
visible vGameState
visible vStarPoly
visible vStarAngle
visible vStarAngleSpeed
' Create a window: title, width, height, fullscreen (false), scale (2x)
set window "Neon Star Eater", 320, 240, false, 2
' Turn off automatic redraw so we can draw everything in the background
' and flip it to the screen all at once (double buffering) to prevent flickering.
set redraw off
' Initialize the player's shape as a list of x, y coordinates forming a polygon.
vStarPoly = [0, -12, 4, -4, 12, 0, 4, 4, 0, 12, -4, 4, -12, 0, -4, -4]
vStarAngle = 0
vStarAngleSpeed = 4
' Call our setup function to set the initial game state
ResetGame()
' --- MAIN GAME LOOP ---
' This loop runs continuously until the user presses the Escape key.
while not keydown(KEY_ESCAPE)
' Clear the screen to black at the start of every frame
set color 0, 0, 0
cls
' Handle logic and drawing based on the current game state
if vGameState = STATE_PLAYING
UpdatePlaying()
DrawPlaying()
elseif vGameState = STATE_GAMEOVER
UpdateGameOver()
DrawGameOver()
endif
' Copy our background drawings to the actual window
redraw
' Cap the frame rate at 60 Frames Per Second
fwait 60
wend
' --- GAME STATE FUNCTIONS ---
function ResetGame()
vScore = 0
vGameState = STATE_PLAYING
' Empty tables (arrays) to hold our game objects
vEnemies = []
vFood = []
' Spawn initial food using a 'for' loop
for i = 1 to 3
SpawnFood()
next
endfunc
function SpawnEnemy()
' Get current mouse position to know where the player is
mx = mousex(); my = mousey()
safe = false
ex = 0
ey = 0
' Keep picking random coordinates until we find one far enough from the player
while not safe
ex = rnd(320)
ey = rnd(240)
' Calculate the squared distance (a^2 + b^2 = c^2).
' We skip the square root to save processing power!
distSq = (ex - mx)*(ex - mx) + (ey - my)*(ey - my)
' 10000 means the enemy must spawn at least 100 pixels away (100^2 = 10000)
if distSq > 10000
safe = true
endif
wend
' Create a new enemy object and set its properties
obj = MovingRectangle(ex, ey, 12 + rnd(12), 12 + rnd(12))
obj.SetDir(rnd(1, 4))
obj.SetSpeed(1.0 + rnd()*2.0)
obj.r = 255; obj.g = 32; obj.b = 32 ' RGB color: Bright Red
' Add the new object to the end of the vEnemies array.
' sizeof() gets the current number of items in the array.
vEnemies[sizeof(vEnemies)] = obj
endfunc
function UpdatePlaying()
mx = mousex(); my = mousey()
' Rotate the star. The modulo (%) operator keeps the angle between 0 and 359
vStarAngle = (vStarAngle + vStarAngleSpeed)%360
' Create a temporary table representing the player's bounding box for collisions
playerBox = [x: mx - 8, y: my - 8, w: 16, h: 16]
' --- ENEMY UPDATE ---
if sizeof(vEnemies)
' Loop backwards! When removing items from an array inside a loop,
' looping backwards prevents index shifting errors.
for i = sizeof(vEnemies) - 1 to 0
vEnemies[i].Update()
if Overlaps(playerBox, vEnemies[i])
vGameState = STATE_GAMEOVER
endif
next
endif
' --- FOOD UPDATE ---
if sizeof(vFood)
for i = sizeof(vFood) - 1 to 0
vFood[i].Update()
if Overlaps(playerBox, vFood[i])
vScore = vScore + 10
' 'free key' removes an item from an array at the specified index
free key vFood, i
SpawnFood()
' The modulo (%) operator checks if the score is perfectly divisible by 50
if vScore % 50 = 0
SpawnEnemy()
endif
endif
next
endif
endfunc
function DrawPlaying()
' 'foreach' is a clean way to loop through an array when you don't need the index
foreach e in vEnemies
e.Draw()
next
foreach f in vFood
f.Draw()
next
' Set color to Yellow with 200 alpha (slight transparency)
set color 255, 255, 0, 200
mx = mousex(); my = mousey()
' Draw the player polygon.
' xform allows scaling (1, 1) and rotation. 'rad()' converts degrees to radians.
' The last parameter (true) means the polygon will be filled.
draw poly xform vStarPoly, mx, my, 1, 1, rad(vStarAngle), 0, 0, true
' Draw UI Score text at coordinates x:4, y:4
set color 255, 255, 255
set caret 4, 4
write "SCORE: " + vScore
endfunc
function UpdateGameOver()
' Wait for the player to press SPACE to restart
if keydown(KEY_SPACE)
ResetGame()
endif
endfunc
function DrawGameOver()
DrawPlaying() ' Keep the game visible in the background
' Draw a dark, translucent rectangle over the whole screen (0,0 to 320,240)
set color 0, 0, 0, 180
draw rect 0, 0, 320, 240, true
' Draw Game Over text
set color 255, 50, 50
set caret 120, 100
write "GAME OVER"
set color 255, 255, 255
set caret 95, 130
write "Final Score: " + vScore
set color 200, 200, 200
set caret 70, 160
write "Press SPACE to Restart"
endfunc
' --- CORE OBJECT CLASSES ---
' This function acts like a "Class Constructor" in other languages.
' It creates a table with properties and attaches functions to it.
function MovingRectangle(centerX, centerY, w, h)
' Create the base table (object) with its properties
mr = [dir: DIR_NONE,
x: centerX - w/2, y: centerY - h/2,
w: w, h: h, dx: 0, dy: 0,
speed: 1,
r: 255, g: 255, b: 255]
' Attach a method to the object.
' 'this' refers to the specific table that owns the function.
mr.SetDir = function(dir)
this.dir = dir
' 'select/case' is a cleaner way to write multiple 'if/elseif' statements
select dir
case DIR_LEFT
this.dx = -this.speed; this.dy = 0
case DIR_RIGHT
this.dx = this.speed; this.dy = 0
case DIR_UP
this.dx = 0; this.dy = -this.speed
case DIR_DOWN
this.dx = 0; this.dy = this.speed
default
this.dx = 0; this.dy = 0
endsel
endfunc
mr.SetSpeed = function(spd)
this.speed = spd
this.SetDir(this.dir) ' Update dx/dy based on the new speed
endfunc
' Bounce off the screen edges
if this.dir = DIR_LEFT and this.x < 0
this.SetDir(DIR_RIGHT)
elseif this.dir = DIR_RIGHT and this.x > 320 - this.w
this.SetDir(DIR_LEFT)
endif
if this.dir = DIR_UP and this.y < 0
this.SetDir(DIR_DOWN)
elseif this.dir = DIR_DOWN and this.y > 240 - this.h
this.SetDir(DIR_UP)
endif
return true
endfunc
mr.Draw = function()
set color this.r, this.g, this.b
draw rect this.x, this.y, this.w, this.h, true
endfunc
' Return the fully constructed object
return mr
endfunc
' Inheritance! This creates a MovingEllipse by first creating a MovingRectangle,
' and then overriding its Draw function.
function MovingEllipse(x, y, w, h)
me = MovingRectangle(x, y, w, h)
' Override the default rectangle drawing method
me.Draw = function()
set color this.r, this.g, this.b
' Ellipses are drawn from their center point, so we add half the width/height
draw ellipse this.x + this.w/2, this.y + this.h/2, this.w/2, this.h/2, true
endfunc
return me
endfunc
' Axis-Aligned Bounding Box (AABB) collision detection.
' Checks if the right side of A is past the left side of B, etc.
function Overlaps(a, b)
return a.x + a.w > b.x and a.x < b.x + b.w and a.y + a.h > b.y and a.y < b.y + b.h
endfunc
, and here's the second one, after I told gemini it needn't re-use that much of the code:
Code:
' Neon Herder - An Anomaly Containment Game
' ----------------------------------------------------
' Goal: Herd all the yellow wandering anomalies into the center blue core using the mouse.
' Mechanics: The mouse emits a repulsion field. Anomalies flee when the mouse gets close.
set window "Neon Herder", 320, 240, false, 2
set redraw off
' Global variables
visible vAnomalies
visible anomaliesLeft
vAnomalies = []
anomaliesLeft = 50
' Generate 50 wandering anomalies
for i = 1 to 50
' Create anomaly object
anomaly = [x: rnd(320), y: rnd(240), dx: 0.0, dy: 0.0, captured: false]
' Object method to handle physics and AI
anomaly.Update = function(mx, my)
if this.captured
' Gently settle into the core once captured
this.x = this.x + (160 - this.x) * 0.05
this.y = this.y + (120 - this.y) * 0.05
return
endif
' Repulsion from the mouse cursor
' We use distance squared to avoid needing a heavy square root calculation
distSq = (this.x - mx)*(this.x - mx) + (this.y - my)*(this.y - my)
' Check if anomaly has been corralled into the core (radius 20)
distToCore = (this.x - 160)*(this.x - 160) + (this.y - 120)*(this.y - 120)
if distToCore < 400
this.captured = true
anomaliesLeft = anomaliesLeft - 1
endif
endfunc
' Object method to draw the anomaly
anomaly.Draw = function()
if this.captured
set color 0, 255, 255 ' Cyan when captured
else
set color 255, 255, 0 ' Yellow when wild
endif
' Draw as a crisp 3x3 pixel
draw rect this.x - 1, this.y - 1, 3, 3, true
endfunc
' Append to our global array
vAnomalies[sizeof(vAnomalies)] = anomaly
next
' Main Game Loop
while not keydown(KEY_ESCAPE)
mx = mousex()
my = mousey()
' Clear screen to a dark, moody blue-black
set color 10, 10, 20
cls
' Draw the containment core in the center
set color 0, 100, 255, 100
draw ellipse 160, 120, 20, 20, true
set color 0, 200, 255
draw ellipse 160, 120, 20, 20, false
' Draw the player's repulsion field aura
set color 255, 50, 50, 30
draw ellipse mx, my, 50, 50, true
set color 255, 50, 50, 128
draw ellipse mx, my, 50, 50, false
' Update and draw all anomalies
foreach a in vAnomalies
a.Update(mx, my)
a.Draw()
next
' UI / HUD
set color 255, 255, 255
set caret 4, 4
if anomaliesLeft > 0
write "Uncontained: " + anomaliesLeft
else
set color 0, 255, 0
write "CONTAINMENT COMPLETE!"
endif