Log In  

Yes! If you remember, back in January I had posted a code snippet that would let you play Minesweeper in the Picotron Playground. I later updated that snippet to have some comments describing the settings needed to recreate the three difficulty levels, but otherwise nothing changed. Well now, after months of (not that) hard (but still a little tricky) work, the third version of my Minesweeper game on Picotron is OUT! New features include:

  • Easy windowed mode: Running the program will instantly switch to the desktop. No more faffing about with run_program_inside_terminal! Who wants to type that out every single time?
  • A help screen: Just click the handy little blue question mark (?) and you can reference the rules anytime. Also shows up the first time the program is run.
  • In-game minefield customization: Nobody wants to edit the code every time just to change the difficulty. Nobody.
  • Highscores: Though they can't be saved between sessions, they CAN be saved if you close the program and re-run it without refreshing the browser tab, and it should work on the executable version of Picotron when that's released.
  • A screenshot button: Though it sucks and I wouldn't use it, since the BBS won't see colors #16-31 as anything other than black, so you can't post them here. :/

HOW TO GET STARTED

First, copy the following hidden block of code: (The -- end of program line should be #702)

-- Minesweeper (v3)
-- By: Kai
-- Other things I might want to do:
-- o Unique font for 7-seg displays? (Dunno how to use custom fonts on Picotron yet...)
-- o Release to SPLORE. (When Picotron is released, obviously.)
-- o Make the screenshot function return a PNG file instead? (If it's possible...)
-- o Maybe online time submissions? (Again, if it's possible...)
--
-- GFX decoder
function decode(str) -- necessary so the forum doesn't screw up the code
    return userdata(chr(91,103,102,120,93)..str..chr(91,47,103,102,120,93))
end
--
-- GFX: faces
smile=decode"101077777777777777767666666666666665766660000006666576660aaaaaa066657660aaaaaaaa0665760aa00aa00aa065760aa00aa00aa065760aa00aa00aa065760aaaaaaaaaa065760aaaaaaaaaa065760aa0aaaa0aa0657660aa0000aa066576660aaaaaa06665766660000006666576666666666666656555555555555555"
curious=decode"101077777777777777767666666666666665766660000006666576660aaaaaa066657660aaaaaaaa0665760aa00aa00aa065760aa00aa00aa065760aa00aa00aa065760aaaaaaaaaa065760aaaaaaaaaa065760aaaa00aaaa0657660aaa00aaa066576660aaaaaa06665766660000006666576666666666666656555555555555555"
frown=decode"101077777777777777767666666666666665766660000006666576660aaaaaa066657660aaaaaaaa0665760aa0aaaa0aa065760aaa0aa0aaa065760aa0aaaa0aa065760aaaaaaaaaa065760aaa0000aaa065760aa0aaaa0aa0657660aaaaaaaa066576660aaaaaa06665766660000006666576666666666666656555555555555555"
cool=decode"101077777777777777767666666666666665766660000006666576660aaaaaa066657660aaaaaaaa0665760a000aa000a0657600000000000065760a000aa000a065760aaaaaaaaaa065760aa0aaaa0aa065760aaa0000aaa0657660aaaaaaaa066576660aaaaaa06665766660000006666576666666666666656555555555555555"
-- GFX: tiles
boom=decode"08088888888m8686868m8855588m8656568m8855588m8686868m8888888mmmmmmmmm"
block=decode"08087777776m7666665m7666665m7666665m7666665m7666665m6555555mmmmmmmmm"
empty=decode"0808kkkkkkkmk444444mk444444mk444444mk444444mk444444mk444444mmmmmmmmm"
flag=decode"08087777776m7668665m7688665m7888665m766i665m76iii65m6555555mmmmmmmmm"
mine=decode"0808kkkkkkkmk646464mk455544mk656564mk455544mk646464mk444444mmmmmmmmm"
falseflag=decode"08087777778m7662685m7622865m7228665m768i665m78iii65m8555555mmmmmmmmm"
-- GFX: numbers
numbercols={12,27,24,16,2,17,13,0} numbers={
decode"08080000000000770000000700000007000000070000007770000000000000000000",
decode"08080000000000777000000070000077700000700000007770000000000000000000",
decode"08080000000000777000000070000007700000007000007770000000000000000000",
decode"08080000000000707000007070000077700000007000000070000000000000000000",
decode"08080000000000777000007000000077700000007000007770000000000000000000",
decode"08080000000000700000007000000077700000707000007770000000000000000000",
decode"08080000000000777000000070000000700000007000000070000000000000000000",
decode"08080000000000777000007070000077700000707000007770000000000000000000"}
-- GFX: buttons
wrench=decode"0808777777767666m6657666m665766mmmm576mmm6657m6m66657mm6666565555555"
help=decode"08087777777676hhhh657hh6hhh57666hhh5766hh66576666665766hh66565555555"
left=decode"0808777777767666mm65766mmm6576mmmm6576mmmm65766mmm657666mm6565555555"
right=decode"08087777777676mm666576mmm66576mmmm6576mmmm6576mmm66576mm666565555555"
pause=decode"08087777777676m66m6576m66m6576m66m6576m66m6576m66m6576m66m6565555555"
fishhook=decode"08087777777676mmmm657m6666m5766666m576mm66m57m6mmm6576mm666565555555"
unusable=decode"0808777777767o6666o576o66o65766oo665766oo66576o66o657o6666o565555555"
cam=decode"080877777776766mm6657mmmmmm57mm66mm57mm66mm57mmmmmm57666666565555555"
--
-- adjustables
-- beginner: 9x9/10
-- intermediate: 16x16/40
-- expert: 30x16/99
-- WARNING!!! Setting the size of the minefield too high will cause Picotron to start
-- flashing the screen, rendering the game virtually unplayable and pose a risk to those
-- vulnerable to said flashing. User discretion is advised.
-- DO NOT ADJUST as of v3. PLEASE keep these at 9x9/10 and
-- use the in-game editor to change these.
width=9 -- min: 9 | max: 59
height=9 -- min: 1 | max: 28
mines=10 -- idealy ~10-20% of total tiles
--
-- other setup
lmb=0
dead=false
won=false
start=true
paused=false
remainingmines=mines
timer=0
set_window(width*8+1,height*8+20)
cheatinput={}
-- highscores setup
highscores_txt=fetch"/best_minesweeper_times.txt"
if not highscores_txt then
    highscores_txt="999,999,999\n999,999,999\n999,999,999"
    store("/best_minesweeper_times.txt",highscores_txt)
    first_time=true
end
highscores_arr=split(highscores_txt,"\n")
for i=1,3 do
    this_diff=split(highscores_arr[i],",")
    for j=1,3 do
        this_diff[j]=tonumber(this_diff[j])
    end
    highscores_arr[i]=this_diff
end
difficulty=1
--
-- data format:
-- bit 0: mine
-- bit 1: uncovered
-- bit 2: flagged
-- bit 3: UNUSED
-- bit 4: UNUSED
-- bit 5: UNUSED
-- bit 6: UNUSED
-- bit 7: UNUSED
--
minefield=userdata("u8",width,height)
--
-- debug: randomized test board
--for x=0,width-1 do
    --for y=0,height-1 do
        --set(minefield,x,y,flr(rnd(2)))
    --end
--end
--dead=true
-- /debug
--
function place_mines(cx,cy)
    local goodcandidates={}
    local nearcandidates={}
    for x=0,width-1 do
        for y=0,height-1 do
            if abs(x-cx)>1 or abs(y-cy)>1 then
                add(goodcandidates,{x,y})
            elseif x~=cx or y~=cy then
                add(nearcandidates,{x,y})
            end
        end
    end
    for i=1,mines do
        --local position=candidates[flr(rnd(#candidates))+1]
        local position
        if #goodcandidates>0 then
            position=deli(goodcandidates,flr(rnd(#goodcandidates))+1)
        else
            position=deli(nearcandidates,flr(rnd(#nearcandidates))+1)
        end
        set(minefield,position[1],position[2],1)
        --del(candidates,position)
    end
end
--
function bit(n,b,c)
    if type(c)=="boolean" then
        return c and n|2^b or n&-1-2^b
    else
        return n&2^b>0
    end
end
--
function uncover(tx,ty)
    if (bit(get(minefield,tx,ty),1)) return -- already uncovered
    if (bit(get(minefield,tx,ty),2)) return -- flagged
    if (tx<0 or tx>=width or ty<0 or ty>=height) return -- out of bounds
    set(minefield,tx,ty,get(minefield,tx,ty)+2)
    local nearbymine
    for xdelta=-1,1 do
        for ydelta=-1,1 do
            if (bit(get(minefield,tx+xdelta,ty+ydelta),0)) nearbymine=true
        end
    end
    if not nearbymine then
        for xdelta=-1,1 do
            for ydelta=-1,1 do
                local x=tx+xdelta
                local y=ty+ydelta
                if (x==mid(0,x,width-1) and y==mid(0,y,height-1) and not bit(get(minefield,x,y),1) and not bit(get(minefield,x,y),2)) uncover(x,y)
            end
        end
    elseif bit(get(minefield,tx,ty),0) then
        dead=true
    end
end
--
function checkwon()
    if (dead) return
    local coveredtiles=0
    for x=0,width-1 do
        for y=0,height-1 do
            if (not bit(get(minefield,x,y),1)) coveredtiles+=1
        end
    end
    if coveredtiles==mines then
        won=true
        remainingmines=0
        local best_times=highscores_arr[difficulty]
        local score=flr(timer)
        rank=0
        if cheatinput and difficulty>0 and score< best_times[3] then
            best_times[3]=score rank=3
            if (best_times[3]< best_times[2]) best_times[3]=best_times[2] best_times[2]=score rank=2
            if (best_times[2]< best_times[1]) best_times[2]=best_times[1] best_times[1]=score rank=1
            --if (score< world_recoreds[difficulty]) rank="*" scoresub() -- world record!
            highscores_arr[difficulty]=best_times -- probably unnecessary
            highscores_txt=highscores_arr[1][1]..","..highscores_arr[1][2]..","..highscores_arr[1][3].."\n"..highscores_arr[2][1]..","..highscores_arr[2][2]..","..highscores_arr[2][3].."\n"..highscores_arr[3][1]..","..highscores_arr[3][2]..","..highscores_arr[3][3]
            rm"/best_minesweeper_times.txt"
            store("/best_minesweeper_times.txt",highscores_txt)
        end
        if (not cheatinput) rank="x"
        if (difficulty==0) rank="?"
    end
end
--
function draw_game()
    if (not (dead or won or start or paused)) timer=min(timer+1/60,999)
    local tx=(mx-1)\8
    local ty=(my-20)\8
    if tx==mid(0,tx,width-1) and ty==mid(0,ty,height-1) and not dead and not won and not paused then
        -- on grid
        if bit(mbp,0) and not bit(get(minefield,tx,ty),1) and not bit(get(minefield,tx,ty),2) then
            if (start) place_mines(tx,ty) start=false
            --set(minefield,tx,ty,bit(get(minefield,tx,ty),1,true))
            uncover(tx,ty)
            checkwon()
        elseif bit(mbp,2) and not bit(get(minefield,tx,ty),1) and not start then
            set(minefield,tx,ty,get(minefield,tx,ty)^^4)
            remainingmines+=bit(get(minefield,tx,ty),2) and -1 or 1
        elseif bit(mbp,1) and bit(get(minefield,tx,ty),1) then
            local closemines=0
            local closeflags=0
            for xdelta=-1,1 do
                for ydelta=-1,1 do
                    if (bit(get(minefield,tx+xdelta,ty+ydelta),0)) closemines+=1
                    if (bit(get(minefield,tx+xdelta,ty+ydelta),2)) closeflags+=1
                end
            end
            if closemines==closeflags then
                for xdelta=-1,1 do
                    for ydelta=-1,1 do
                        uncover(tx+xdelta,ty+ydelta)
                    end
                end
                checkwon()
            end
        end
    elseif mx==mid(width*4-8,mx,width*4+7) and my==mid(2,my,17) and bit(mbp,0) then -- clicked on face
        -- reset puzzle
        dead=false
        won=false
        start=true
        paused=false
        remainingmines=mines
        timer=0
        for x=0,width-1 do
            for y=0,height-1 do
                set(minefield,x,y,0)
            end
        end
        rank=nil
    elseif mx==mid(width*4+10,mx,width*4+17) and my==mid(6,my,13) and bit(mbp,0) then -- clicked on wrench
        if start then
            draw_function=draw_menu
            if (not (dead or won or start)) paused=true
            cheatinput={}
        elseif dead or won then
            -- screenshot function
            local screenshot=get_draw_target()
            local clipboard_txt=chr(91,103,102,120,93)
            local w,h=tww,twh
            local function to_p8scii_num(n)
                if n<10 then
                    return tostr(flr(n))
                else
                    return chr(flr(n+87))
                end
            end
            clipboard_txt..=to_p8scii_num(flr(w/16))..to_p8scii_num(w%16)..to_p8scii_num(flr(h/16))..to_p8scii_num(h%16)
            for i=0,w*h-1 do
                clipboard_txt..=to_p8scii_num(get(screenshot,i%w,i\w))
            end
            clipboard_txt..=chr(91,47,103,102,120,93)
            set_clipboard_text(clipboard_txt)
            cls() return
        else
            paused=not paused
        end
    elseif mx==mid(width*4-18,mx,width*4-11) and my==mid(6,my,13) and bit(mbp,0) then -- clicked on help
        draw_function=draw_help
        if (not (dead or won or start)) paused=true
    end
    --
    -- draw game
    cls()
    rectfill(0,0,width*8,19,22)
    rectfill(0,19,width*8,height*8+19,32)
    palt(0,false)
    local emote=dead and frown or won and cool or (bit(mb,0) or bit(mb,1)) and tx==mid(0,tx,width-1) and ty==mid(0,ty,height-1) and curious or smile
    spr(emote,width*4-8,2)
    spr(start and wrench or (dead or won) and cam or paused and right or pause,width*4+10,6)
    spr(help,width*4-18,6)
    palt(0,true)
    if paused then
        print("** PAUSED **",width*4-30,height*4+15,7)
    else
        for x=0,width-1 do
            for y=-0,height-1 do
                local tile
                local tdata=get(minefield,x,y)
                local showneighbors
                if bit(tdata,1) then -- uncovered
                    if bit(tdata,0) then -- mine
                        tile=boom
                    else -- no mine
                        tile=empty
                        showneighbors=true
                    end
                else -- covered
                    if bit(tdata,2) or won then -- flag
                        if bit(tdata,0) or not dead then -- correct or still playing
                            tile=flag
                        else -- game over corrections
                            tile=falseflag
                        end
                    else -- no flag
                        if bit(tdata,0) and dead then -- game over clairvoyance
                            tile=mine
                        else -- nothing special
                            tile=block
                        end
                    end
                end
                spr(tile,x*8+1,y*8+20)
                if showneighbors then
                    local closemines=0
                    for xdelta=-1,1 do
                        for ydelta=-1,1 do
                            if (bit(get(minefield,x+xdelta,y+ydelta),0)) closemines+=1
                        end
                    end
                    pal(7,numbercols[closemines])
                    spr(numbers[closemines],x*8+1,y*8+20)
                    pal(7,7)
                end
            end
        end
    end
    --rect(0,0,width*8,19,1)
    rect(0,19,width*8,height*8+19,17)
    rectfill(1,5,16,13,2)
    local str=mid(-99,flr(remainingmines),999)
    if str<-9 then
        -- do nothing
    elseif str<0 then
        str="-0"..-str
    elseif str<10 then
        str="00"..str
    elseif str<100 then
        str="0"..str
    else
        -- do nothing
    end
    print(({[0]="non","1St","2nd","3rd",["*"]="tOP",["x"]="CHt",["!"]=str,["?"]="CUS"})[rank or "!"],2,6,8)
    --print("888\f8\-1"..({[0]="non","1St","2nd","3rd",["*"]="tOP",["x"]="CHt",["!"]=str,["?"]="CUS"})[rank or "!"],2,6,24)
    rectfill(width*8-16,5,width*8-1,13,2)
    local str=mid(-99,flr(timer),999)
    if str<-9 then
        -- do nothing
    elseif str<0 then
        str="-0"..-str
    elseif str<10 then
        str="00"..str
    elseif str<100 then
        str="0"..str
    else
        -- do nothing
    end
    print(str,width*8-15,6,8)
    --print("888\f8\-1"..str,width*8-15,6,24)
    if cheatinput then
        for i=97,122 do
            if (get_key_pressed(chr(i))) add(cheatinput,i)
        end
        while #cheatinput>5 do
            deli(cheatinput,1)
        end
        if (#cheatinput>=5 and cheatinput[1]..cheatinput[2]..cheatinput[3]..cheatinput[4]..cheatinput[5]=="120121122122121" and get_key_state"shift") cheatinput=nil
    else -- cheating!!!
        pset(0,0,(tx<0 or tx>=width or ty<0 or ty>=height) and 5 or bit(get(minefield,tx,ty),0) and 32 or 7)
        --pset(width*8,0)
    end
    -- debug: display mouse statistics
        --local ww,wh=window_size()
        --cursor(width*8+2,1) color(27)
        --?"mx: "..mx
        --?"my: "..my
        --?"mb: "..mb
        --?"mbp: "..mbp
        --?"tx: "..tx
        --?"ty: "..ty
        --?"ww: "..ww
        --?"wh: "..wh
        --?"b: "..highscores_arr[1]
        --?"i: "..highscores_arr[2]
        --?"e: "..highscores_arr[3]
    -- /debug
    --
    tww=width*8+1
    twh=height*8+20
end
--
function draw_menu()
    if mbp%2==1 then
        if mx==mid(150,mx,157) and my==mid(70,my,77) then
            draw_function=draw_game
            remainingmines=mines
            minefield=userdata("u8",width,height)
        elseif mx==mid(0,mx,6) and my==mid(21,my,63) then
            for i=2,5 do
                -- check if we are close to the appropriate difficulty selector
                if abs(mx-3)^2+abs(my-i*12)^2<9 then
                    difficulty=i-1
                    if difficulty==4 then
                        difficulty=0
                    else
                        width= ({09,16,30})[difficulty]
                        height=({09,16,16})[difficulty]
                        mines= ({10,40,99})[difficulty]
                    end
                    break
                end
            end
        elseif difficulty==0 and mx==mid(131,mx,157) and my==mid(13,my,44) then
            local change=get_key_state"ctrl" and get_key_state"shift" and 100 or get_key_state"ctrl" and 10 or get_key_state"shift" and 5 or 1
            if (mx==mid(131,mx,138) and my==mid(13,my,20)) width-=change
            if (mx==mid(150,mx,157) and my==mid(13,my,20)) width+=change
            if (mx==mid(131,mx,138) and my==mid(25,my,32)) height-=change
            if (mx==mid(150,mx,157) and my==mid(25,my,32)) height+=change
            if (mx==mid(131,mx,138) and my==mid(37,my,44)) mines-=change
            if (mx==mid(150,mx,157) and my==mid(37,my,44)) mines+=change
            width=mid(9,width,30)
            height=mid(9,height,16)
            mines=mid(10,mines,min(99,width*height-1))
        end
    end
    --
    cls(22)
    ?"Minefield Customization Menu",1,1,23
    spr(fishhook,150,70)
    ?"Beginner\nIntermediate\nExpert\nCustom",7,21,6
    for i=2,5 do
        local y=i*12
        if (i-1==difficulty or i==5 and difficulty==0) circfill(3,y,2,27)
        circ(3,y,2,6)
    end
    if difficulty==0 then
        ?" width:\nheight:\n mines:",96,13,6
        for y=12,36,12 do
            rectfill(130,y,158,y+9,32)
            ?y==12 and (width<10 and "0"..width or width) or y==24 and (height<10 and "0"..height or height) or mines,140,y+1,6
        end
        spr(width>9 and left or unusable,131,13)
        spr(width<30 and right or unusable,150,13)
        spr(height>9 and left or unusable,131,25)
        spr(height<16 and right or unusable,150,25)
        spr(mines>10 and left or unusable,131,37)
        spr(mines< min(99,width*height-1) and right or unusable,150,37)
    else
        local best_times=highscores_arr[difficulty]
        ?"1st: "..best_times[1].."\n2nd: "..best_times[2].."\n3rd: "..best_times[3],111,13,6
    end
    --
    tww=160
    twh=80
end
--
function draw_help()
    page=page or 1
    local pages={{[[
Welcome to Picotron Minesweeper!

Click the   to continue reading.
Click the   to go back a page.
Click the   to exit this screen.
]],
spr,right,49,25,
spr,left,49,37,
spr,fishhook,49,49},
{[[
Note that the game is paused in
this menu, so don't worry about
looking back here in the middle
of a game when you need help.
Click the   to get back here.
]],
spr,help,49,49
},{[[
Click a \0121tile\012s (  ) to uncover it.
A number will be revealed.

This number shows how many
\0121mines\012s (  ) surround the tile.
]],
rectfill,71,0,79,8,22,
spr,block,72,1,
rectfill,36,48,44,56,22,
spr,boom,37,49,
function()
    rectfill(1,24,73,32,22)
    for i=0,8 do
        local x=i*8+2
        spr(empty,x,25)
        pal(7,numbercols[i])
        spr(numbers[i],x,25)
        pal(7,7)
    end
end},
{[[
\0121Uncovering a mine will end the
game.\012s The goal is to uncover
all non-mine tiles as \0121quickly
as possible.\012s
]]},{[[
Right-click a tile to \0121flag\012s (  )
it as having a mine underneath.

Use this to keep track of what
you think is and isn't a mine.
]],
rectfill,141,0,149,8,22,
spr,flag,142,1},
{[[
The number in the upper-left
displays \0121an approximation of
how many mines are remaining.\012s
The number in the upper-right
displays your current \0121time.\012s
]]},{[[
If a tile with \0121zero\012s neighboring
mines is uncovered, \0121all\012s nearby
tiles will automatically be
uncovered. Your first click is
\0121guaranteed\012s to land on such a
tile, if at all possible.
]]},{[[
To restart the game, click the
\0121face\012s in the top-middle of the
screen. Its face also \0121corres-
ponds to the current game state.\012s
]],
pal,0,0,
spr,smile,35,50,
spr,curious,60,50,
spr,frown,85,50,
spr,cool,110,50,
palt,0,true},
{[[
If you middle-click a revealed
tile, \0121all surrounding non-
flagged tiles\012s will be opened \0121IF\012s
the \0121number of the clicked tile
is equal to the number of
surrounding flags.\012s
]]},
{[[
To the left of the face is
that   that you can click to
get back to this help screen,
but you probably figured that
out already.
]],
spr,help,24,13},
{[[
To the \0121right\012s of the face,
however, is a button that has
multiple functions \0121depending
on the game's state.\012s
]],
spr,wrench,100,37,
spr,pause,110,37,
spr,right,120,37,
spr,cam,130,37},
{[[
When the game ends (one way
or another), the \0121camera button\012s
(  ) shows up to let you get a
\0121screenshot\012s as a \0121Picotron GFX 
string copied to your clipboard.\012s
(experimental!)
]],
spr,cam,7,25},
{[[
In the middle of a game,
the \0121pause\012s (  ) and \0121unpause\012s (  )
buttons can be used if you
need to take a break
for whatever reason.
]],
spr,pause,57,13,
spr,right,142,13},
{[[
The most interesting option
though is at the beginning of
a round with the \0121Minefield
Customization Menu.\012s (  )
]],
spr,wrench,107,37},
{[[
In this menu, you can switch to
one of \0121three difficulty levels\012s
and see your \0121top three scores\012s
for each of them.
]]},{[[
You can also create a minefield
with \0121custom parameters,\012s such as
\0121width, height and mines\012s using
the   and   buttons.
]],
spr,left,19,37,
spr,right,49,37},
{[[
After winning the game, the
\0121estimated mine count\012s will
switch to showing how well you
did. \0121'non'\012s means no best time,
while \0121'1St'\012s, \0121'2nd'\012s and \0121'3rd'\012s
mean the appropriate place.
]]},
{[[

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\0121That's about all there is to
Picotron Minesweeper. Have fun!\012s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
]]} -- ...Aside from the hidden *CHEAT CODE*, that is...
--,{[[
--\0121Push\012s button
--Recieve \0121bacon\012s
--]]}
}
    --
    if mbp%2==1 and my==mid(70,my,77) then
        if mx==mid(150,mx,157) and page<#pages then
            page+=1
            first_time=nil
        elseif mx==mid(140,mx,147) and page>1 then
            page-=1
        elseif mx==mid(120,mx,127) and not first_time then
            --page=nil -- reset current page number (I decided against this later)
            draw_function=draw_game
            --if (not page) return -- avoid a crash if we reset the page number
        end
    end
    --
    cls(17)
    local to_draw=pages[page]
    local i=0
    local text=to_draw[1]
    while i<#text do
        i+=1
        if sub(text,i,i)=="\\" then
            text=sub(text,1,i-1)..chr(tonumber(sub(text,i+1,i+3)))..sub(text,i+4,#text)
        end
    end
    ?text,1,1,28
    --palt(22,true)
    --for i=2,#to_draw,3 do
        --spr(to_draw[i],to_draw[i+1],to_draw[i+2])
    --end
    --if page==3 then
        --pal(22,22) rectfill(1,24,73,32,22) palt(22,true)
        --for i=0,8 do
            --local x=i*8+2
            --spr(empty,x,25)
            --pal(7,numbercols[i])
            --spr(numbers[i],x,25)
            --pal(7,7)
        --end
    --end
    --pal(22,22)
    i=2
    while i<=#to_draw do
        local func=to_draw[i]
        i+=1
        local args={}
        while type(to_draw[i])~="function" and to_draw[i]~=nil do
            add(args,to_draw[i])
            i+=1
        end
        func(table.unpack(args))
     end
    ?"Page: "..page.."/"..#pages,2,71,12
    spr(first_time and unusable or fishhook,120,70)
    spr(page==1 and unusable or left,140,70)
    spr(page==#pages and unusable or right,150,70)
    --
    tww=160
    twh=80
end
--
draw_function=first_time and draw_help or draw_game
--
function _draw()
    -- mouse handling
    mx,my,mb=get_mouse()
    mbp=mb&~lmb -- mb pressed
    lmb=mb -- last mb
    --
    draw_function() -- varies
    --
    -- housekeeping stuff (their necessity is mostly Picotron's fault)
    rnd() -- sufficiently jumbles up the RNG seeds
    -- (necessary due to Picotron not randomizing the seed on startup)
    -- next up: a bunch of stuff to prevent you from resizing the window manually
    local ww,wh=window_size()
    if (tww~=ww or twh~=wh) set_window(tww,twh)
    -- more complicated than it needs to be because set_window() has WEEEEEIIIIIRRRRRD
    -- effects on how the drawing works. for example, sometimes it will prevent all
    -- future drawing actions from taking place. the solution would be to set the window
    -- last. HOWEVER, sometimes it will allow future drawing, but CLEAR THE SCREEN TOO.
    -- in that case, you would want to set the window first thing! long story short,
    -- to make thing easier we only fix the window size if we really, really have to.
end
-- end of program


Then paste it into a blank codebase in Picotron Playground, hit Ctrl+R and... THAT'S IT.

By default, you will be booted into the help screen, but if you don't care for that, just click the 'next page' button, and then the back button will show up so you can skip straight to playing the game.

Technical notes

(Warning: Long wall of text follows)


Most of the streamlining goes to set_window(w,h). With this function, the program will automatically make it's own window, set it's size, and flip to the desktop view, creating the desktop if it hasen't been booted up this session. If it's called multiple times, it will instead adjust the window it had already made earlier, though with some inconsistency as to how it treats anything already on the screen, and how it treats newly drawn stuff. (In my experience, one or the other will continue working, but never both, but which one works and which on doesen't seem to switch randomly whenever you do something like _draw=draw_game and I have no idea why. Earlier in development I edited the _draw function like this, but I decided against that when I needed some stuff like the mouse variables to always be updated, which inadvertently fixed the inconsistency issue, as it I just tested it now and it seem to be stuck on the 'lock future drawing, don't clear screen' mode, whereas before when I switched the _draw function around it seemed to switch modes. Still, I don't want to risk anything going wrong, so I still do the check to make sure it's actually necessary to adjust the window, just in case.) Credit to @pancelor for their function-lister, as otherwise this would not have been possible.

I also changed the font to be smaller, because that old font looked waayyy too big, and didn't go well at ALL with the shading on the holes, particularly with some colors (for example, the purple of the 4 combined with the shading to make it look like an 'A').

Did you know that the original Minesweeper had a cheat code? This version has that cheat code implemented too, so if you know the code you can solve puzzles that would of otherwise been impossible without guessing. (Though, you won't be able to save your score if do so.) Want to know the code? Well... for that, you're gonna have to look in the game's code. You gotta EARN it, you know?

Pasting the program into PICO-8 shows that this program is consists of 2944 tokens, 24445 chars, and 7642 compressed bytes (48% of .p8.png capacity). Of course, the program won't work in PICO-8 - it crashes just from attempting to define the sprites - but it's still a good metric for how much work I put in. (Answer: Actually not that much, at least compared to some other people.)

If you have too many empty tiles onscreen at any one time, this happens:

Actually, it's even worse, as it constantly swaps between this and the normal desktop at sixty frames a second. So, uh, I suppose I should give out an epilepsy warning, I guess?

Also, posting this on here was... harder than you would think. For starters, there's that gosh-darned decode function at the top of the codebase. Why is that? Well...

jelpi=userdata"
	
[8x8]

That looks horrible! And yes, that is what happens when you try to put a {gfx}..{/gfx} tag (imagine those as being square brackets and not curly ones) in a post, even in a section marked as code. The decode function's job is to synthesize one of those tags without actually having one of them in the code. So to make that Jelpi sprite:

function decode(str)
    return userdata(chr(91,103,102,120,93)..str..chr(91,47,103,102,120,93))
end
jelpi=decode"0808000000000f000f000ffffff00f1fff100effffe0002220000088800000f0f000"

You can see that the chr function is helping out greatly here by letting us encode the gfx portion as a series of numbers, and then decoding them to feed them to userdata.

The other issue I had when posting this code here was statements like blah< foo. If you remove the space between < and foo, it will remove ALL code between that point and whenever it finally sees a >. This doesn't seem to happen when a number or a space is immediatly following the < however, so fortunately this was relatively easy to fix.

That's about all I had. Like others, I probably won't continue to work on anything major in Picotron until a new version comes out (particularly the 0.1 release), but I still enjoyed working on it nonetheless. Special thanks to @Liquidream, @dw817 and @merwok for the kind words spoken about this project that helped me motivate myself to continue working on it.

P#128198 2023-04-07 21:33

1

Very impressive! @dw817 may not be here anymore, but I'd say this deserves a gold star!

This is definitely a strong step forward for Picotron development!

P#134700 2023-09-21 04:09 ( Edited 2023-09-21 04:12)

[Please log in to post a comment]