(Receive email notifications)
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"
|
||||||
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.
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!
[Please log in to post a comment]




