Sweeper

Copyright © 2023 by Víctor Parada

SWEEPER

This is a little game for the 2023 NOMAM's BASIC 10-liners Contest. This program fits in the EXTREM-256 category, and it was written using FastBasic 4.6 for the 8-bits ATARI XL/XE computers line. Development started on 2023-03-05, and it took 2+5 days. The final version's date is 2023-03-15.

UPDATE: It obtained the 7th place of 19 entries in the category.


Description

Clean the floor with your broom. Use the hints of surrounding tiles to avoid mines.


Instructions

SWEEPER easy Move the joystick to select the difficulty level from EASY, COOL and HARD.
SWEEPER cool SWEEPER hard
SWEEPER start Press the button to start the game. A pointer will appear over the tiles. You will see the number of tiles to clean at the top left and timer showing the seconds at the top right.
SWEEPER move Use the joystick to move around. Press the button to clean a tile. If a number appears, it says how many mines are adjacent to it, horizontally, vertically or diagonally.
SWEEPER clean Clean some other tiles to get an idea about where are the mines. Sometimes, you could find a danger-free area that will be cleaned immediatelly by a single click.
SWEEPER broom When you are sure that a tile is hiding a mine, press and hold the button until the broom appears, then move the joystick to get a flag, then release the button.
SWEEPER flag SWEEPER redflag
SWEEPER tile To remove a misplaced flag, hold the button over it and move the joystick until a tile appears, and then release the button.
SWEEPER done When you clean all the tiles that are not hiding a mine, the game is complete and the timer shows how many second were required. Try to be quick!!! All unflagged tiles will get their flag.
SWEEPER fail If you clean a tile that is hidding a mine, this will explode. Then, the location of every mine will be shown. If you misplaced a flag, it will remain in its place.

Development of the game

A couple of years ago, I wrote a FastBasic demo using DLI to enable a customized GRAPHICS 2 text mode with 8 colors and no flickering. Every paired colors could also be combined to create 4 aditional colors. Using this trick, 13 colors including background were available in a 20x12 text mode or tiles. I was thinking about using that trick in my The Children puzzle game like in PitKat for the 2600, but then I had another idea about DLI support that fitted better in that game and didn't use too many source listing space.

12 colors demo

12 colors demo.

PitKat

PitKat for Atari 2600.

The Children

The Children.

Some days ago, I thought that the trick from the demo could be used in a Minesweeper clone, as this game requires 8 numbers matching 8 different colors, 4 tiles (unknown, flag, doubt "?" and mine) matching 4 mixed colors, and the empty space matching the background. I was sure that game logic would be simple to develope, and could fit in the space left by the setting up for the trick (too much listing space for a tenliner). I started to write a prototype over the demo to measure if it was feasible.

SWEEPER prototype

Old demo with redefined charset.

SWEEPER prototype

Visualization of hint numbers being computed.

During the development, I defined and redefined the charset and the colors many times as I was adding more features. Of course, I run out of space soon and I had to simplify things. But I also got unexpected glitches, being the most important the one that appeared when I turned P/M graphics on. The HBLANK time was partially used by the ANTIC chip's management of P/M, and I got less CPU cycles to manage PF color registers. The solution was to manage only 3 PF registers, so the number of colors for the trick were reduced from 8 to 7, and the number of mixed colors from 4 to 3. It was not that bad, because I could repeat any of the other colors for the 8 and it couldn't be noticed just because the probability of its appearance is almost 0. Also, instead of modifying colors for all 16 scan lines, I had to leave the 16th intact, because it was running when the next interrupt was triggered.

SWEEPER prototype

Adding a pointer using P/M.

SWEEPER prototype

A glitch was detected!!!

SWEEPER prototype

Less colors in the palette.

In order to manage multiple difficulty levels, I had to change data management and built a single DATA byte array with a simple loop to proccess it, dropping blocks or bytes directly into fixed memory areas or system registers like the color palette. Also, in that block I stored some indexed tables that are accessed at certain points of the game. With the extra space I got, I could add sound FXs, a timer and, later, an animated Smiley face.

SWEEPER prototype

New pointer and a timer was added.

SWEEPER prototype

A classic Smiley and a better pointer.

The levels have a similar difficulty than the original Minesweeper from Windows. This is a comparison of both games:

Difficulty LevelSweeperMinesweeper
1NameEASYBeginner
Size9x99x9
Mines1010
Probability0.123450.12345
2NameCOOLIntermediate
Size13x1016x16
Mines2040
Probability0.153840.15625
3NameHARDExpert
Size19x1130x16
Mines4099
Probability0.191380.20625

There are differences between PAL and NTSC computers: the color palette is shifted for the same register values, the screen refresh rate is not the same, affecting the speed of jiffies (Hertz), and the aspect ratio changes. As this game was developed for PAL machines, I had to build a NTSC version of it to match the original.


Download and try

Get the SWEEPER.ATR file and set it as drive 1 in a real Atari (or emulator). Turn the computer on and the game should start after the loading completes. A joystick in port 1 is required.

NOTE: This game is for PAL computers. For NTSC computers, the ATR contains a file called SWEENTSC.XEX with minor changes in color palette, timer and Smiley.


The code

The abbreviated BASIC code is the following:

The full and expanded BASIC listing is:


Sweeper (Minesweeper)
(c) 2023 Víctor Parada

$8000-$81FF: Top half of charset
$8200-$83FF: Bottom half of charset
$8400-$844D: Custom display list
$8500-$8607: Secret screen data
$8700-$88FF: Screen data

This game uses 2 blocks of memory for data: screen (visible board) and secret (internal area with hidden mines and hint numbers). Both blocks are paired byte by byte.
Board has a visible size of PxQ, while data size goes from 0 to P+1 and 0 to Q+1 respectively, leaving one row and one column free on each side for simplicity in the hint number computation in the internal area.
Rows start every 20 bytes matching screen width. This means that the max visible size of the board should be 18x11, but left and right borders of the internal area could be merged (they are never displayed in the visible board), so the effective max size is 19x11.
The total data size is 260 bytes for both areas, but top line in screen can be reused to display messages, timer and counter without conflict, and the bottom line is never displayed.
graphics 31
Clears top 8K of RAM, where P/M graphics will be stored
data d() byte="{binary data}",
data byte="{binary data}"
Data block
29 bytes: Smileys
60 bytes: Configs 3 level x 10 words
12 bytes: Tile color mappings
44 bytes: 4 pointer bitmaps (11 bytes each)
85+3 bytes: Top half of the charset
0+3 bytes: Fix for splitted DATA in string format
86+3 bytes: Bottom half of the charset
75+3 bytes: Custom Display List
6+3 bytes: Color palette
2+3 bytes: Enable Display List (560)
2+3 bytes: Redirects screen data (88)
1+3 bytes: Sets horizontal position of P3 (Smiley)
dli set in = {color and charset assignment}
Display List Interrupt (DLI) for the board
dim f(12) byte,w(40),g(240),s,t,p,q,p2,
  q2,m,x,y,z
Arrays:
F(): Fonts mapping to bitmaps: 0-8,mine,tile,flag
W(): Relative location of each mine in the board (x+y*20)
G(): Queue for area cleaning
Difficulty level configuration:
S: Pointer to screen position of the first board cell (minus 21)
T: Pointer to memory storing the secret data of the board (minus 21)
P: Width of the board
Q: Height of the board
P2: Horizontal offset of the board in terms of P/M pixels
Q2: Vertical offset of the board in terms of P/M pixels
M: Number of mines to hide
X: Initial horizontal position of the pointer
X: Initial vertical position of the pointer
Z: Initial relative position of the pointer in the board (x+y*20)
mset $8000,$800,0
Clears data area
graphics 18
Graphics mode 2+16 (text 20x12)
pmgraphics 2
Double height P/M graphics
move adr(d)+90,adr(f),12
Sets up tile mapping
e=adr(d)+146
while peek(e)
  move e+3,dpeek(e+1),peek(e)
  e=e+3+peek(e)
wend
Loads data into memory
c=pmadr(2)
Gets P/M address of the pointer
dli in
Activates DLI
exec _n 0
Defaults to EASY level
do
MAIN LOOP
  mset c,256,0
Removes pointer and smiley
  poke 708,$28
Restores dynamic text color
  exec _q 0
Prints default smiley
  repeat
MENU LOOP
    v=10
Current tile value
    r=p*q-m
Number of required tiles to clean
    mset $8500,$300,0
Cleans hidden data and screen
    position 6,0
    print #6,"{binary data}";
Prints title
    exec _m e+1
Displays current difficulty name
    mset s+21,p,f(10)
    move s+21,s+41,q*20-20
Draws the board
    repeat
    until stick(0)=15
Waits for a released stick
    sound
Silence...
    repeat
Waits for next difficulty selection or confirmation of the current one
      a=stick(0)<15
Was joystick moved?
      b=strig(0)=0
Was trigger pressed?
      if a
If joystick was moved
        e=(e+1) mod 3
Selects next difficulty
        exec _n e
      endif
Loads new level configuration
      sound 0,72+7*e*a-62*b,10-4*b,
        a*8+b*2
Beep? Different tones based on action and level
    until a+b
Action was taken
    exec _a
Disables atract mode
  until b
Start?
  exec _m 4
Prints WAIT message
  n=0
  while n<m
Hides mines
    b=rand(p)+rand(q)*20+21
Selects a random cell
    a=t+b
    if peek(a)=0
Is that cell in the board empty?
      poke a,9
Yes, hide a mine there
      w(n)=b
Saves its position to simplify the hint numbers computation and to reveal them after loosing the game
      inc n
    endif
  wend
Next mine
  sound
Turns of the level selection beep
  for n=0 to m-1
    for j=-20 to 20 step 20
      for i=-1 to 1
Computes hint numbers surrounding each mine
        a=t+w(n)+j+i
        b=peek(a)
Picks a cell and its content
        if b<9
Is it not a mine?
          poke a,b+1
        endif
      next i
    next j
  next n
Increase the counter for that cell
  exec _m 0
Prints the number of remaining blocks
  exec _c 0
Enables standard pointer
  timer
  k=0
Resets timer
  repeat
GAME LOOP
    exec _q 0
Prints default smiley
    a=stick(0)
Gets stick position
    if a<15
If the joystick was moved
      sound 0,200,10,6
Sound FX
      exec _a
Disables attract mode
      x=x+(a&8=0)*(x<p)-(a&4=0)*(x>1)
      y=y+(a&2=0)*(y<q)-(a&1=0)*(y>1)
Computes new coordinates within the limits
      z=x+y*20
      v=peek(s+z)&$f
Gets the selected cell and its current type
      exec _c 0
Moves the pointer to the new position
      sound
Enough FX
      pause 5
Delay to avoid quick move
    elif strig(0)=0 and v>9
If the trigger was pressed over a covered cell
      exec _q 1
Displays a gessing smiley
      exec _a
Disables attract mode
      o=0
      exec _c 1
Selects the broom as the action pointer
      repeat
        if stick(0)<15
While the trigger is being pressed, check if the joystick was moved
          sound 0,9,10,8
Selection sound
          o=(o+1) mod 3
          exec _c o+1
Next pointer: (0=broom, 1=flag, 2=clear)
          sound
Silence...
          repeat
Waits for the joystick to be released
            exec _t
          until stick(0)=15
        endif
Updates the onscreen timer
        exec _t
Updates the onscreen timer
      until strig(0)
Repeat until the trigger is released
      if o
        poke s+z,f(12-o)
If the selected action is not to uncover, puts the corresponding tile (moves between flag and clear)
      else
        v=peek(t+z)
        poke s+z,f(v)
The action is to reveal the cell content, copying it from the hidden area to the screen
        if v=9
If a mine was revealed
          exec _q 2
Display a sad Smiley
          for a = 30 to 150
            sound 0,a,8,15-a/10
          next a
Plays and explosion FX
        else
A hint was revealed
          sound 0,9,8,2
Plays a cleaning sound FX
          dec r
Decreases the remaining cells counter
          if v=0
If the cell was clear, reveal all surrounding hints
            h=0
            n=0
            mset adr(g),480,0
Initializes queue and pointers
            g(n)=z
Stores the current cell at the start of the queue
            while g(h)
While the queue has pending cells
              for i=-1 to 1
                for j=-20 to 20 step 20
Checks for unrevealed cells surrounding it
                  a=g(h)+i+j
Checks surrounding cell's content
                  if peek(s+a)=f(10)
                    dec r
                    b=peek(t+a)
                    poke s+a,f(b)
If that cell is still unrevealed, copy its content from the hidden board
                    if b=0
                      inc n
                      g(n)=a
                    endif
                  endif
                next j
              next i
If that cell was empty, add it to the queue
              inc h
            wend
          else
Updates pointer for next queue item
            pause 2
          endif
Delay to allow cleaning FX to be listened
          exec _m 0
Displays the final number of remaining cells
          sound
        endif
      endif
Silence
      exec _c 0
    endif
Recover the pointer shape
    exec _t
Updates the onscreen timer
    l=v=9
Was a mine revealed?
  until r=0 or l
Game is over when there are no more required blocks or a mine was revealed
  exec _m 5+l
Prints DONE or FAIL message
  for n=0 to m-1
    poke s+w(n),f(11-l-l)
  next n
In case of success, completes missing flags, otherwise reveal position of each mine, replacing all the flags, except those not over a real mine
  if l=0
If last cell was not a mine
    exec _q 3
Displays a cool smiley
    for a=63 to 0 step -1
      sound 0,a,10,a mod 8
      pause
    next a
  endif
Congratulations sound FX
  while strig(0)
The game is over. Waits for the trigger to be pressed and then released
    poke 708,peek(20)&$3C*4+12
  wend
  repeat
  until strig(0)
Continuously changes the color of the game over message
  exec _a
loop
Disables the attract mode
proc _t
Updates the timer
  if k<999
If the timer has not reached its limit, update it
    pause
Waits for VBLANK to avoid glitches with system timer
    k=(time/2)&$7FFF/25
Converts jiffies into seconds
    u$="00"
    u$=+str$(k)
    u3$=u$[peek(adr(u$))-2]
Converts a decimal number into a 3 digits string
    position 16,0
    print #6,color(32) u3$
  endif
endproc
Prints the updated timer
proc _c a
  mset c,128,0
  pmhpos 2,p2+x*8+2
  move adr(d)+102+a*11,c+q2+y*8-1,11
endproc
Updates pointer position and shape
proc _a
  poke 77,0
endproc
Disables attract mode
proc _m a
  position 1,0
  if a
    print #6,
      "EASYCOOLHARDWAITDONEFAIL"[a*4-3,4]
  else
    print #6,rtab(5) r
  endif
endproc
Prints message or required blocks
proc _n e
Gets level config
  move adr(d)+30+e*20,adr(s),20
endproc
This MOVE sets up 10 integer variables in a single statement, plus the level as an argument
proc _q a
  move adr(d)+7*a+1,pmadr(3)+15,8
endproc
Updates Smiley

Return to my 10-liners page.

© 2023 by Víctor Parada - 2023-03-14 (updated: 2023-04-03)