Log In  


Cart #minesv2-4 | 2025-05-30 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5

A Minesweeper game submitted to the TweetTweet Jam X.

Now improved with 0-fill and flags!

Basics:

  1. Left click a blank square to reveal the block; the number in the block shows how many mines are next to that block (vertically, horizontally and diagonally)
  2. Reveal all the non-mined blocks to win; which will reveal the board (green mines)
  3. Hitting a mine will reveal the board (red mines)
  4. You can flag blocks you think might be a mine with Right Click; right click again to remove the flag (flagged squares cannot be clicked to avoid accidentally revealing a mine and losing the game)
5


Impressive how much there is of minesweeper in this, given the tiny code size, but it would still need a bit more to be nice to play.
What were the self imposed constraints ?

Also, there is some bug with the counting in the top left corner :

But you're likely aware and the code size is more important than full correctness, I'm guessing.


Thanks, this was for tweettweet jam x so had a strict 500 character limit (and I was rushing a bit at the end since I was trying to fit as much as I could in the limit at the very end of the jam). Definitely want to come back to it and add more features/fix issues with it; and hopefully some how keep in under 500 characters :)


Pico-8's car count is at 482. Is that counter accurate for tweet jam ?
If so, we could add a pal statement so the zeroes stay invisible, and maybe some recursion to auto clear connected zeroes ?
I'll tale a look.


That's the number I got. I have gotten it down to 475 with the fix for the problem you found (a classic off by one in a comparison). I also have a strategy for doing the fill connected zeroes using the mine counting logic but I'm very sure I'll land at like 530 characters or something after I add it. If you come up with a good way of handling it, I'd be interested to see it


Idea is to move the clear cell logic into a recursive function.
I didn't know about the one liner nested ifs, so my "expanded" version keeps crashing if the nested if manipulates nil. It's also confusing to me that i is the table and t the index, I keep swapping the two. I'll come back to it later today.


Yeah, I have been using Shrinko8 to make it smaller and it likes to rename all my variables. I swear they started with names that made sense :)

I was able to include the 0-fill logic reusing the mine logic as I mentioned and kept it down to 493 characters. I think its as playable as I want/can expect from the 500 character limit though theres always ways to make it better (timer, actual win condition, flags, etc). Of course, if you thought the logic was a bit hard to follow before, now its even more convoluted unfortunately, but it takes alot of reordering and what would normally be considered terrible coding practices to get it to fit. Its a fun challenge though.

If you are struggling with it and want some help, I can try to provide a commented version with what everything is doing and maybe rename the variables to at least a letter that makes sense.


After the 0-fill and flagging, Ive hit 500 characters exactly and I dont think theres really much more I can do while maintaining the limit from the jam (which was always my goal for this project). Anyone might wanting to play with it more or try to add the missing things from regular minesweeper (timer, mine counter, face) can do so, independent of the limit :)


Nice, this is now playable and in the 500 limit !
I took a peek at the code and recoiled in fear :p
The function at the start makes sense, you add 1 to the 8 squares of the array in parameter around tile index i.
This is used on the two arrays :
one is for the bomb count if less than 9 or 9 for bombs, and the second one is confusing and is used for click tracking? negative for flag, nil for not clicked, 0 for clicked and >0 for clicked zero, maybe ???
Could you post the non minified version ? This feels like I'm back in the 80s, trying to make sense of the disassembly of self modifying code in a game I'm trying to patch...


1

I dont really have an unminified version unfortunately, I started with an already somewhat shrunk version and just kept hacking and cutting from there, refactoring the minified code to the point where it is now: Complete incomprehensible if you didnt know the evolution and overall design. So instead, I have blown up the code from its super compact form and provided what I hope are alot of helpful comments on what every line does and how it all works:

--[[
Mines 500 characters broken down

The table e is used to record whats behind each box, 0-8 for numbers
or >=9 for mines (we dont saturate or anything so a mine might be up to 13)

The table a is used to record the state of each box, <1 is flagged, 1 is
hidden, 2 is revealed (we saturate this to avoid issues with both the 0-fill
logic and mouse being held down)

These tables are indexed 0-389 for a 15x26 grid, only 13x24 is shown (the 
extra padding simplifies the logic)
]]--

--[[
Function for incrementing neighbors, useful for creating the numbers around 
mines and for 0-filling, depending on the table given for argument f. There
is a "hidden" argument i, which is the center square. This form with some
hardcoded numbers is the smallest I could make it
]]--
function l(f)
  --[[
  Since the grid is 26 wide, neighbors are +-1 (right/left), +-26(bottom/top)
  +-25(bottom-left/top-right), +-27(bottom-right/top-left)
  ]]--
  j={1,o,25,27}
  for d in all(j)do 
    f[i-d]+=1
    f[i+d]+=1
  end
end 

-- Initialization
a={}
e={}
u=56 -- Number of mines; feel free to adjust for difficulty B)
o=26 -- Grid width, used so many places that its efficient to have as a variable

-- Main loop
::f::
?"⁶1⁶c0⁶!5f2d3" -- Print magic to wait a frame, clear screen and enable mouse

-- Get mouse X and Y
w=stat(32)
h=stat(33)

-- Iterate over the 15x26 grid with index f
for f=0,389do
  --[[
  Some "premath", both the print X and bounds checking needed f%o ie index mod
  width and doing the *4 used in the print X was more efficient to do here
  ]]--
  r=f%o*4
  --[[
  To avoid indexing the reveal table everywhere, cache it in d, we write it 
  back later
  ]]--
  d=a[f]
  --[[
  b is the bounds check for the current index to know if its in the 13x24 grid
  The second part should hopefully be clear, once index > 26, its past the first
  hidden row, index < 364 means its not at the last hidden row

  The other part would normally be f%o>0 and f%o<25; checking the first and last
  hidden columns, but this is too many characters. Instead, we leverage integer
  division and the "premath" from earlier. We need f%o*4 to be different numbers
  for 0 and 25 but the same for 1-24; which this is:
    f = 0 -> 0%26*4 = 0 - 4 = -4 \ 96 = -1
    f = 1 -> 1%26*4 = 4 - 4 = 0 \ 96 = 0
    ...
    f = 24 -> 24%26*4 = 96 - 4 = 92 \ 96 = 0
    f = 25 -> 25%26*4 = 100 - 4 = 96 \ 96 = 1
  ]]--
  b=(r-4)\96==0and f>26and f<364

  --[[
  Here we see if we are in the loop where the mouse is, using integer division to
  round into the square coordinates
  ]]--
  if f==w\4+h\8*o-29do
    --[[
    The logic for flagging, if right mouse clicked and not revealed (d=0 flagged,
    d=1 hidden), use more math to toggle d. This math is actually wrong and why
    you need a double click to unflag the square, since it comes from an earlier
    version where d=-1 for flagged, d=0 for hidden, d=1 for revealed. Thanks to
    the mod 3, d can only take on 3 different values here, I leave it to you to
    go through the math
    ]]--
    if(btnp(4)and d<2)
      d=1-2*d%3

    --[[
    The logic for revealing, simpl assign d to revealed (we dont care its if
    its already revealed. Here we also have the mine creating logic since in
    minesweeper, the first click is never a mine, so we need to do it after
    the first click
    ]]--
    if(btn(5))
      d=2
      --[[
      The mine placing logic, which doubles as an if for if we have done it
      since we decrement the number of mines variable to 0 in this loop
      ]]--
      while(u>0)
        --[[
        Pick a square, any square in 13x24, which ensuring the mine is in
        the bounds without complex logic. We just need to remap it into the
        15x26 which is what the below math does
        ]]--
        i=rnd(312)\1
        i=i\24*o+i%24+27
        -- Check if its already a mine or the clicked square to skip it
        if(e[i]<9and i~=f)
          -- If not, make it a mine, decrement mine count, and fix neighbors
          e[i]=9
          u-=1
          l(e) -- Get second argument index (i) for free here
  end

  n=e[f] -- Pull out number/mine from table to save characters
  --[[
  Finally actually initialize the start values of the tables, the expectation
  is that nothing could have happened yet since we are first drawing the board
  below. m is never declared so this is a short hand for n == nil. I guess (not n)
  is actually the same number of characters but this seemed better at the time :)
  ]]--
  if(n==m)
    e[f]=0 -- all squares start at 0, incremented to the right number in mine creation
    d=1 -- all squares start hidden

  -- Check if square is revealed (ie, 2) and saturate it
  if d>1do
    d=2
    c=n+7 -- Set the color, starting with white 0, red 1, etc
    --[[
    To leverage a bit of the display logic, we also use this check for revealed
    squares to see if we either have a revealed 0 (ie n<1, better than n==0) or
    if a mine has been revealed (k, we will see it later), and if its in bounds
    to avoid incrementing outside the tables by running this logic on the hidden
    edge boxes
    ]]--
    if(n<1or k)
      if(b)
        --[[
        Pass in the argument via the i variable from mine creation, which isnt used
        any more, so quite convenient
        ]]--
        i=f 
        l(a)

    --[[
    Check for revealed mine, print it dull red and set the bomb k, which was nil
    until now, but since is set, will reuse the 0-fill logic above
    ]]--
    if(n>8)
      n="⁙"
      c=2
      k=1
  else
    n="▮" -- hidden grey square
    c=6
  end
  --[[
  If in bounds, print the square/number/mine at the location given by the index f
  converted into pixel coordinated, reusing our f%o*4 for the X position with offset
  12,8
  ]]--
  if(b)
    ?n,r+12,8+f\o*8,c
  a[f]=d -- Write back into the reveal tables
  -- If flagged, go back and print a red flag over the square
  if(d<1)
    ?"⁴a▶",8
end
?"⁶.¹³⁷ᶠ³⁴\0\0",w,h,7 -- Draw the mouse at the x,y gotten at the start of the loop
goto f -- and back to main loop top

Thanks for the documented code.
There are a few bytes up for grab here and there, but not much.

for d in all{1,o,25,27}do
no intermediate j

a,e,u,o={},{},56,26
saves a few = signs
a few more bytes here and there with other multi-assignations.

n=e[f]or 0
(nil is false, false is false, anything else is true)

That doesn't leave much to add functionality or bug fixes.

I think we can get rid of the problem of strange effects when clicking outside the grid by having a bigger grid that covers the entire screen at no extra cost.

With the few saved bytes, I suspect we can squeeze in either flag protection or chording... Or maybe black zeroes for better readability of the board.

If we rewrite the entire thing, I think we could squeeze much more by using quarter to half the space for a small custom mini interpreter (somewhere between forth and turing machine/assembly) that would take bytecode as its input, and the second half for the minesweeper code as bytecode string.


Cool, I didnt know about the all{} syntax so that does save a few. Fixing the issue with the flag toggle math can also save a few characters. Enough to add flag protection (ie, left clicking on a flag wont reveal it). a,e,s,o={},{},56,26 actually costs characters since the extra commas add more than the equal signs; so saves tokens but not characters. I do think the or 0 trick would save some characters as well since it avoids a slightly long if; but only a few since logical expressions like that struggle to be compressed :)

As for the bigger grid, Im not sure if that would save much since some of the math might grow from single to triple digits (like the bounds checking one), but maybe. I do think the extra space already helps if you are clicking near the edge though.

Its possible doing some kind of interpreter might work but I am very skeptical of that. If it was about saving tokens, I think youre right since a big string is only 1 token. But for saving characters, I dont think that would work.


Ok, this really is the last one; thanks to some of the savings from this thread and a bit more work, Ive been able to have the game confirm you've won and reveal the board in either hitting a mine or revealing all non-mines (green mines for win, red mines for loss). Ive updated the rules to match.

Exactly 500 characters and I really think there isnt anything that can be done to make it any smaller but if anyon wants to try, please go ahead. The breakdown from before is still basically correct but the code has been reordered a bit.


1


[Please log in to post a comment]