Mine Sweeper

By Leo Bronder - Giroux

The game of mine sweeper consists of a N*L grid with M mines hidden in it. M may not excede N*L.  To play, an initial location is tested, either there is a mine there and the game is over or the number of mines bordering on this location is revealed. If there are no mines at the location guessed, then all neighboring locations are automatically tested by the game.  This step is not neccesary to play the game, but makes it much more fun.  Once, the out come of the initial guess has been assesed the player is allowed to guess additional locations untill all of the non-mine squares have been identified or a the location of a mine has been guessed and ends the game. Often the player is allowed to mark locations where a mine is suspected to exist in order to avoid accidentally guess ing that location, this is not to say that that location cannot be guessed, but that there is a visual reminder.

My idea on how to write this program

To break it into smaller problems:

First,devise a way to generate two N*L matricies one with the locations of M mines and 		one that indicates what locations have been guessed to the player.
Second,how can the player guess a spotand then have these matricies evaluated and modi		fied.
Third,improve evaluation so that if a space with no neighboring mines is clicked it is 		the equivalent of clicking all the surrounding spaces.
Fourth,create a way to "flag" mines once the player has identified them.

The program

Moduules that need to be Initialized

As can be seen in the earliest versions of this program I mucked around alot with creating the array of the board.  I finally realized that the table function would do exactly what I was trying to do with do loops and append commands.

generateMinefield[n_, l_] :=  
      Module[{g, 
          h}, 
minefield = 
            Table["?", {g, 1, n}, {h, 1, 
                l}];
]

This was the bigest jump of he program.  I initally thought it would be easiest to make a matrix of "?", to show spaces on the board, and replace a certain number of them with"X", to signify mines.  It turned outthat this was much harder because the random points had to be first tested to see that the had not already been chosen.  If mines had been alowed to double up, then the gameboards would not have enough mines.  I resolved this issue by creating a list of coordinate points and choosing from that list the points I needed.  This turned out to make analyzing th board much easier.

\(selectQofOptions[q_\ , options_] := 
      Module[{optionsa, chosen, p}, 
optionsa = 
          options; 
 (*\(Print[
              options];\)*) 
 (*\(Print[
              optionsa];\)*) 
chosen = {}; 
 (*\(Print["chosen = " <> 
                ToString[
                  chosen]];\)*) 
Do[
 (*\(Print["length optionsa" <> 
                  ToString[Length[optionsa]]];\)*) 
p = 
            Random[Integer, {1, 
                Length[optionsa]}]; 
 (*\(Print["p=" <> 
                  ToString[p]];\)*) 
 (*\(Print[
                optionsa[\([p]\)]];\)*) 
chosen = 
            Append[chosen, 
              optionsa[\([p]\)]]; 
 (*\(Print["chosen is " <> ToString[chosen]];\)*) 
optionsa = 
            Delete[optionsa, 
              p];
 (*\(Print["optionsa is " <> 
                  ToString[
                    optionsa]];\)*) 
, {q}]; 
Return[chosen];
]\)

This module generated a list of coordinates for the mines to be chosen from.  I intended to parse it into a matrix, but I realized that a list of locations would be better for the analysis of the board.

generateAnswer[n_, l_, m_] := 
      Module [{b, a, listofcoordinates, 
          flatlist  }, 
  
answer = 
          Table[0, {g, 1, n}, {h, 1, 
              l}]; 
listofcoordinates = 
          Table[{g, h}, {g, 1, n}, {h, 1, l}]; 
flatlist = 
          Flatten[listofcoordinates, 1]; 
mineplaces = 
          selectQofOptions[m, 
            flatlist];
    ]

I think he useage says it all. This part was extremely strraight foreward.

checkSurounding::usage = "checks the value of the boxes around a \
square and tallies them";

checkSurounding[row_, column_] := 
      Module[{r, t, suroundingmines, w}, 
suroundingmines = 
          0; 
Do[
Do[
If[mineplaces[[w]] == {r, 
                      t}, 
suroundingmines = 
                    suroundingmines + 
                      1
];
, {w, 1, 
                Length[mineplaces]}];
, {r, row - 1, 
            row + 1}, {t, column - 1, 
            column + 1}]; 
boardvalues = 
          ReplacePart[boardvalues, 
            suroundingmines, {row, column}]
]

putinMine[row_, column_] := 
      Module[{w}, 
Do[  
If[{row, column} == 
                  mineplaces[[w]], 
  
boardvalues = 
                    ReplacePart[
                      boardvalues, "X", {row, 
                        column}];];
, {w, 1, 
              Length[mineplaces]}
];
]

For some reason I could not create a function that would give coordinates number values *and* put "X" where mines were, so i wrote two modules and ran them succesively.

\(makeBoardvalues := 
      Module[{ (*r, t, s, 
            d, *) }, 
Do[ (*\(Print["hey dude"];\)*) 
\(checkSurounding[r, t];\)
, {r, 1, 
            Length[boardvalues]}, {t, 1, 
            Length[boardvalues[\([1]\)]]}]; 
Do[ (*\(Print["sup playa"];\)*) 
\(putinMine[s, 
              d];\)
 (*\(Print["oh dear"];\)*) 
, {s, 1, Length[boardvalues]}, {d, 1, 
            Length[boardvalues[\([1]\)]]}];
]\)

This function was the one that tied it all to gether.  I expected it to be more complicated, but this one worked like a charm.  The major dificulty that exists now is getting theboard to redraw over itself.  This is a matter of giving the gameboard  cell tag so that it can be deleted and redraw.  It would be good to leave a "histogram" option where the user could set the program to display his/her past moves.  The other functionality that could be added would be to automatically click the cells surrounding any zero that is clicked.  This would be simple to do by adding annother if to the click function.

\(buttonClick[row_, column_] := 
      Module[{}, 
If[\(boardvalues[\([row]\)]\)[\([column]\)] == "X", 
 (*then*) 
currentboard = 
            boardvalues; 
Print["We all die sometime!"];, 
 (*else*) 
\(currentboard = 
              ReplacePart[
                currentboard, \(boardvalues[\([row]\)]\)[\([column]\)], {row, 
                  column}];\)
]; 
NotebookDelete[
          gamewindow]; 
makeBoard; 
gameBoardDraw;
]\)

This function draws the board and was modified from the "visual turtle"

gameBoardDraw := 
      Module[{nb}, 
NotebookWrite[
            gamewindow, 
Cell[
BoxData[
GridBox[
b,  (*
RowLines -> 
                      True, 
ColumnLines -> 
                      True, *) 
ColumnWidths -> 
                    1.5, 
RowMinHeight -> 
                    1.5, 
ColumnsEqual -> 
                    True
]
]
]
];
]

Makes the buttons to sew onto the pants,,, or for the program to work

\(\(buttonMake[row_, column_] := 
        Module[{}, 
If[\(currentboard[\([row]\)]\)[\([column]\)] == "?", 
\(a = 
                ButtonBox[
                  RowBox[{" "}], 
ButtonFunction :> buttonClick[row, 
                      column], 
ButtonEvaluator -> 
                    Automatic, 
Active -> 
                    True, 
ButtonMargins -> 
                    15, 
Background -> 
                    GrayLevel[
                      0.75]
];\), 
\(If[
                0 ≤ \(currentboard[\([row]\)]\)[\([column]\)] ≤ 
                    8 || \(currentboard[\([row]\)]\)[\([column]\)] == "X", 
\(a = 
                    ButtonBox[
                      RowBox[{\(currentboard[\([row]\)]\)[\([column]\)]}], 
ButtonFunction :> 
                        None, 
ButtonEvaluator -> 
                        Automatic, 
Active -> 
                        False, 
ButtonMargins -> 
                        15, 
Background -> 
                        GrayLevel[
                          0.8]
];\)
];\)
]; 
Return[
            a];
];\)\)

makeBoard := 
      Module[{}, 
        b = Table[
            buttonMake[row, column], 
{row, 1, 
              Length[currentboard]}, {column, 1, 
              Length[currentboard[[1]]]}]
]

\(newGameWindow := 
      Module[{}, 
NotebookClose[
          gamewindow]; 
gamewindow = 
          NotebookCreate[
WindowTitle -> "Game Window"
 (*\(,\)\(WindowSize -> {size\ of\ ' 
                    gameboard'}\)*) 
];
]\)

The newgame module resets all the initial values and runs the functions that construct the gameboard, beyond this point the entire program is run by the click function and the matrix that has the job of storing mine locations and he values of other locations

newGame[n_, l_, m_] := 
      Module[{}, 
generateMinefield[n, 
          l]; 
currentboard = 
          minefield; 
generateAnswer[n, l, 
          m]; 
boardvalues = 
          minefield; 
makeBoardvalues; 
newGameWindow; 
makeBoard; 
gameBoardDraw;
]

To run the game

Simply use the "newGame" module. look at the useage for more detail.

newGame[4, 5, 6]