Welcome, Guest
You have to register before you can post on our site.

Username
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 52
» Latest member: Rick
» Forum threads: 377
» Forum posts: 2,802

Full Statistics

Online Users
There are currently 338 online users.
» 0 Member(s) | 333 Guest(s)
Applebot, Baidu, Bing, DuckDuckGo, Google

Latest Threads
Space Pooper (N7)
Forum: NaaLaa 7 Code
Last Post: johnno56
09-03-2026, 09:27 AM
» Replies: 36
» Views: 1,924
Terrible Sokoban levels b...
Forum: AI "Slop"
Last Post: aurel
08-27-2026, 06:08 PM
» Replies: 4
» Views: 469
Joystick vs Background Mu...
Forum: NaaLaa 7 Questions
Last Post: 1micha.elok
08-26-2026, 05:06 AM
» Replies: 3
» Views: 618
Autostereogram generator?
Forum: NaaLaa 6 Questions
Last Post: Marcus
08-20-2026, 09:44 AM
» Replies: 2
» Views: 333
3D Breakout, just a quick...
Forum: NaaLaa 7 Code
Last Post: Marcus
08-13-2026, 09:42 AM
» Replies: 11
» Views: 11,493
Parallax
Forum: AI "Slop"
Last Post: Marcus
08-06-2026, 06:53 AM
» Replies: 3
» Views: 1,381
Hangman
Forum: AI "Slop"
Last Post: johnno56
08-02-2026, 02:58 AM
» Replies: 5
» Views: 470
Whack-A-Square
Forum: NaaLaa 7 Code
Last Post: 1micha.elok
08-02-2026, 01:12 AM
» Replies: 8
» Views: 1,961
Flip the Classic Jam for ...
Forum: Programming
Last Post: aurel
07-25-2026, 06:47 PM
» Replies: 9
» Views: 1,419
Nova Editor
Forum: NaaLaa 7 Code
Last Post: Nelson
07-06-2026, 01:11 PM
» Replies: 21
» Views: 3,570

 
  Terrible Sokoban levels by Gemini
Posted by: Marcus - 08-26-2026, 04:16 PM - Forum: AI "Slop" - Replies (4)

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

CreateAssets()

levels = [
        "                " +
        "                " +
        "                " +
        "      #####     " +
        "      #   #     " +
        "      # . #     " +
        "      # $ #     " +
        "      # @ #     " +
        "      #   #     " +
        "      #####     " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "                " +
        "     #######    " +
        "     #     #    " +
        "     # . . #    " +
        "     # $ $ #    " +
        "     #  @  #    " +
        "     #     #    " +
        "     #######    " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "                " +
        "     ########   " +
        "     #      #   " +
        "     # . ## #   " +
        "     # . $  #   " +
        "     #   $@ #   " +
        "     #      #   " +
        "     ########   " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "                " +
        "    #########   " +
        "    # .   . #   " +
        "    #   $   #   " +
        "    #   $   #   " +
        "    #   @   #   " +
        "    #########   " +
        "                " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "    #######     " +
        "    #     #     " +
        "    #  .  #     " +
        "    ## $  #     " +
        "    ## $ ##     " +
        "    #  @  #     " +
        "    #  .  #     " +
        "    #######     " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "                " +
        "      #######   " +
        "      #     #   " +
        "    ###  $  ##  " +
        "    # . $    #  " +
        "    # .   @  #  " +
        "    ##########  " +
        "                " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "                " +
        "      ######    " +
        "      #    #    " +
        "      # $. ###  " +
        "      # $  . #  " +
        "      ## @   #  " +
        "       #######  " +
        "                " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "    ######      " +
        "    #    #      " +
        "  ### ## ###    " +
        "  # .  .. .#    " +
        "  # $  $  ##    " +
        "  ## $  $ #     " +
        "   #  @   #     " +
        "   ########     " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "    #######     " +
        "    #     #     " +
        "    # .$. #     " +
        "    ## $ ##     " +
        "    ## @ ##     " +
        "    #  $  #     " +
        "    #  .  #     " +
        "    #######     " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "     #####      " +
        "     #   #      " +
        "   ### $ ###    " +
        "   #   .   #    " +
        "   # $   $ #    " +
        "   ### . ###    " +
        "     # . #      " +
        "     #@  #      " +
        "     #####      " +
        "                " +
        "                ",
        "                " +
        "                " +
        "   ########     " +
        "   #   .  #     " +
        "   # $    #     " +
        "   #  ##  #     " +
        "   #  .   #     " +
        "   #### $ #     " +
        "      #   #     " +
        "      #@  #     " +
        "      #####     " +
        "                " +
        "                ",
        "                " +
        "                " +
        "     ######     " +
        "     #    #     " +
        "   ###  $ ###   " +
        "   #  ...   #   " +
        "   #  $$    #   " +
        "   ###  @ ###   " +
        "     #    #     " +
        "     ######     " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "                " +
        "   #######      " +
        "   #  .  #      " +
        "   # $   #      " +
        "   #  $..#      " +
        "   ## $ ##      " +
        "    #  @#       " +
        "    #####       " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "    ######      " +
        "    #    #      " +
        "    #  $ #      " +
        "  ### #  ###    " +
        "  #  . . . #    " +
        "  # $  $  ##    " +
        "  ##   @   #    " +
        "   #########    " +
        "                " +
        "                " +
        "                ",
        "                " +
        "    #######     " +
        "    #  .  #     " +
        "    # $   #     " +
        "  ### ### ###   " +
        "  # .  .  . #   " +
        "  #   $$$   #   " +
        "  #   @ ### #   " +
        "  ###########   " +
        "                " +
        "                " +
        "                " +
        "                ",
        "                " +
        "                " +
        "   ##########   " +
        "   #    # . #   " +
        "   #  $ #   #   " +
        "   ## $ #   #   " +
        "    # .   . #   " +
        "    ## $$  ##   " +
        "     #  @  #    " +
        "     #  .  #    " +
        "     #######    " +
        "                " +
        "                "]

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

Print this item

  Joystick vs Background Music
Posted by: 1micha.elok - 08-24-2026, 09:11 AM - Forum: NaaLaa 7 Questions - Replies (3)

Hi Marcus,

In the Space Pooper (N7 version) that I am currently working on, I’ve run into something that I can’t seem to resolve 'Joystick problem' by myself.

My joystick responds correctly when this part of the background music code is not included. I believe I may be doing something wrong in my coding.

Code:
' Background music
'title = loadmusic("assets/title.mp3")
'play music title, true

If you have some free time when you’re not at work, would you mind taking a look at my code? Please don’t worry about it—it’s not urgent at all.

Thank you very much in advance for your time and help.

Code:
'===================================
' Joystick Demo
' - stage 2
' - joystick support (LEFT, RIGHT)
'===================================

include "tilemap.n7"

#win32
set window "Joystick Demo", 256, 240, false, 2
set redraw off
randomize time()

' Variables
visible player = []
visible scrollY , scrollTimer 'scrolling tilemap
visible title

' Background music
'title = loadmusic("assets/title.mp3")
'play music title, true

' Init Player
player.img = loadimage("assets/player.png") ;set image colorkey player.img, 255, 0, 255 ; set image grid player.img, 2, 1
player.x = (width() / 2) - (width(player.img) / 2)
player.y = 180
player.dx = 0
player.cel = 0
player.tickCounter = 0

' Init Tilemap
ResetTilemap()


'------------
' Main Loop
'------------
while not keydown(KEY_ESCAPE, true)
    ' Tilemap vertical scrolling
    scrollTimer = (scrollTimer + 1) % 4
    if scrollTimer = 0 then
        scrollY = scrollY - 1
    endif

    ' Keyboard & Joystick Input
    if keydown(KEY_LEFT)  player.dx = player.dx - 0.4
    if keydown(KEY_RIGHT) player.dx = player.dx + 0.4
    player.dx = player.dx + joyx() * 0.4
   
    player.dx = player.dx * 0.9
    player.x = player.x + player.dx

    ' Boundaries
    if player.x < 0 then player.x = 0
    if player.x + width(player.img) > width() then player.x = width() - width(player.img)

    ' Player Animation
    player.tickCounter = (player.tickCounter + 1) % 8
    if player.tickCounter = 0 then player.cel = 1 - player.cel

    ' Clear screen
    set color 0, 0, 0 ; cls ; set color 255, 255, 255
   
    ' Tilemap camera
    TM_SetCamera(int(player.x) * 32 / width(), scrollY)
    TM_Render()

    ' Draw Player
    draw image player.img, player.x, player.y, player.cel

    ' Draw Heads-Up-Display
    set caret width() / 2, 4 ; center "SCORE"
    set justification left ; set caret 4, 4 ; wln "SHIPS"
    set justification right ;set caret width() - 4, 4; wln "STAGE" ; wln "2"

    redraw
    fwait 60
wend


'------------
' Functions
'------------
function ResetTilemap()
    if TM_LoadMap("assets/2.txt") = 0 then end
    scrollY = TM_MapHeight() * 16 - height()
    scrollTimer = 0
    TM_SetView(0, 0, width(), height())
    TM_SetCamera(16, scrollY)
endfunc



Attached Files
.zip   joystick_test.zip (Size: 422.15 KB / Downloads: 2)
Print this item

  Autostereogram generator?
Posted by: Marcus - 08-20-2026, 07:20 AM - Forum: NaaLaa 6 Questions - Replies (2)

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.

Print this item

  Space Pooper (N7)
Posted by: 1micha.elok - 08-13-2026, 09:12 AM - Forum: NaaLaa 7 Code - Replies (36)

[Image: bnwsf.jpg]

Do you still remember https://naalaa.com/forum/thread-9.html Space Pooper N6 by JSM (John Master) ?

What if ... there's an N7 version coming ?
Could Space Pooper N7 be real ?

Stay tuned ... the countdown has begun

Print this item

  Parallax
Posted by: 1micha.elok - 08-04-2026, 09:00 AM - Forum: AI "Slop" - Replies (3)

Parallax in game dev is a visual trick where background layers move slower than the stuff up front to make a flat 2D game look deep....

Code:
'====================================================
' PARALLAX AUTO-SCROLL DEMO v2 - 3 minute cycle
' - blinking stars (sin-driven alpha twinkle)
' - moon drawn with draw ellipse + glow halo
' - mountain silhouettes from sin() contours
' - treeline & trees with varied heights
'====================================================

set window "Parallax Demo", 384, 224, false, 2
set redraw off

constant SCROLL_SPEED = 90.0    ' camera pixels per frame
constant WORLD_W = 1535.0       ' one-way travel distance
visible CYCLE = 4 * WORLD_W / SCROLL_SPEED  ' = 180 seconds
constant GROUND_Y = 180

visible cam = [] ; cam.x = 0

' --- 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

    ' ==================== DRAWING ====================

    ' 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"

    redraw
    fwait 60
until keydown(KEY_ESCAPE)

Edited to make the mountains smoother

Print this item

  Hangman
Posted by: johnno56 - 07-29-2026, 09:27 AM - Forum: AI "Slop" - Replies (5)

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...


.n7   N7hangman.n7 (Size: 12.46 KB / Downloads: 2)

           

Print this item

  Whack-A-Square
Posted by: 1micha.elok - 07-28-2026, 07:28 AM - Forum: NaaLaa 7 Code - Replies (8)

Just a simple "Whack-A-Squre" game ...

Code:
'===================
' Whack-A-Square
'===================

set window "Whack-A-Square", 300,300,false,2
set redraw off
set mouse off 
randomize time()

constant SCORE_PER_LEVEL = 3
constant MAX_LEVEL = 6

visible score, level, isPlaying, activeMoleIdx
visible popDelayTimer, moleUpFrames
visible holes_x=[], holes_y=[], holeCount

visible px=[], py=[], pvx=[], pvy=[], plife=[]
visible particleCount

level = 1
isPlaying = 1
moleUpFrames = 0
particleCount = 0

createLayout(1)

'-----------
' MAIN LOOP
'-----------
while not keydown(KEY_ESCAPE, true)
    mx = mousex()
    my = mousey()
    click = mousebutton(0,true) 'left click
   
    if isPlaying then
        if activeMoleIdx >= 0 then
            moleUpFrames = moleUpFrames + 1
            duration = (1000 - level * 80) / 16.67
            if duration < 350 / 16.67 then duration = 350 / 16.67
            if moleUpFrames >= duration then
                activeMoleIdx = -1
                popDelayTimer = (800 - level * 50) / 16.67
                if popDelayTimer < 200 / 16.67 then popDelayTimer = 200 / 16.67
            endif
        else
            if popDelayTimer > 0 then
                popDelayTimer = popDelayTimer - 1
            else
                activeMoleIdx = rnd(holeCount)
                moleUpFrames = 0
            endif
        endif
    endif
   
    whacked = false
    if click and isPlaying then whacked = checkWhack(mx,my)
   
    ' Grass background
    set color 39,174,96; cls
   
    ' UI
    set color 255,255,255
    set caret 10,10; write level
   
    for i = 0 to holeCount - 1
        drawHole(holes_x[i], holes_y[i])
    next
   
    if not whacked and activeMoleIdx >= 0 then
        drawMole(holes_x[activeMoleIdx], holes_y[activeMoleIdx])
    endif
   
    ' Cursor
    set color 255, 0, 0, 150
    draw rect mx - 8, my - 8, 50, 50, true
   
    updateParticles()
    drawParticles()
       
    redraw
    fwait 60
wend

'-----------
' FUNCTIONS
'-----------
function createLayout(count)
    holeCount = count
    holes_x=[]
    holes_y=[]
   
    cols1 = 1
    if count > 1 and count <= 4 then cols1 = 2
    if count > 4 then cols1 = 3
   
    rows1 = (count + cols1 - 1) / cols1
    totalW = cols1 * 80 + (cols1 - 1) * 15
    totalH = rows1 * 80 + (rows1 - 1) * 15
    startX = (320 - totalW) / 2 + 40
    startY = (320 - totalH) / 2 + 40
   
    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
               
                ' timing reset
                moleUpFrames = 0
                popDelayTimer = (800 - level * 50) / 16.67
                if popDelayTimer < 200 / 16.67 then popDelayTimer = 200 / 16.67
               
                return true
            endif
        endif
    next
    return false
endfunc

function spawnExplosion(x, y)
    count = 16 ' Fixed burst count
    max_particles = 32
    for i = 0 to count - 1
        if particleCount >= max_particles then break
        idx = particleCount
        particleCount = particleCount + 1
       
        px[idx] = x
        py[idx] = y
        pvx[idx] = (rnd(101) - 50) / 12
        pvy[idx] = (rnd(101) - 50) / 12
        plife[idx] = 25
    next
endfunc

function updateParticles()
    i = 0
    while i < particleCount
        plife[i] = plife[i] - 1
        px[i] = px[i] + pvx[i]
        py[i] = py[i] + pvy[i]
        pvy[i] = pvy[i] + 0.25  ' Gravity
        pvx[i] = pvx[i] * 0.96  ' Air resistance

        if plife[i] <= 0 then
            particleCount = particleCount - 1
            if i < particleCount then
                px[i] = px[particleCount]
                py[i] = py[particleCount]
                pvx[i] = pvx[particleCount]
                pvy[i] = pvy[particleCount]
                plife[i] = plife[particleCount]
            endif
        else
            i = i + 1
        endif
    wend
endfunc

function drawParticles()
    particle_size = 8
   
    if particleCount <= 0 then return
   
    set color 220, 130, 50
    half = particle_size / 2
   
    for i = 0 to particleCount - 1
        draw rect int(px[i]) - half, int(py[i]) - half, particle_size, particle_size, true
    next
endfunc

Print this item

  Flip the Classic Jam for BASIC Programmers
Posted by: Marcus - 07-15-2026, 09:47 AM - Forum: Programming - Replies (9)

I just got a mail about this jam:

https://itch.io/t/6614044/flip-the-class...rogrammers

I don't know if n7 is an accepted language though.

Print this item

  Telling Gemini to be creative
Posted by: Marcus - 07-04-2026, 08:39 AM - Forum: AI "Slop" - Replies (1)

Gave gemini this training program:

Code:
' 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.Stop = function()
        this.SetDir(DIR_NONE)
    endfunc
   
    mr.GetDir = function()
        return this.dir
    endfunc
   
    mr.SetSpeed = function(spd)
        this.spd = spd
    endfunc
   
    mr.GetSpeed = function()
        return this.spd
    endfunc

    mr.Update = function()
        ' Move.
        this.x = this.x + this.dx
        this.y = this.y + this.dy
        ' Turn on screen edges.
        if this.dir = DIR_LEFT and this.x < 0
            this.SetDir(DIR_RIGHT)
            this.bouncesLeft = this.bouncesLeft - 1
        elseif this.dir = DIR_RIGHT and this.x > 320
            this.SetDir(DIR_LEFT)
            this.bouncesLeft = this.bouncesLeft - 1
        endif
        if this.dir = DIR_UP and this.y < 0
            this.SetDir(DIR_DOWN)
            this.bouncesLeft = this.bouncesLeft - 1
        elseif this.dir = DIR_DOWN and this.y > 240
            this.SetDir(DIR_UP)
            this.bouncesLeft = this.bouncesLeft - 1
        endif
        return this.bouncesLeft > 0
    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 SpawnFood()
    obj = MovingEllipse(rnd(320), rnd(240), 10, 10)
    obj.SetDir(rnd(1, 4))
    obj.SetSpeed(0.5 + rnd()*1.5)
    obj.r = 32; obj.g = 255; obj.b = 32 ' RGB color: Bright Green
    vFood[sizeof(vFood)] = 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

    mr.Update = function()
        ' Move the object
        this.x = this.x + this.dx
        this.y = this.y + this.dy
       
        ' 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

        ' Brownian motion (random wandering)
        this.dx = this.dx + (rnd() * 2 - 1) * 0.5
        this.dy = this.dy + (rnd() * 2 - 1) * 0.5

        ' Apply friction to prevent infinite speed
        this.dx = this.dx * 0.9
        this.dy = this.dy * 0.9

        ' 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)
       
        if distSq < 2500 ' 50 pixel radius
            ' Flee from the mouse!
            this.dx = this.dx + (this.x - mx) * 0.03
            this.dy = this.dy + (this.y - my) * 0.03
        endif

        ' Apply velocity
        this.x = this.x + this.dx
        this.y = this.y + this.dy

        ' Wall collisions (bounce)
        if this.x < 0
            this.x = 0; this.dx = -this.dx
        elseif this.x > 319
            this.x = 319; this.dx = -this.dx
        endif
       
        if this.y < 0
            this.y = 0; this.dy = -this.dy
        elseif this.y > 239
            this.y = 239; this.dy = -this.dy
        endif

        ' 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

    ' Render and cap at 60 FPS
    redraw
    fwait 60
wend

Print this item

  Sudoku
Posted by: Marcus - 07-02-2026, 03:47 PM - Forum: AI "Slop" - Replies (4)

Here's a sudoku game! I did write all the ui stuff, so it's not 100% ai slop, but gemini did the puzzle generator, graphics and music.

   



Attached Files
.zip   ai_slop_sudoku.zip (Size: 3.69 MB / Downloads: 5)
Print this item