GAME / FREE / MIT EXAMPLE SOURCE

Meadow

Find a safe path

A 9 × 9 Minesweeper with safe first clicks, flags, flood reveal, and a fresh board every round.

Meadow, running as a native Yeho application on Windows
Actual native application capture. The downloadable source produces this example.

Take it for a spin.

Left click reveals, right click flags, and R starts a new meadow.

Extract the Windows download to a writable folder and open minesweeper.exe. Escape closes it. Database examples keep their local data in the working folder.

See how it works.

Grid data, neighbours, deterministic randomness, and iterative flood fill.

Nine by nine, ten mines, local play.

READ / CHANGE / UNDERSTAND

The whole example is yours.

These are the exact files in the source download. Start with the model, then follow the window’s event loop. Comments explain the decisions.

model.yh
// Store each cell as data; rendering never changes the game rules.
thing Cell
{
    bool mine=false
    bool open=false
    bool flag=false
    int neighbours=0
}
class Minefield
{
    list of Cell cells=[]
    bool planted=false
    bool lost=false
    bool won=false
    int seed=71
    Minefield()
    {
        this.Reset()
    }
    Reset()
    {
        this.cells=[]
        for i from 0 < 81
        {
            this.cells.Add(Cell())
        }
        this.planted=false
        this.lost=false
        this.won=false
    }
    Plant(int safe)
    {
        int count=0
        while count<10
        {
            this.seed=((this.seed*251+17)-(this.seed*251+17)/9973*9973)
            int index=(this.seed-this.seed/81*81)
            int sx=(safe-safe/9*9)
            int sy=safe/9
            int x=(index-index/9*9)
            int y=index/9
            // The first click and its neighbours are safe, giving flood reveal room.
            if !this.cells[index].mine and (Math.AbsFloat(Math.IntToFloat(x-sx))>1 or Math.AbsFloat(Math.IntToFloat(y-sy))>1)
            {
                this.cells[index].mine=true
                count=count+1
            }
        }
        for i from 0 < 81
        {
            int x=(i-i/9*9)
            int y=i/9
            int n=0
            for dy from -1 <= 1
            {
                for dx from -1 <= 1
                {
                    int nx=x+dx
                    int ny=y+dy
                    if nx>=0 and nx<9 and ny>=0 and ny<9 and this.cells[ny*9+nx].mine
                    {
                        n=n+1
                    }
                }
            }
            this.cells[i].neighbours=n
        }
        this.planted=true
    }
    Flag(int index)
    {
        if index>=0 and index<81 and !this.lost and !this.won and !this.cells[index].open
        {
            this.cells[index].flag=!this.cells[index].flag
        }
    }
    Reveal(int index)
    {
        if index<0 or index>=81 or this.lost or this.won or this.cells[index].flag
        {
            return
        }
        if !this.planted
        {
            this.Plant(index)
        }
        if this.cells[index].mine
        {
            this.cells[index].open=true
            this.lost=true
            return
        }
        // A queue avoids recursive flood-fill stack growth. Open on enqueue so each
        // cell is enqueued at most once, even when several neighbours discover it.
        list of int queue=[index]
        this.cells[index].open=true
        int cursor=0
        while cursor<queue.count
        {
            int at=queue[cursor]
            cursor=cursor+1
            if this.cells[at].neighbours==0
            {
                for dy from -1 <= 1
                {
                    for dx from -1 <= 1
                    {
                        int x=(at-at/9*9)+dx
                        int y=at/9+dy
                        if x>=0 and x<9 and y>=0 and y<9
                        {
                            int next=y*9+x
                            if !this.cells[next].open and !this.cells[next].flag and !this.cells[next].mine
                            {
                                this.cells[next].open=true
                                queue.Add(next)
                            }
                        }
                    }
                }
            }
        }
        int revealed=0
        for cell in this.cells
        {
            if cell.open
            {
                revealed=revealed+1
            }
        }
        this.won=revealed==71
    }
}