There is an n by m grid that has a random number (between 10% to 20% of the total number of tiles, though older implementations may use 20%..60% instead) of randomly placed mines that need to be found.

Positions in the grid are modified by entering their coordinates where the first coordinate is horizontal in the grid and the second vertical. The top left of the grid is position 1,1; the bottom right is at n,m.

 The total number of mines to be found is shown at the beginning of the game.
 Each mine occupies a single grid point, and its position is initially unknown to the player
 The grid is shown as a rectangle of characters between moves.
 You are initially shown all grids as obscured, by a single dot '.'
 You may mark what you think is the position of a mine which will show as a '?'
 You can mark what you think is free space by entering its coordinates.
 If the point is free space then it is cleared, as are any adjacent points that are also free space- this is repeated recursively for subsequent adjacent free points unless that point is marked as a mine or is a mine.
 Points marked as a mine show as a '?'.
 Other free points show as an integer count of the number of adjacent true mines in its immediate neighborhood, or as a single space ' ' if the free point is not adjacent to any true mines.
 Of course you lose if you try to clear space that has a hidden mine.
 You win when you have correctly identified all mines.
The Task is to create a program that allows you to play minesweeper on a 6 by 4 grid, and that assumes all user input is formatted correctly and so checking inputs for correct form may be omitted.
You may also omit all GUI parts of the task and work using text input and output.

Note: Changes may be made to the method of clearing mines to more closely follow a particular implementation of the game so long as such differences and the implementation that they more accurately follow are described.


C.F: wp:Minesweeper (computer game)

get "libhdr"
 
static $( randstate = 0 $)
 
manifest $( 
    nummask  = #XF
    flagmask = #X10
    bombmask = #X20
    revealed = #X40
    MAXINT = (~0)>>1
    MININT = ~MAXINT
$)
 
let min(x,y) = x<y -> x, y
and max(x,y) = x>y -> x, y
 
let rand() = valof
$(  randstate := random(randstate)
    resultis randstate >> 7
$)
 
let randto(x) = valof
$(  let r, mask = ?, 1
    while mask<x do mask := (mask << 1) | 1
    r := rand() & mask repeatuntil r < x
    resultis r 
$)
 
// Place a bomb on the field (if not already a bomb)
let placebomb(field, xsize, ysize, x, y) =
    (field!(y*xsize+x) & bombmask) ~= 0 -> false, 
    valof
$(  for xa = max(x-1, 0) to min(x+1, xsize-1)
        for ya = max(y-1, 0) to min(y+1, ysize-1)
        $(  let loc = ya*xsize+xa
            let n = field!loc & nummask
            field!loc := (field!loc & ~nummask) | (n + 1)
        $)
    field!(y*xsize+x) := field!(y*xsize+x) | bombmask
    resultis true
$)
 
// Populate the field with N bombs
let populate(field, xsize, ysize, nbombs) be
$(  for i=0 to xsize*ysize-1 do field!i := 0
    while nbombs > 0
    $(  let x, y = randto(xsize), randto(ysize)
        if placebomb(field, xsize, ysize, x, y) then
            nbombs := nbombs - 1
    $)
$)
 
// Reveal field (X,Y) - returns true if stepped on a bomb
let reveal(field, xsize, ysize, x, y) =
    (field!(y*xsize+x) & bombmask) ~= 0 -> true,
    valof
$(  let loc = y*xsize+x
    field!loc := field!loc | revealed
    if (field!loc & nummask) = 0 then
        for xa = max(x-1, 0) to min(x+1, xsize-1)
            for ya = max(y-1, 0) to min(y+1, ysize-1)
                if (field!(ya*xsize+xa) & 
                        (bombmask | flagmask | revealed)) = 0 do
                    reveal(field, xsize, ysize, xa, ya)
    resultis false
$)
 
// Toggle flag
let toggleflag(field, xsize, ysize, x, y) be
$(  let loc = y*xsize+x
    field!loc := field!loc neqv flagmask
$)
 
// Show the field. Returns true if won.
let showfield(field, xsize, ysize, kaboom) = valof
$(  let bombs, flags, hidden, found = 0, 0, 0, 0
    for i=0 to xsize*ysize-1
    $(  if (field!i & revealed) = 0 do hidden := hidden + 1
        unless (field!i & bombmask) = 0 do bombs := bombs + 1
        unless (field!i & flagmask) = 0 do flags := flags + 1
        if (field!i & bombmask) ~= 0 & (field!i & flagmask) ~= 0
            do found := found + 1
    $)
    writef("Bombs: %N - Flagged: %N - Hidden: %N*N", bombs, flags, hidden)
    wrch('+')
    for x=0 to xsize-1 do wrch('-')
    writes("+*N")
    for y=0 to ysize-1
    $(  wrch('|')
        for x=0 to xsize-1
        $(  let loc = y*xsize+x
            test kaboom & (field!loc & bombmask) ~= 0 do
                wrch('**')
            or test (field!loc & (flagmask | revealed)) = flagmask do
                wrch('?')
            or test (field!loc & revealed) = 0 do 
                wrch('.')
            or test (field!loc & nummask) = 0 do
                wrch(' ')
            or 
                wrch('0' + (field!loc & nummask))
        $)
        writes("|*N")
    $)
    wrch('+')
    for x=0 to xsize-1 do wrch('-')
    writes("+*N")
    resultis found = bombs
$)
 
// Ask a question, get number 
let ask(q, min, max) = valof
$(  let n = ?
    $(  writes(q)
        n := readn()
    $) repeatuntil min <= n <= max
    resultis n
$)
 
// Read string
let reads(v) = valof
$(  let ch = ?
    v%0 := 0
    $(  ch := rdch()
        if ch = endstreamch then resultis false
        v%0 := v%0 + 1
        v%(v%0) := ch
    $) repeatuntil ch = '*N'
    resultis true 
$)
 
// Play game given field
let play(field, xsize, ysize) be
$(  let x = ?
    let y = ?
    let ans = vec 80
    if showfield(field, xsize, ysize, false)
    $(  writes("*NYou win!*N")
        finish
    $)
    $(  writes("*NR)eveal, F)lag, Q)uit? ")
        unless reads(ans) finish
        unless ans%0 = 2 & ans%2='*N' loop
        ans%1 := ans%1 | 32
        if ans%1 = 'q' then finish
    $) repeatuntil ans%1='r' | ans%1='f'
 
    y := ask("Row? ", 1, ysize)-1
    x := ask("Column? ", 1, xsize)-1
 
    switchon ans%1 into
    $(  case 'r':
            unless (field!(y*xsize+x) & flagmask) = 0
            $(  writes("*NError: that field is flagged, unflag it first.*N")
                endcase
            $)
            unless (field!(y*xsize+x) & revealed) = 0
            $(  writes("*NError: that field is already revealed.*N")
                endcase
            $)
            if reveal(field, xsize, ysize, x, y)
            $(  writes("*N K A B O O M *N*N")
                showfield(field, xsize, ysize, true)
                finish
            $)
            endcase
        case 'f':
            test (field!(y*xsize+x) & revealed) = 0
               do toggleflag(field, xsize, ysize, x, y)
               or writes("*NError: that field is already revealed.*N")
            endcase
    $)
    wrch('*N')
$) repeat
 
let start() be
$(  let field, xsize, ysize, bombs = ?, ?, ?, ?
 
    writes("Minesweeper*N-----------*N*N")
    randstate := ask("Random seed? ", MININT, MAXINT)
    xsize := ask("Width (4-64)? ", 4, 64)
    ysize := ask("Height (4-22)? ", 4, 22)
 
    // 10 to 20% bombs 
    bombs := muldiv(xsize,ysize,10) + randto(muldiv(xsize,ysize,10)+1)
 
    field := getvec(xsize*ysize)
    populate(field, xsize, ysize, bombs)
    play(field, xsize, ysize)
$)