Minesweeper game
You are encouraged to solve this task according to the task description, using any language you may know.
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)
Ada
with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;
procedure Minesweeper is
package IO renames Ada.Text_IO;
package Nat_IO is new IO.Integer_IO (Natural);
package Nat_RNG is new Ada.Numerics.Discrete_Random (Natural);
type Stuff is (Empty, Mine);
type Field is record
Contents : Stuff := Empty;
Opened : Boolean := False;
Marked : Boolean := False;
end record;
type Grid is array (Positive range <>, Positive range <>) of Field;
-- counts how many mines are in the surrounding fields
function Mines_Nearby (Item : Grid; X, Y : Positive) return Natural is
Result : Natural := 0;
begin
-- left of X:Y
if X > Item'First (1) then
-- above X-1:Y
if Y > Item'First (2) then
if Item (X - 1, Y - 1).Contents = Mine then
Result := Result + 1;
end if;
end if;
-- X-1:Y
if Item (X - 1, Y).Contents = Mine then
Result := Result + 1;
end if;
-- below X-1:Y
if Y < Item'Last (2) then
if Item (X - 1, Y + 1).Contents = Mine then
Result := Result + 1;
end if;
end if;
end if;
-- above of X:Y
if Y > Item'First (2) then
if Item (X, Y - 1).Contents = Mine then
Result := Result + 1;
end if;
end if;
-- below of X:Y
if Y < Item'Last (2) then
if Item (X, Y + 1).Contents = Mine then
Result := Result + 1;
end if;
end if;
-- right of X:Y
if X < Item'Last (1) then
-- above X+1:Y
if Y > Item'First (2) then
if Item (X + 1, Y - 1).Contents = Mine then
Result := Result + 1;
end if;
end if;
-- X+1:Y
if Item (X + 1, Y).Contents = Mine then
Result := Result + 1;
end if;
-- below X+1:Y
if Y < Item'Last (2) then
if Item (X + 1, Y + 1).Contents = Mine then
Result := Result + 1;
end if;
end if;
end if;
return Result;
end Mines_Nearby;
-- outputs the grid
procedure Put (Item : Grid) is
Mines : Natural := 0;
begin
IO.Put (" ");
for X in Item'Range (1) loop
Nat_IO.Put (Item => X, Width => 3);
end loop;
IO.New_Line;
IO.Put (" +");
for X in Item'Range (1) loop
IO.Put ("---");
end loop;
IO.Put ('+');
IO.New_Line;
for Y in Item'Range (2) loop
Nat_IO.Put (Item => Y, Width => 3);
IO.Put ('|');
for X in Item'Range (1) loop
if Item (X, Y).Opened then
if Item (X, Y).Contents = Empty then
if Item (X, Y).Marked then
IO.Put (" - ");
else
Mines := Mines_Nearby (Item, X, Y);
if Mines > 0 then
Nat_IO.Put (Item => Mines, Width => 2);
IO.Put (' ');
else
IO.Put (" ");
end if;
end if;
else
if Item (X, Y).Marked then
IO.Put (" + ");
else
IO.Put (" X ");
end if;
end if;
elsif Item (X, Y).Marked then
IO.Put (" ? ");
else
IO.Put (" . ");
end if;
end loop;
IO.Put ('|');
IO.New_Line;
end loop;
IO.Put (" +");
for X in Item'Range (1) loop
IO.Put ("---");
end loop;
IO.Put ('+');
IO.New_Line;
end Put;
-- marks a field as possible bomb
procedure Mark (Item : in out Grid; X, Y : in Positive) is
begin
if Item (X, Y).Opened then
IO.Put_Line ("Field already open!");
else
Item (X, Y).Marked := not Item (X, Y).Marked;
end if;
end Mark;
-- clears a field and it's neighbours, if they don't have mines
procedure Clear
(Item : in out Grid;
X, Y : in Positive;
Killed : out Boolean)
is
-- clears the neighbours, if they don't have mines
procedure Clear_Neighbours (The_X, The_Y : Positive) is
begin
-- mark current field opened
Item (The_X, The_Y).Opened := True;
-- only proceed if neighbours don't have mines
if Mines_Nearby (Item, The_X, The_Y) = 0 then
-- left of X:Y
if The_X > Item'First (1) then
-- above X-1:Y
if The_Y > Item'First (2) then
if not Item (The_X - 1, The_Y - 1).Opened and
not Item (The_X - 1, The_Y - 1).Marked
then
Clear_Neighbours (The_X - 1, The_Y - 1);
end if;
end if;
-- X-1:Y
if not Item (The_X - 1, The_Y).Opened and
not Item (The_X - 1, The_Y).Marked
then
Clear_Neighbours (The_X - 1, The_Y);
end if;
-- below X-1:Y
if The_Y < Item'Last (2) then
if not Item (The_X - 1, The_Y + 1).Opened and
not Item (The_X - 1, The_Y + 1).Marked
then
Clear_Neighbours (The_X - 1, The_Y + 1);
end if;
end if;
end if;
-- above X:Y
if The_Y > Item'First (2) then
if not Item (The_X, The_Y - 1).Opened and
not Item (The_X, The_Y - 1).Marked
then
Clear_Neighbours (The_X, The_Y - 1);
end if;
end if;
-- below X:Y
if The_Y < Item'Last (2) then
if not Item (The_X, The_Y + 1).Opened and
not Item (The_X, The_Y + 1).Marked
then
Clear_Neighbours (The_X, The_Y + 1);
end if;
end if;
-- right of X:Y
if The_X < Item'Last (1) then
-- above X+1:Y
if The_Y > Item'First (2) then
if not Item (The_X + 1, The_Y - 1).Opened and
not Item (The_X + 1, The_Y - 1).Marked
then
Clear_Neighbours (The_X + 1, The_Y - 1);
end if;
end if;
-- X+1:Y
if not Item (The_X + 1, The_Y).Opened and
not Item (The_X + 1, The_Y).Marked
then
Clear_Neighbours (The_X + 1, The_Y);
end if;
-- below X+1:Y
if The_Y < Item'Last (2) then
if not Item (The_X + 1, The_Y + 1).Opened and
not Item (The_X + 1, The_Y + 1).Marked
then
Clear_Neighbours (The_X + 1, The_Y + 1);
end if;
end if;
end if;
end if;
end Clear_Neighbours;
begin
Killed := False;
-- only clear closed and unmarked fields
if Item (X, Y).Opened then
IO.Put_Line ("Field already open!");
elsif Item (X, Y).Marked then
IO.Put_Line ("Field already marked!");
else
Killed := Item (X, Y).Contents = Mine;
-- game over if killed, no need to clear
if not Killed then
Clear_Neighbours (X, Y);
end if;
end if;
end Clear;
-- marks all fields as open
procedure Open_All (Item : in out Grid) is
begin
for X in Item'Range (1) loop
for Y in Item'Range (2) loop
Item (X, Y).Opened := True;
end loop;
end loop;
end Open_All;
-- counts the number of marks
function Count_Marks (Item : Grid) return Natural is
Result : Natural := 0;
begin
for X in Item'Range (1) loop
for Y in Item'Range (2) loop
if Item (X, Y).Marked then
Result := Result + 1;
end if;
end loop;
end loop;
return Result;
end Count_Marks;
-- read and validate user input
procedure Get_Coordinates
(Max_X, Max_Y : Positive;
X, Y : out Positive;
Valid : out Boolean)
is
begin
Valid := False;
IO.Put ("X: ");
Nat_IO.Get (X);
IO.Put ("Y: ");
Nat_IO.Get (Y);
Valid := X > 0 and X <= Max_X and Y > 0 and Y <= Max_Y;
exception
when Constraint_Error =>
Valid := False;
end Get_Coordinates;
-- randomly place bombs
procedure Set_Bombs (Item : in out Grid; Max_X, Max_Y, Count : Positive) is
Generator : Nat_RNG.Generator;
X, Y : Positive;
begin
Nat_RNG.Reset (Generator);
for I in 1 .. Count loop
Placement : loop
X := Nat_RNG.Random (Generator) mod Max_X + 1;
Y := Nat_RNG.Random (Generator) mod Max_Y + 1;
-- redo placement if X:Y already full
if Item (X, Y).Contents = Empty then
Item (X, Y).Contents := Mine;
exit Placement;
end if;
end loop Placement;
end loop;
end Set_Bombs;
Width, Height : Positive;
begin
-- can be dynamically set
Width := 6;
Height := 4;
declare
The_Grid : Grid (1 .. Width, 1 .. Height);
-- 20% bombs
Bomb_Count : Positive := Width * Height * 20 / 100;
Finished : Boolean := False;
Action : Character;
Chosen_X, Chosen_Y : Positive;
Valid_Entry : Boolean;
begin
IO.Put ("Nr. Bombs: ");
Nat_IO.Put (Item => Bomb_Count, Width => 0);
IO.New_Line;
Set_Bombs
(Item => The_Grid,
Max_X => Width,
Max_Y => Height,
Count => Bomb_Count);
while not Finished and Count_Marks (The_Grid) /= Bomb_Count loop
Put (The_Grid);
IO.Put ("Input (c/m/r): ");
IO.Get (Action);
case Action is
when 'c' | 'C' =>
Get_Coordinates
(Max_X => Width,
Max_Y => Height,
X => Chosen_X,
Y => Chosen_Y,
Valid => Valid_Entry);
if Valid_Entry then
Clear
(Item => The_Grid,
X => Chosen_X,
Y => Chosen_Y,
Killed => Finished);
if Finished then
IO.Put_Line ("You stepped on a mine!");
end if;
else
IO.Put_Line ("Invalid input, retry!");
end if;
when 'm' | 'M' =>
Get_Coordinates
(Max_X => Width,
Max_Y => Height,
X => Chosen_X,
Y => Chosen_Y,
Valid => Valid_Entry);
if Valid_Entry then
Mark (Item => The_Grid, X => Chosen_X, Y => Chosen_Y);
else
IO.Put_Line ("Invalid input, retry!");
end if;
when 'r' | 'R' =>
Finished := True;
when others =>
IO.Put_Line ("Invalid input, retry!");
end case;
end loop;
Open_All (The_Grid);
IO.Put_Line
("Solution: (+ = correctly marked, - = incorrectly marked)");
Put (The_Grid);
end;
end Minesweeper;
AutoHotkey
Gui clone w/o graphic files but with cheats.
; Minesweeper.ahk - v1.0.6
; (c) Dec 28, 2008 by derRaphael
; Licensed under the Terms of EUPL 1.0
; Modded by Sobriquet and re-licenced as CeCILL v2
; Modded (again) by Sobriquet and re-licenced as GPL v1.3
#NoEnv
#NoTrayIcon
SetBatchLines,-1
#SingleInstance,Off
/*
[InGameSettings]
*/
Level =Beginner
Width =9
Height =9
MineMax =10
Marks =1
Color =1
Sound =0
BestTimeBeginner =999 seconds Anonymous
BestTimeIntermediate=999 seconds Anonymous
BestTimeExpert =999 seconds Anonymous
; above settings are accessed as variables AND modified by IniWrite - be careful
BlockSize =16
Title =Minesweeper
MineCount =0
mColor =Blue,Green,Red,Purple,Navy,Olive,Maroon,Teal
GameOver =0
TimePassed =0
; Add mines randomly
While (MineCount<MineMax) ; loop as long as neccessary
{
Random,x,1,%Width% ; get random horizontal position
Random,y,1,%Height% ; get random vertical position
If (T_x%x%y%y%!="M") ; only if not already a mine
T_x%x%y%y%:="M" ; assign as mine
,MineCount++ ; keep count
}
Gui +OwnDialogs
;Menu,Tray,Icon,C:\WINDOWS\system32\winmine.exe,1
Menu,GameMenu,Add,&New F2,NewGame
Menu,GameMenu,Add
Menu,GameMenu,Add,&Beginner,LevelMenu
Menu,GameMenu,Add,&Intermediate,LevelMenu
Menu,GameMenu,Add,&Expert,LevelMenu
Menu,GameMenu,Add,&Custom...,CustomMenu
Menu,GameMenu,Add
Menu,GameMenu,Add,&Marks (?),ToggleMenu
Menu,GameMenu,Add,Co&lor,ToggleMenu
Menu,GameMenu,Add,&Sound,ToggleMenu
Menu,GameMenu,Add
Menu,GameMenu,Add,Best &Times...,BestTimesMenu
Menu,GameMenu,Add
Menu,GameMenu,Add,E&xit,GuiClose
Menu,GameMenu,Check,% "&" level . (level="Custom" ? "..." : "")
If (Marks)
Menu,GameMenu,Check,&Marks (?)
If (Color)
Menu,GameMenu,Check,Co&lor
If (Sound)
Menu,GameMenu,Check,&Sound
Menu,HelpMenu,Add,&Contents F1,HelpMenu
Menu,HelpMenu,Add,&Search for Help on...,HelpMenu
Menu,HelpMenu,Add,Using &Help,HelpMenu
Menu,HelpMenu,Add
Menu,HelpMenu,Add,&About Minesweeper...,AboutMenu
Menu,MainMenu,Add,&Game,:GameMenu
Menu,MainMenu,Add,&Help,:HelpMenu
Gui,Menu,MainMenu
Gui,Font,s22,WingDings
Gui,Add,Button,% "h31 w31 y13 gNewGame vNewGame x" Width*BlockSize/2-4,K ; J=Smiley / K=Line / L=Frowny / m=Circle
Gui,Font,s16 Bold,Arial
Gui,Add,Text,% "x13 y14 w45 Border Center vMineCount c" (color ? "Red" : "White"),% SubStr("00" MineCount,-2) ; Remaining mine count
Gui,Add,Text,% "x" Width*BlockSize-34 " y14 w45 Border Center vTimer c" (color ? "Red" : "White"),000 ; Timer
; Buttons
Gui,Font,s11,WingDings
Loop,% Height ; iterate vertically
{
y:=A_Index ; remember line
Loop,% Width ; set covers
{
x:=A_Index
Gui,Add,Button,% "vB_x" x "y" y " w" BlockSize ; mark variable / width
. " h" BlockSize " " (x = 1 ; height / if 1st block
? "x" 12 " y" y*BlockSize+39 ; fix vertical offset
: "yp x+0") ; otherwise stay inline /w prev. box
}
}
; Show Gui
Gui,Show,% "w" ((Width>9 ? Width : 9))*BlockSize+24 " h" Height*BlockSize+68,%Title%
OnMessage(0x53,"WM_HELP") ; invoke Help handling for tooltips
Return ;--------------------------------------------------------; End of auto-execute section
#IfWinActive Minesweeper ahk_class AutoHotkeyGUI ; variable %title% not supported
StartGame(x,y) {
Global
If (TimePassed)
Return
If (T_x%x%y%y%="M") ; we'll save you this time
{
T_x%x%y%y%:="" ; remove this mine
Loop,% Height
{
y:=A_Index
Loop,% Width
{
x:=A_Index
If ( T_x%x%y%y%!="M") ; add it to first available non-mine square
T_x%x%y%y%:="M"
,break:=1
IfEqual,break,1,Break
}
IfEqual,break,1,Break
}
}
Loop,% Height ; iterate over height
{
y:=A_Index
Loop,% Width ; iterate over width
{
x:=A_Index
Gui,Font
If (T_x%x%y%y%="M") { ; check for mine
Gui,Font,s11,WingDings
hColor:="Black" ; set color for text
} Else {
Loop,3 { ; loop over neighbors: three columns vertically
ty:=y-2+A_Index ; calculate top offset
Loop,3 { ; and three horizontally
tx:=x-2+A_Index ; calculate left offset
If (T_x%tx%y%ty%="M") ; no mine and inbound
T_x%x%y%y%++ ; update hint txt
}
}
Gui,Font,s11 Bold,Courier New Bold
If (Color)
Loop,Parse,mColor,`, ; find color
If (T_x%x%y%y% = A_Index) { ; hinttxt to color
hColor:=A_LoopField ; set color for text
Break
}
Else hColor:="Black"
}
Gui,Add,Text,% "c" hColor " Border +Center vT_x" x "y" y ; set color / variable
. " w" BlockSize " h" BlockSize " " (x = 1 ; width / height / if 1st block
? "x" 12 " y" y*BlockSize+39 ; fix vertical position
: "yp x+0") ; otherwise align to previous box
,% T_x%x%y%y% ; M is WingDings for mine
GuiControl,Hide,T_x%x%y%y%
}
}
GuiControl,,Timer,% "00" . TimePassed:=1
SetTimer,UpdateTimer,1000 ; start timer
If (Sound)
SoundPlay,C:\WINDOWS\media\chord.wav
}
GuiSize:
If (TimePassed)
SetTimer,UpdateTimer,On ; for cheat below
Return
UpdateTimer: ; timer
GuiControl,,Timer,% (++TimePassed < 999) ? SubStr("00" TimePassed,-2) : 999
If (Sound)
SoundPlay,C:\WINDOWS\media\chord.wav
Return
Check(x,y){
Global
If (GameOver || B_x%x%y%y% != "" ; the game is over / prevent click on flagged squares and already-opened squares
|| x<1 || x>Width || y<1 || y>Height) ; do not check neighbor on illegal squares
Return
If (T_x%x%y%y%="") { ; empty field?
CheckNeighbor(x,y) ; uncover it and any neighbours
} Else If (T_x%x%y%y% != "M") { ; no Mine, ok?
GuiControl,Hide,B_x%x%y%y% ; hide covering button
GuiControl,Show,T_x%x%y%y%
B_x%x%y%y%:=1 ; remember we been here
UpdateChecks()
} Else { ; ewww ... it's a mine!
SetTimer,UpdateTimer,Off ; kill timer
GuiControl,,T_x%x%y%y%,N ; set to Jolly Roger=N to mark death location
GuiControl,,NewGame,L ; set Smiley Btn to Frown=L
RevealAll()
If (Sound) ; do we sound?
If (FileExist("C:\Program Files\Microsoft Office\Office12\MEDIA\Explode.wav"))
SoundPlay,C:\Program Files\Microsoft Office\Office12\MEDIA\Explode.wav
Else
SoundPlay,C:\WINDOWS\Media\notify.wav
}
}
CheckNeighbor(x,y) { ; This function checks neighbours of the clicked
Global ; field and uncovers empty fields plus adjacent hint fields
If (GameOver || B_x%x%y%y%!="")
Return
GuiControl,Hide,B_x%x%y%y% ; uncover it
GuiControl,Show,T_x%x%y%y%
B_x%x%y%y%:=1 ; remember it
UpdateChecks()
If (T_x%x%y%y%!="") ; is neighbour an nonchecked 0 value field?
Return
If (y-1>=1) ; check upper neighbour
CheckNeighbor(x,y-1)
If (y+1<=Height) ; check lower neighbour
CheckNeighbor(x,y+1)
If (x-1>=1) ; check left neighbour
CheckNeighbor(x-1,y)
If (x+1<=Width) ; check right neighbour
CheckNeighbor(x+1,y)
If (x-1>=1 && y-1>=1) ; check corner neighbour
CheckNeighbor(x-1,y-1)
If (x-1>=1 && y+1<=Height) ; check corner neighbour
CheckNeighbor(x-1,y+1)
If (x+1<=Width && y-1>=1) ; check corner neighbour
CheckNeighbor(x+1,y-1)
If (x+1<=Width && y+1<=Height) ; check corner neighbour
CheckNeighbor(x+1,y+1)
}
UpdateChecks() {
Global
MineCount:=MineMax, Die1:=Die2:=0
Loop,% Height
{
y:=A_Index
Loop,% Width
{
x:=A_Index
If (B_x%x%y%y%="O")
MineCount--
If (T_x%x%y%y%="M" && B_x%x%y%y%!="O") || (T_x%x%y%y%!="M" && B_x%x%y%y%="O")
Die1++
If (B_x%x%y%y%="" && T_x%x%y%y%!="M")
Die2++
}
}
GuiControl,,MineCount,% SubStr("00" MineCount,-2)
If (Die1 && Die2)
Return ; only get past here if flags+untouched squares match mines perfectly
SetTimer,UpdateTimer,Off ; game won - kill timer
GuiControl,,NewGame,J ; set Smiley Btn to Smile=J
RevealAll()
If (Sound) ; play sound
SoundPlay,C:\WINDOWS\Media\tada.wav
If (level!="Custom" && TimePassed < SubStr(BestTime%level%,1,InStr(BestTime%level%," ")-1) )
{
InputBox,name,Congratulations!,You have the fastest time`nfor level %level%.`nPlease enter your name.`n,,,,,,,,%A_UserName%
If (name && !ErrorLevel)
{
BestTime%level%:=%TimePassed% seconds %name%
IniWrite,BestTime%level%,%A_ScriptFullPath%,InGameSettings,BestTime%level%
}
}
}
RevealAll(){
Global
Loop,% Height ; uncover all
{
y:=A_Index
Loop,% Width
{
x:=A_Index
If(T_x%x%y%y%="M")
{
GuiControl,Hide,B_x%x%y%y%
GuiControl,Show,T_x%x%y%y%
}
}
}
GameOver:=True ; remember the game's over to block clicks
}
~F2::NewGame()
NewGame(){
NewGame:
Reload
ExitApp
}
LevelMenu:
If (A_ThisMenuItem="&Beginner")
level:="Beginner", Width:=9, Height:=9, MineMax:=10
Else If (A_ThisMenuItem="&Intermediate")
level:="Intermediate", Width:=16, Height:=16, MineMax:=40
Else If (A_ThisMenuItem="&Expert")
level:="Expert", Width:=30, Height:=16, MineMax:=99
IniWrite,%level%,%A_ScriptFullPath%,InGameSettings,level
IniWrite,%Width%,%A_ScriptFullPath%,InGameSettings,Width
IniWrite,%Height%,%A_ScriptFullPath%,InGameSettings,Height
IniWrite,%MineMax%,%A_ScriptFullPath%,InGameSettings,MineMax
IniWrite,%Marks%,%A_ScriptFullPath%,InGameSettings,Marks
IniWrite,%Sound%,%A_ScriptFullPath%,InGameSettings,Sound
IniWrite,%Color%,%A_ScriptFullPath%,InGameSettings,Color
NewGame()
Return
CustomMenu: ; label for setting window
Gui,2:+Owner1 ; make Gui#2 owned by gameWindow
Gui,2:Default ; set to default
Gui,1:+Disabled ; disable gameWindow
Gui,-MinimizeBox +E0x00000400
Gui,Add,Text,x15 y36 w38 h16,&Height:
Gui,Add,Edit,Number x60 y33 w38 h20 vHeight HwndHwndHeight,%Height% ; use inGame settings
Gui,Add,Text,x15 y60 w38 h16,&Width:
Gui,Add,Edit,Number x60 y57 w38 h20 vWidth HwndHwndWidth,%Width%
Gui,Add,Text,x15 y85 w38 h16,&Mines:
Gui,Add,Edit,Number x60 y81 w38 h20 vMineMax HwndHwndMines,%MineMax%
Gui,Add,Button,x120 y33 w60 h26 Default HwndHwndOK,OK
Gui,Add,Button,x120 y75 w60 h26 g2GuiClose HwndHwndCancel,Cancel ; jump directly to 2GuiClose label
Gui,Show,w195 h138,Custom Field
Return
2ButtonOK: ; label for OK button in settings window
Gui,2:Submit,NoHide
level:="Custom"
MineMax:=MineMax<10 ? 10 : MineMax>((Width-1)*(Height-1)) ? ((Width-1)*(Height-1)) : MineMax
Width:=Width<9 ? 9 : Width>30 ? 30 : Width
Height:=Height<9 ? 9 : Height>24 ? 24 : Height
GoSub,LevelMenu
Return
2GuiClose:
2GuiEscape:
Gui,1:-Disabled ; reset GUI status and destroy setting window
Gui,1:Default
Gui,2:Destroy
Return
ToggleMenu:
Toggle:=RegExReplace(RegExReplace(A_ThisMenuItem,"\s.*"),"&")
temp:=%Toggle%
%Toggle%:=!%Toggle%
Menu,GameMenu,ToggleCheck,%A_ThisMenuItem%
IniWrite,% %Toggle%,%A_ScriptFullPath%,InGameSettings,%Toggle%
Return
BestTimesMenu:
Gui,3:+Owner1
Gui,3:Default
Gui,1:+Disabled
Gui,-MinimizeBox +E0x00000400
Gui,Add,Text,x15 y22 w250 vBestTimeBeginner HwndHwndBTB,Beginner: %BestTimeBeginner%
Gui,Add,Text,x15 y38 w250 vBestTimeIntermediate HwndHwndBTI,Intermediate: %BestTimeIntermediate%
Gui,Add,Text,x15 y54 w250 vBestTimeExpert HwndHwndBTE,Expert: %BestTimeExpert%
Gui,Add,Button,x38 y89 w75 h20 HwndHwndRS,&Reset Scores
Gui,Add,Button,x173 y89 w45 h20 Default HwndHwndOK,OK
Gui,Show,w255 h122,Fastest Mine Sweepers
Return
3ButtonResetScores:
BestTimeBeginner =999 seconds Anonymous
BestTimeIntermediate=999 seconds Anonymous
BestTimeExpert =999 seconds Anonymous
GuiControl,,BestTimeBeginner,Beginner: %BestTimeBeginner%`nIntermediate: %BestTimeIntermediate%`nExpert: %BestTimeExpert%
GuiControl,,BestTimeIntermediate,Intermediate: %BestTimeIntermediate%`nExpert: %BestTimeExpert%
GuiControl,,BestTimeExpert,Expert: %BestTimeExpert%
IniWrite,%BestTimeBeginner%,%A_ScriptFullPath%,InGameSettings,BestTimeBeginner
IniWrite,%BestTimeIntermediate%,%A_ScriptFullPath%,InGameSettings,BestTimeIntermediate
IniWrite,%BestTimeExpert%,%A_ScriptFullPath%,InGameSettings,BestTimeExpert
3GuiEscape: ; fall through:
3GuiClose:
3ButtonOK:
Gui,1:-Disabled ; reset GUI status and destroy setting window
Gui,1:Default
Gui,3:Destroy
Return
^q::
GuiClose:
ExitApp
HelpMenu:
;Run C:\WINDOWS\Help\winmine.chm
Return
AboutMenu:
Msgbox,64,About Minesweeper,AutoHotkey (r) Minesweeper`nVersion 1.0.6`nCopyright (c) 2014`nby derRaphael and Sobriquet
Return
~LButton::
~!LButton::
~^LButton::
~#LButton::
Tooltip
If (GetKeyState("RButton","P"))
Return
If A_OSVersion not in WIN_2003,WIN_XP,WIN_2000,WIN_NT4,WIN_95,WIN_98,WIN_ME
If(A_PriorHotkey="~*LButton Up" && A_TimeSincePriorHotkey<100)
GoTo,*MButton Up ; invoke MButton handling for autofill on left double-click in vista+
If (GameOver)
Return
MouseGetPos,,y
If (y>47)
GuiControl,,NewGame,m
Return
~*LButton Up::
If (GetKeyState("RButton","P"))
GoTo,*MButton Up
If (GameOver)
Return
GuiControl,,NewGame,K
control:=GetControlFromClassNN(), NumX:=NumY:=0
RegExMatch(control,"Si)T_x(?P<X>\d+)y(?P<Y>\d+)",Num) ; get Position
StartGame(NumX,NumY) ; start game if neccessary
Check(NumX,NumY)
Return
+LButton::
*MButton::
Tooltip
If (GameOver)
Return
MouseGetPos,,y
If (y>47)
GuiControl,,NewGame,m
Return
+LButton Up::
*MButton Up::
If (GameOver)
Return
GuiControl,,NewGame,K
StartGame(NumX,NumY) ; start game if neccessary
If (GetKeyState("Esc","P"))
{
SetTimer,UpdateTimer,Off
Return
}
control:=GetControlFromClassNN(), NumX:=NumY:=0
RegExMatch(control,"Si)T_x(?P<X>\d+)y(?P<y>\d+)",Num)
If ( !NumX || !NumY || B_x%NumX%y%NumY%!=1)
Return
temp:=0
Loop,3 { ; loop over neighbors: three columns vertically
ty:=NumY-2+A_Index ; calculate top offset
Loop,3 { ; and three horizontally
tx:=NumX-2+A_Index ; calculate left offset
If (B_x%tx%y%ty% = "O") ; count number of marked mines around position
temp++
}
}
If (temp=T_x%NumX%y%NumY%)
Loop,3 { ; loop over neighbors: three columns vertically
ty:=NumY-2+A_Index ; calculate top offset
Loop,3 { ; and three horizontally
tx:=NumX-2+A_Index ; calculate left offset
Check(tx,ty) ; simulate user clicking on each surrounding square
}
}
Return
~*RButton::
If (GameOver || GetKeyState("LButton","P"))
Return
control:=GetControlFromClassNN(), NumX:=NumY:=0
RegExMatch(control,"Si)T_x(?P<X>\d+)y(?P<y>\d+)",Num) ; grab 'em
If ( !NumX || !NumY || B_x%NumX%y%NumY%=1)
Return
StartGame(NumX,NumY) ; start counter if neccessary
B_x%NumX%y%NumY%:=(B_x%NumX%y%NumY%="" ? "O" : (B_x%NumX%y%NumY%="O" && Marks=1 ? "I" : ""))
GuiControl,,B_x%NumX%y%NumY%,% B_x%NumX%y%NumY%
UpdateChecks()
Return
~*RButton Up::
If (GetKeyState("LButton","P"))
GoTo,*MButton Up
Return
GetControlFromClassNN(){
Global width
MouseGetPos,,,,control
If (SubStr(control,1,6)="Button")
NumX:=mod(SubStr(control,7)-2,width)+1,NumY:=(SubStr(control,7)-2)//width+1
Else If (SubStr(control,1,6)="Static")
NumX:=mod(SubStr(control,7)-3,width)+1,NumY:=(SubStr(control,7)-3)//width+1
Return "T_x" NumX "y" NumY
}
WM_HELP(_,_lphi)
{
Global
hItemHandle:=NumGet(_lphi+0,12)
If (hItemHandle=HwndBTB || hItemHandle=HwndBTI || hItemHandle=HwndBTE)
ToolTip Displays a player's best game time`, and the player's name`, for`neach level of play: Beginner`, Intermediate`, and Expert.
Else If (hItemHandle=HwndRS)
ToolTip Click to clear the current high scores.
Else If (hItemHandle=HwndOK)
ToolTip Closes the dialog box and saves any changes you`nhave made.
Else If (hItemHandle=HwndCancel)
ToolTip Closes the dialog box without saving any changes you have`nmade.
Else If (hItemHandle=HwndHeight)
ToolTip Specifies the number of vertical squares on the`nplaying field.
Else If (hItemHandle=HwndWidth)
ToolTip Specifies the number of horizontal squares on the`nplaying field.
Else If (hItemHandle=HwndMines)
ToolTip Specifies the number of mines to be placed on the`nplaying field.
}
:*?B0:xyzzy:: ; cheat
KeyWait,Shift,D T1 ; 1 sec grace period to press shift
If (ErrorLevel)
Return
While,GetKeyState("Shift","P")
{
control:=GetControlFromClassNN()
If (%control% = "M")
SplashImage,,B X0 Y0 W1 H1 CW000000 ; black pixel
Else
SplashImage,,B X0 Y0 W1 H1 CWFFFFFF ; white pixel
Sleep 100
}
SplashImage,Off
Return
/*
Version History
=================
Dec 29, 2008
1.0.0 Initial Release
1.0.1 BugFix
- Game Restart Issues mentioned by Frankie
- Guess count fixup I
- Startbehaviour of game (time didnt start when 1st click was RMB Guess)
Dec 30, 2008
1.0.2 BugFix
- Field Size Control vs Max MineCount / mentioned by Ā°digitĀ° / IsNull
1.0.3 BugFix
- Guess count fixup II mentioned by Ā°digitĀ°
- Corrected count when 'guessed' field uncovered
Dec 31, 2008
1.0.4 BugFix
- Fix of Min Field & MineSettings
Mar 7, 2014
1.0.5 AddFeatures
- Make appearance more like original
- Make first click always safe
- Re-license as CeCILL v2
Mar 8, 2014
1.0.6 AddFeatures
- Add cheat code
- Add middle-button shortcut
- Re-license as GPL 1.3
*/
BASIC
BASIC256
Mouse version
N = 6 : M = 5 : H = 25 : P = 0.2
fastgraphics
graphsize N*H,(M+1)*H
font "Arial",H/2+1,75
dim f(N,M) # 1 open, 2 mine, 4 expected mine
dim s(N,M) # count of mines in a neighborhood
trian1 = {1,1,H-1,1,H-1,H-1} : trian2 = {1,1,1,H-1,H-1,H-1}
mine = {2,2, H/2,H/2-2, H-2,2, H/2+2,H/2, H-2,H-2, H/2,H/2+2, 2,H-2, H/2-2,H/2}
flag = {H/2-1,3, H/2+1,3, H-4,H/5, H/2+1,H*2/5, H/2+1,H*0.9-2, H*0.8,H-2, H*0.2,H-2, H/2-1,H*0.9-2}
mines = int(N*M*P) : k = mines : act = 0
while k>0
i = int(rand*N) : j = int(rand*M)
if not f[i,j] then
f[i,j] = 2 : k = k - 1 # set mine
s[i,j] = s[i,j] + 1 : gosub adj # count it
end if
end while
togo = M*N-mines : over = 0 : act = 1
gosub redraw
while not over
clickclear
while not clickb
pause 0.01
end while
i = int(clickx/H) : j = int(clicky/H)
if i<N and j<M then
if clickb=1 then
if not (f[i,j]&4) then ai = i : aj = j : gosub opencell
if not s[i,j] then gosub adj
else
if not (f[i,j]&1) then
if f[i,j]&4 then mines = mines+1
if not (f[i,j]&4) then mines = mines-1
f[i,j] = (f[i,j]&~4)|(~f[i,j]&4)
end if
end if
if not (togo or mines) then over = 1
gosub redraw
end if
end while
imgsave "Minesweeper_game_BASIC-256.png", "PNG"
end
redraw:
for i = 0 to N-1
for j = 0 to M-1
if over=-1 and f[i,j]&2 then f[i,j] = f[i,j]|1
gosub drawcell
next j
next i
# Counter
color (32,32,32) : rect 0,M*H,N*H,H
color white : x = 5 : y = M*H+H*0.05
if not over then text x,y,"Mines: " + mines
if over=1 then text x,y,"You won!"
if over=-1 then text x,y,"You lost"
refresh
return
drawcell:
color darkgrey
rect i*H,j*H,H,H
if f[i,j]&1=0 then # closed
color black : stamp i*H,j*H,trian1
color white : stamp i*H,j*H,trian2
color grey : rect i*H+2,j*H+2,H-4,H-4
if f[i,j]&4 then color blue : stamp i*H,j*H,flag
else
color 192,192,192 : rect i*H+1,j*H+1,H-2,H-2
# Draw
if f[i,j]&2 then # mine
if not (f[i,j]&4) then color red
if f[i,j]&4 then color darkgreen
circle i*H+H/2,j*H+H/2,H/5 : stamp i*H,j*H,mine
else
if s[i,j] then color (32,32,32) : text i*H+H/3,j*H+1,s[i,j]
end if
end if
return
adj:
aj = j-1
if j and i then ai = i-1 : gosub adjact
if j then ai = i : gosub adjact
if j and i<N-1 then ai = i+1 : gosub adjact
aj = j
if i then ai = i-1 : gosub adjact
if i<N-1 then ai = i+1 : gosub adjact
aj = j+1
if j<M-1 and i then ai = i-1 : gosub adjact
if j<M-1 then ai = i : gosub adjact
if j<M-1 and i<N-1 then ai = i+1 : gosub adjact
return
adjact:
if not act then s[ai,aj] = s[ai,aj]+1 : return
if act then gosub opencell : return
opencell:
if not (f[ai,aj]&1) then
f[ai,aj] = f[ai,aj]|1
togo = togo-1
end if
if f[ai,aj]&2 then over = -1
return
BCPL
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)
$)
- Output:
Minesweeper ----------- Random seed? 50 Width (4-64)? 6 Height (4-22)? 4 Bombs: 4 - Flagged: 0 - Hidden: 24 +------+ |......| |......| |......| |......| +------+ R)eveal, F)lag, Q)uit? r Row? 1 Column? 1 Bombs: 4 - Flagged: 0 - Hidden: 16 +------+ | 1....| | 2....| | 2....| | 1....| +------+ R)eveal, F)lag, Q)uit? r Row? 4 Column? 6 Bombs: 4 - Flagged: 0 - Hidden: 10 +------+ | 1....| | 2....| | 2.311| | 1.1 | +------+ R)eveal, F)lag, Q)uit? r Row? 1 Column? 3 Bombs: 4 - Flagged: 0 - Hidden: 9 +------+ | 11...| | 2....| | 2.311| | 1.1 | +------+ R)eveal, F)lag, Q)uit? r Row? 4 Column? 3 Bombs: 4 - Flagged: 0 - Hidden: 8 +------+ | 11...| | 2....| | 2.311| | 111 | +------+ R)eveal, F)lag, Q)uit? f Row? 2 Column? 3 Bombs: 4 - Flagged: 1 - Hidden: 8 +------+ | 11...| | 2?...| | 2.311| | 111 | +------+ R)eveal, F)lag, Q)uit? f Row? 3 Column? 3 Bombs: 4 - Flagged: 2 - Hidden: 8 +------+ | 11...| | 2?...| | 2?311| | 111 | +------+ R)eveal, F)lag, Q)uit? r Row? 1 Column? 4 Bombs: 4 - Flagged: 2 - Hidden: 7 +------+ | 112..| | 2?...| | 2?311| | 111 | +------+ R)eveal, F)lag, Q)uit? r Row? 2 Column? 4 Bombs: 4 - Flagged: 2 - Hidden: 6 +------+ | 112..| | 2?3..| | 2?311| | 111 | +------+ R)eveal, F)lag, Q)uit? f Row? 2 Column? 5 Bombs: 4 - Flagged: 3 - Hidden: 6 +------+ | 112..| | 2?3?.| | 2?311| | 111 | +------+ R)eveal, F)lag, Q)uit? r Row? 2 Column? 6 Bombs: 4 - Flagged: 3 - Hidden: 5 +------+ | 112..| | 2?3?2| | 2?311| | 111 | +------+ R)eveal, F)lag, Q)uit? r Row? 1 Column? 5 Bombs: 4 - Flagged: 3 - Hidden: 4 +------+ | 1122.| | 2?3?2| | 2?311| | 111 | +------+ R)eveal, F)lag, Q)uit? f Row? 1 Column? 6 Bombs: 4 - Flagged: 4 - Hidden: 4 +------+ | 1122?| | 2?3?2| | 2?311| | 111 | +------+ You win!
C
dwlmines uses curses rather than battleship coordinate input, giving the game a gui feel driven with the keyboard rather than the mouse, played in a character cell terminal rather than a window filled with glorious new icons. On ubuntu linux distribution this command might be that I used to install curses: $ sudo apt-get install libncurses-dev
#if 0
Unix build:
make CPPFLAGS=-DNDEBUG LDLIBS=-lcurses mines
dwlmines, by David Lambert; sometime in the twentieth Century. The
program is meant to run in a terminal window compatible with curses
if unix is defined to cpp, or to an ANSI terminal when compiled
without unix macro defined. I suppose I have built this on a
windows 98 computer using gcc running in a cmd window. The original
probably came from a VAX running VMS with a vt100 sort of terminal.
Today I have xterm and gcc available so I will claim only that it
works with this combination.
As this program can automatically play all the trivially counted
safe squares. Action is quick leaving the player with only the
thoughtful action. Whereas 's' steps on the spot with the cursor,
capital 'S' (Stomp) invokes autoplay.
The cursor motion keys are as in the vi editor; hjkl move the cursor.
'd' displays the number of unclaimed bombs and cells.
'f' flags a cell.
The numbers on the field indicate the number of bombs in the
unclaimed neighboring cells. This is more useful than showing the
values you expect. You may find unflagging a cell adjacent to a
number will help you understand this.
There is extra code here. The multidimensional array allocator
allocarray is much better than those of Numerical Recipes in C. If
you subtracted the offset 1 to make the arrays FORTRAN like then
allocarray could substitute for those of NR in C.
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifndef NDEBUG
# define DEBUG_CODE(A) A
#else
# define DEBUG_CODE(A)
#endif
#include <time.h>
#define DIM(A) (sizeof((A))/sizeof(*(A)))
#define MAX(A,B) ((A)<(B)?(B):(A))
#define BIND(A,L,H) ((L)<(A)?(A)<(H)?(A):(H):(L))
#define SET_BIT(A,B) ((A)|=1<<(B))
#define CLR_BIT(A,B) ((A)&=~(1<<(B)))
#define TGL_BIT(A,B) ((A)^=1<<(B))
#define INQ_BIT(A,B) ((A)&(1<<(B)))
#define FOREVER for(;;)
#define MODINC(i,mod) ((i)+1<(mod)?(i)+1:0)
#define MODDEC(i,mod) (((i)<=0?(mod):(i))-1)
void error(int status,const char *message) {
fprintf(stderr, "%s\n", message);
exit(status);
}
void*dwlcalloc(int n,size_t bytes) {
void*rv = (void*)calloc(n,bytes);
if (NULL == rv)
error(1,"memory allocation failure");
DEBUG_CODE(fprintf(stderr,"allocated address %p\n",rv);)
return rv;
}
void*allocarray(int rank,size_t*shape,size_t itemSize) {
/*
Allocates arbitrary dimensional arrays (and inits all pointers)
with only 1 call to malloc. David W. Lambert, written before 1990.
This is wonderful because one only need call free once to deallocate
the space. Special routines for each size array are not need for
allocation of for deallocation. Also calls to malloc might be expensive
because they might have to place operating system requests. One call
seems optimal.
*/
size_t size,i,j,dataSpace,pointerSpace,pointers,nextLevelIncrement;
char*memory,*pc,*nextpc;
if (rank < 2) {
if (rank < 0)
error(1,"invalid negative rank argument passed to allocarray");
size = rank < 1 ? 1 : *shape;
return dwlcalloc(size,itemSize);
}
pointerSpace = 0, dataSpace = 1;
for (i = 0; i < rank-1; ++i)
pointerSpace += (dataSpace *= shape[i]);
pointerSpace *= sizeof(char*);
dataSpace *= shape[i]*itemSize;
memory = pc = dwlcalloc(1,pointerSpace+dataSpace);
pointers = 1;
for (i = 0; i < rank-2; ) {
nextpc = pc + (pointers *= shape[i])*sizeof(char*);
nextLevelIncrement = shape[++i]*sizeof(char*);
for (j = 0; j < pointers; ++j)
*((char**)pc) = nextpc, pc+=sizeof(char*), nextpc += nextLevelIncrement;
}
nextpc = pc + (pointers *= shape[i])*sizeof(char*);
nextLevelIncrement = shape[++i]*itemSize;
for (j = 0; j < pointers; ++j)
*((char**)pc) = nextpc, pc+=sizeof(char*), nextpc += nextLevelIncrement;
return memory;
}
#define PRINT(element) \
if (NULL == print_elt) \
printf("%10.3e",*(double*)(element)); \
else \
(*print_elt)(element)
/* matprint prints an array in APL\360 style */
/* with a NULL element printing function matprint assumes an array of double */
void matprint(void*a,int rank,size_t*shape,size_t size,void(*print_elt)()) {
union {
unsigned **ppu;
unsigned *pu;
unsigned u;
} b;
int i;
if (rank <= 0 || NULL == shape)
PRINT(a);
else if (1 < rank) {
for (i = 0; i < shape[0]; ++i)
matprint(((void**)a)[i], rank-1,shape+1,size,print_elt);
putchar('\n');
for (i = 0, b.pu = a; i < shape[0]; ++i, b.u += size) {
PRINT(b.pu);
putchar(' ');
}
}
}
#ifdef __unix__
# include <curses.h>
# include <unistd.h>
# define SRANDOM srandom
# define RANDOM random
#else
# include <windows.h>
void addch(int c) { putchar(c); }
void addstr(const char*s) { fputs(s,stdout); }
# define ANSI putchar(27),putchar('[')
void initscr(void) { printf("%d\n",AllocConsole()); }
void cbreak(void) { ; }
void noecho(void) { ; }
void nonl(void) { ; }
int move(int r,int c) { ANSI; return printf("%d;%dH",r+1,c+1); }
int mvaddch(int r,int c,int ch) { move(r,c); addch(ch); }
void refresh(void) { ; }
# define WA_STANDOUT 32
int attr_on(int a,void*p) { ANSI; return printf("%dm",a); }
int attr_off(int a,void*p) { attr_on(0,NULL); }
# include <stdarg.h>
void printw(const char*fmt,...) {
va_list args;
va_start(args,fmt);
vprintf(fmt,args);
va_end(args);
}
void clrtoeol(void) {
ANSI;addstr("0J");
}
# define SRANDOM srand
# define RANDOM rand
#endif
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 1 /* just a guess */
#endif
#if 0
cell status
UNKN --- contains virgin earth (initial state)
MINE --- has a mine
FLAG --- was flagged
#endif
enum {UNKN,MINE,FLAG}; /* bit numbers */
#define DETECT(CELL,PROPERTY) (!!INQ_BIT(CELL,PROPERTY))
DEBUG_CODE( \
void pchr(void*a) { /* odd comment removed */ \
putchar('A'+*(char*a)); /* should print the nth char of alphabet */\
} /* where 'A' is 0 */ \
)
char**bd; /* the board */
size_t shape[2];
#define RWS (shape[0])
#define CLS (shape[1])
void populate(int x,int y,int pct) { /* finished size in col, row, % mines */
int i,j,c;
x = BIND(x,4,200), y = BIND(y,4,400); /* confine input as piecewise linear */
shape[0] = x+2, shape[1] = y+2; bd = (char**)allocarray(2,shape,sizeof(char));
memset(*bd,1<<UNKN,shape[0]*shape[1]*sizeof(char)); /* all unknown */
for (i = 0; i < shape[0]; ++i) /* border is safe */
bd[i][0] = bd[i][shape[1]-1] = 0;
for (i = 0; i < shape[1]; ++i)
bd[0][i] = bd[shape[0]-1][i] = 0;
{
time_t seed; /* now I would choose /dev/random */
printf("seed is %u\n",(unsigned)seed);
time(&seed), SRANDOM((unsigned)seed);
}
c = BIND(pct,1,99)*x*y/100; /* number of mines to set */
while(c) {
i = RANDOM(), j = 1+i%y, i = 1+(i>>16)%x;
if (! DETECT(bd[i][j],MINE)) /* 1 mine per site */
--c, SET_BIT(bd[i][j],MINE);
}
DEBUG_CODE(matprint(bd,2,shape,sizeof(int),pchr);)
RWS = x+1, CLS = y+1; /* shape now stores the upper bounds */
}
struct {
int i,j;
} neighbor[] = {
{-1,-1}, {-1, 0}, {-1, 1},
{ 0,-1}, /*home*/ { 0, 1},
{ 1,-1}, { 1, 0}, { 1, 1}
};
/* NEIGHBOR seems to map 0..8 to local 2D positions */
#define NEIGHBOR(I,J,K) (bd[(I)+neighbor[K].i][(J)+neighbor[K].j])
int cnx(int i,int j,char w) { /* count neighbors with property w */
int k,c = 0;
for (k = 0; k < DIM(neighbor); ++k)
c += DETECT(NEIGHBOR(i,j,k),w);
return c;
}
int row,col;
#define ME bd[row+1][col+1]
int step(void) {
if (DETECT(ME,FLAG)) return 1; /* flags offer protection */
if (DETECT(ME,MINE)) return 0; /* lose */
CLR_BIT(ME,UNKN);
return 1;
}
int autoplay(void) {
int i,j,k,change,m;
if (!step()) return 0;
do /* while changing */
for (change = 0, i = 1; i < RWS; ++i)
for (j = 1; j < CLS; ++j)
if (!DETECT(bd[i][j],UNKN)) { /* consider nghbrs of safe cells */
m = cnx(i,j,MINE);
if (cnx(i,j,FLAG) == m) { /* mines appear flagged */
for (k = 0; k < DIM(neighbor); ++k)
if (DETECT(NEIGHBOR(i,j,k),UNKN)&&!DETECT(NEIGHBOR(i,j,k),FLAG)) {
if (DETECT(NEIGHBOR(i,j,k),MINE)) { /* OOPS! */
row = i+neighbor[k].i-1, col = j+neighbor[k].j-1;
return 0;
}
change = 1, CLR_BIT(NEIGHBOR(i,j,k),UNKN);
}
} else if (cnx(i,j,UNKN) == m)
for (k = 0; k < DIM(neighbor); ++k)
if (DETECT(NEIGHBOR(i,j,k),UNKN))
change = 1, SET_BIT(NEIGHBOR(i,j,k),FLAG);
}
while (change);
return 1;
}
void takedisplay(void) { initscr(), cbreak(), noecho(), nonl(); }
void help(void) {
move(RWS,1),clrtoeol(), printw("move:hjkl flag:Ff step:Ss other:qd?");
}
void draw(void) {
int i,j,w;
const char*s1 = " 12345678";
move(1,1);
for (i = 1; i < RWS; ++i, addstr("\n "))
for (j = 1; j < CLS; ++j, addch(' ')) {
w = bd[i][j];
if (!DETECT(w,UNKN)) {
w = cnx(i,j,MINE)-cnx(i,j,FLAG);
if (w < 0) attr_on(WA_STANDOUT,NULL), w = -w;
addch(s1[w]);
attr_off(WA_STANDOUT,NULL);
}
else if (DETECT(w,FLAG)) addch('F');
else addch('*');
}
move(row+1,2*col+1);
refresh();
}
void show(int win) {
int i,j,w;
const char*s1 = " 12345678";
move(1,1);
for (i = 1; i < RWS; ++i, addstr("\n "))
for (j = 1; j < CLS; ++j, addch(' ')) {
w = bd[i][j];
if (!DETECT(w,UNKN)) {
w = cnx(i,j,MINE)-cnx(i,j,FLAG);
if (w < 0) attr_on(WA_STANDOUT,NULL), w = -w;
addch(s1[w]);
attr_off(WA_STANDOUT,NULL);
}
else if (DETECT(w,FLAG))
if (DETECT(w,MINE)) addch('F');
else attr_on(WA_STANDOUT,NULL), addch('F'),attr_off(WA_STANDOUT,NULL);
else if (DETECT(w,MINE)) addch('M');
else addch('*');
}
mvaddch(row+1,2*col,'('), mvaddch(row+1,2*(col+1),')');
move(RWS,0);
refresh();
}
#define HINTBIT(W) s3[DETECT(bd[r][c],(W))]
#define NEIGCNT(W) s4[cnx(r,c,(W))]
const char*s3="01", *s4="012345678";
void dbg(int r, int c) {
int i,j,unkns=0,mines=0,flags=0,pct;
char o[6];
static int hint;
for (i = 1; i < RWS; ++i)
for (j = 1; j < CLS; ++j)
unkns += DETECT(bd[i][j],UNKN),
mines += DETECT(bd[i][j],MINE),
flags += DETECT(bd[i][j],FLAG);
move(RWS,1), clrtoeol();
pct = 0.5+100.0*(mines-flags)/MAX(1,unkns-flags);
if (++hint<4)
o[0] = HINTBIT(UNKN), o[1] = HINTBIT(MINE), o[2] = HINTBIT(FLAG),
o[3] = HINTBIT(UNKN), o[4] = NEIGCNT(MINE), o[5] = NEIGCNT(FLAG);
else
memset(o,'?',sizeof(o));
printw("(%c%c%c) u=%c, m=%c, f=%c, %d/%d (%d%%) remain.",
o[0],o[1],o[2],o[3],o[4],o[5],mines-flags,unkns-flags,pct);
}
#undef NEIGCNT
#undef HINTBIT
void toggleflag(void) {
if (DETECT(ME,UNKN))
TGL_BIT(ME,FLAG);
}
int sureflag(void) {
toggleflag();
return autoplay();
}
int play(int*win) {
int c = getch(), d = tolower(c);
if ('q' == d) return 0;
else if ('?' == c) help();
else if ('h' == d) col = MODDEC(col,CLS-1);
else if ('l' == d) col = MODINC(col,CLS-1);
else if ('k' == d) row = MODDEC(row,RWS-1);
else if ('j' == d) row = MODINC(row,RWS-1);
else if ('f' == c) toggleflag();
else if ('s' == c) return *win = step();
else if ('S' == c) return *win = autoplay();
else if ('F' == c) return *win = sureflag();
else if ('d' == d) dbg(row+1,col+1);
return 1;
}
int convert(const char*name,const char*s) {
if (strlen(s) == strspn(s,"0123456789"))
return atoi(s);
fprintf(stderr," use: %s [rows [columns [percentBombs]]]\n",name);
fprintf(stderr,"default: %s 20 30 25\n",name);
exit(EXIT_SUCCESS);
}
void parse_command_line(int ac,char*av[],int*a,int*b,int*c) {
switch (ac) {
default:
case 4: *c = convert(*av,av[3]);
case 3: *b = convert(*av,av[2]);
case 2: *a = convert(*av,av[1]);
case 1: ;
}
}
int main(int ac,char*av[],char*env[]) {
int win = 1, rows = 20, cols = 30, prct = 25;
parse_command_line(ac,av,&rows,&cols,&prct);
populate(rows,cols,prct);
takedisplay();
while(draw(), play(&win));
show(win);
free(bd);
# ifdef __unix__
{
const char*s = "/bin/stty";
execl(s,s,"sane",(const char*)NULL);
}
# endif
return 0;
}
Mouse version
Using ncurses and mouse input. Compiled with gcc -lncurses -Wall -std=c99
. Run as a.out [height] [width]
; your terminal needs to support mouse input, and at least 2*width + 2 columns wide. Left button clears a cell, right button toggles mine mark, middle button on a cleared cell clears all neighboring cells (or blow up if there are unmarked mines). When mine count drops to zero, click "claim victory" to win the game or blow up.
#include <ncurses.h>
#include <locale.h>
#include <stdlib.h>
int width = 0, height = 0;
int mine_ratio = 10, n_mines;
int reveal = 0;
WINDOW *win, *wrap;
enum {
M_NONE = 0,
M_CLEARED = 1 << 0,
M_MARKED = 1 << 1,
M_MINED = 1 << 2,
M_BOMBED = 1 << 3,
};
typedef struct { unsigned short flag, cnt; } mine_t;
#define for_i for (int i = 0; i < height; i++)
#define for_j for (int j = 0; j < width; j++)
void init_mines(void * ptr)
{
mine_t (*m)[width] = ptr;
for_i for_j
if (rand() % mine_ratio)
m[i][j].flag = M_NONE;
else {
m[i][j].flag = M_MINED;
n_mines ++;
}
for_i for_j {
m[i][j].cnt = 0;
for (int x = j - 1; x <= j + 1; x++) {
if (x < 0 || x > width) continue;
for (int y = i - 1; y <= i + 1; y++) {
if (y < 0 || y >= width) continue;
m[i][j].cnt += 1 && (m[y][x].flag & M_MINED);
}
}
}
}
int mine_clear(void *ptr, int x, int y, int mass_clear)
{
mine_t (*m)[width] = ptr;
unsigned short flag;
if (x < 0 || x >= width || y < 0 || y >= height)
return 1;
flag = m[y][x].flag;
if (((flag & M_CLEARED) && 1) != mass_clear) return 1;
if ((flag & M_MINED) && !(flag & M_MARKED)) {
m[y][x].flag |= M_BOMBED;
reveal = 1;
return 0;
}
if (!(flag & M_MARKED))
flag = (m[y][x].flag |= M_CLEARED);
if (m[y][x].cnt && !mass_clear) return 1;
if (flag & M_MARKED) return 1;
for (int i = y - 1; i <= y + 1; i++)
for (int j = x - 1; j <= x + 1; j++)
if (!mine_clear(ptr, j, i, 0)) return 0;
return 1;
}
void mine_mark(void *ptr, int x, int y)
{
mine_t (*m)[width] = ptr;
if (m[y][x].flag & M_CLEARED) return;
if (m[y][x].flag & M_MARKED)
n_mines ++;
else
n_mines --;
m[y][x].flag ^= M_MARKED;
}
int check_wining(void *ptr)
{
mine_t (*m)[width] = ptr;
int good = 1;
for_i for_j {
int f = m[i][j].flag;
if ((f & M_MINED) && !(f & M_MARKED)) {
m[i][j].flag = M_BOMBED;
good = 0;
}
}
mvwprintw(wrap, height + 1, 0, good ? "All clear! " : "BOOM! ");
reveal = 1;
return good;
}
void repaint(void *ptr)
{
mine_t (*m)[width] = ptr, *p;
box(win, 0, 0);
for_i for_j {
char c;
p = &m[i][j];
int f = p->flag;
if (reveal)
c = (f & M_BOMBED) ? 'X' : (f & M_MINED) ? 'o' : ' ';
else if (p->flag & M_BOMBED)
c = 'X';
else if (p->flag & M_MARKED)
c = '?';
else if (p->flag & M_CLEARED)
c = p->cnt ? p->cnt + '0' : ' ';
else
c = '.';
mvwprintw(win, i + 1, 2 * j + 1, " %c", c);
}
if (reveal);
else if (n_mines)
mvwprintw(wrap, height + 1, 0, "Mines:%6d ", n_mines);
else
mvwprintw(wrap, height + 1, 0, "Claim victory? ");
wrefresh(wrap);
wrefresh(win);
}
int main(int c, char **v)
{
MEVENT evt;
printf("%d\n", c);
if (c >= 3) {
height = atoi(v[1]);
width = atoi(v[2]);
}
if (height < 3) height = 15;
if (width < 3) width = 30;
initscr();
int mines[height][width];
init_mines(mines);
win = newwin(height + 2, 2 * width + 2, 0, 0);
wrap = newwin(height + 3, 2 * width + 2, 1, 0);
keypad(wrap, 1);
mousemask(BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED, 0);
while (1) {
int ch;
repaint(mines);
if ((ch = wgetch(wrap)) != KEY_MOUSE) {
if (ch != 'r') break;
reveal = !reveal;
continue;
}
if (getmouse(&evt) != OK) continue;
if ((evt.bstate & BUTTON1_CLICKED)) {
if (evt.y == height + 2 && !n_mines) {
check_wining(mines);
break;
}
if (!mine_clear(mines, (evt.x - 1) / 2, evt.y - 1, 0))
break;
}
else if ((evt.bstate & BUTTON2_CLICKED)) {
if (!mine_clear(mines, (evt.x - 1) / 2, evt.y - 1, 1))
break;
}
else if ((evt.bstate & BUTTON3_CLICKED))
mine_mark(mines, (evt.x - 1)/2, evt.y - 1);
}
repaint(mines);
mousemask(0, 0);
keypad(wrap, 0);
endwin();
return 0;
}
C#
The following solution implements the required task providing GUI (Windows Forms).
using System;
using System.Drawing;
using System.Windows.Forms;
class MineFieldModel
{
public int RemainingMinesCount{
get{
var count = 0;
ForEachCell((i,j)=>{
if (Mines[i,j] && !Marked[i,j])
count++;
});
return count;
}
}
public bool[,] Mines{get; private set;}
public bool[,] Opened{get;private set;}
public bool[,] Marked{get; private set;}
public int[,] Values{get;private set; }
public int Width{ get{return Mines.GetLength(1);} }
public int Height{ get{return Mines.GetLength(0);} }
public MineFieldModel(bool[,] mines)
{
this.Mines = mines;
this.Opened = new bool[Height, Width]; // filled with 'false' by default
this.Marked = new bool[Height, Width];
this.Values = CalculateValues();
}
private int[,] CalculateValues()
{
int[,] values = new int[Height, Width];
ForEachCell((i,j) =>{
var value = 0;
ForEachNeighbor(i,j, (i1,j1)=>{
if (Mines[i1,j1])
value++;
});
values[i,j] = value;
});
return values;
}
// Helper method for iterating over cells
public void ForEachCell(Action<int,int> action)
{
for (var i = 0; i < Height; i++)
for (var j = 0; j < Width; j++)
action(i,j);
}
// Helper method for iterating over cells' neighbors
public void ForEachNeighbor(int i, int j, Action<int,int> action)
{
for (var i1 = i-1; i1 <= i+1; i1++)
for (var j1 = j-1; j1 <= j+1; j1++)
if (InBounds(j1, i1) && !(i1==i && j1 ==j))
action(i1, j1);
}
private bool InBounds(int x, int y)
{
return y >= 0 && y < Height && x >=0 && x < Width;
}
public event Action Exploded = delegate{};
public event Action Win = delegate{};
public event Action Updated = delegate{};
public void OpenCell(int i, int j){
if(!Opened[i,j]){
if (Mines[i,j])
Exploded();
else{
OpenCellsStartingFrom(i,j);
Updated();
CheckForVictory();
}
}
}
void OpenCellsStartingFrom(int i, int j)
{
Opened[i,j] = true;
ForEachNeighbor(i,j, (i1,j1)=>{
if (!Mines[i1,j1] && !Opened[i1,j1] && !Marked[i1,j1])
OpenCellsStartingFrom(i1, j1);
});
}
void CheckForVictory(){
int notMarked = 0;
int wrongMarked = 0;
ForEachCell((i,j)=>{
if (Mines[i,j] && !Marked[i,j])
notMarked++;
if (!Mines[i,j] && Marked[i,j])
wrongMarked++;
});
if (notMarked == 0 && wrongMarked == 0)
Win();
}
public void Mark(int i, int j){
if (!Opened[i,j])
Marked[i,j] = true;
Updated();
CheckForVictory();
}
}
class MineFieldView: UserControl{
public const int CellSize = 40;
MineFieldModel _model;
public MineFieldModel Model{
get{ return _model; }
set
{
_model = value;
this.Size = new Size(_model.Width * CellSize+1, _model.Height * CellSize+2);
}
}
public MineFieldView(){
//Enable double-buffering to eliminate flicker
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer,true);
this.Font = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold);
this.MouseUp += (o,e)=>{
Point cellCoords = GetCell(e.Location);
if (Model != null)
{
if (e.Button == MouseButtons.Left)
Model.OpenCell(cellCoords.Y, cellCoords.X);
else if (e.Button == MouseButtons.Right)
Model.Mark(cellCoords.Y, cellCoords.X);
}
};
}
Point GetCell(Point coords)
{
var rgn = ClientRectangle;
var x = (coords.X - rgn.X)/CellSize;
var y = (coords.Y - rgn.Y)/CellSize;
return new Point(x,y);
}
static readonly Brush MarkBrush = new SolidBrush(Color.Blue);
static readonly Brush ValueBrush = new SolidBrush(Color.Black);
static readonly Brush UnexploredBrush = new SolidBrush(SystemColors.Control);
static readonly Brush OpenBrush = new SolidBrush(SystemColors.ControlDark);
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
if (Model != null)
{
Model.ForEachCell((i,j)=>
{
var bounds = new Rectangle(j * CellSize, i * CellSize, CellSize, CellSize);
if (Model.Opened[i,j])
{
g.FillRectangle(OpenBrush, bounds);
if (Model.Values[i,j] > 0)
{
DrawStringInCenter(g, Model.Values[i,j].ToString(), ValueBrush, bounds);
}
}
else
{
g.FillRectangle(UnexploredBrush, bounds);
if (Model.Marked[i,j])
{
DrawStringInCenter(g, "?", MarkBrush, bounds);
}
var outlineOffset = 1;
var outline = new Rectangle(bounds.X+outlineOffset, bounds.Y+outlineOffset, bounds.Width-2*outlineOffset, bounds.Height-2*outlineOffset);
g.DrawRectangle(Pens.Gray, outline);
}
g.DrawRectangle(Pens.Black, bounds);
});
}
}
static readonly StringFormat FormatCenter = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment=StringAlignment.Center
};
void DrawStringInCenter(Graphics g, string s, Brush brush, Rectangle bounds)
{
PointF center = new PointF(bounds.X + bounds.Width/2, bounds.Y + bounds.Height/2);
g.DrawString(s, this.Font, brush, center, FormatCenter);
}
}
class MineSweepForm: Form
{
MineFieldModel CreateField(int width, int height)
{
var field = new bool[height, width];
int mineCount = (int)(0.2 * height * width);
var rnd = new Random();
while(mineCount > 0)
{
var x = rnd.Next(width);
var y = rnd.Next(height);
if (!field[y,x])
{
field[y,x] = true;
mineCount--;
}
}
return new MineFieldModel(field);
}
public MineSweepForm()
{
var model = CreateField(6, 4);
var counter = new Label{ };
counter.Text = model.RemainingMinesCount.ToString();
var view = new MineFieldView
{
Model = model, BorderStyle = BorderStyle.FixedSingle,
};
var stackPanel = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
Controls = {counter, view}
};
this.Controls.Add(stackPanel);
model.Updated += delegate{
view.Invalidate();
counter.Text = model.RemainingMinesCount.ToString();
};
model.Exploded += delegate {
MessageBox.Show("FAIL!");
Close();
};
model.Win += delegate {
MessageBox.Show("WIN!");
view.Enabled = false;
};
}
}
class Program
{
static void Main()
{
Application.Run(new MineSweepForm());
}
}
C++
This solution implements the required task and one more command: unknown. It is represented by a '?' that's why the flag in this solution is represented as a '!'
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
typedef unsigned char byte;
enum fieldValues : byte { OPEN, CLOSED = 10, MINE, UNKNOWN, FLAG, ERR };
class fieldData
{
public:
fieldData() : value( CLOSED ), open( false ) {}
byte value;
bool open, mine;
};
class game
{
public:
~game()
{ if( field ) delete [] field; }
game( int x, int y )
{
go = false; wid = x; hei = y;
field = new fieldData[x * y];
memset( field, 0, x * y * sizeof( fieldData ) );
oMines = ( ( 22 - rand() % 11 ) * x * y ) / 100;
mMines = 0;
int mx, my, m = 0;
for( ; m < oMines; m++ )
{
do
{ mx = rand() % wid; my = rand() % hei; }
while( field[mx + wid * my].mine );
field[mx + wid * my].mine = true;
}
graphs[0] = ' '; graphs[1] = '.'; graphs[2] = '*';
graphs[3] = '?'; graphs[4] = '!'; graphs[5] = 'X';
}
void gameLoop()
{
string c, r, a;
int col, row;
while( !go )
{
drawBoard();
cout << "Enter column, row and an action( c r a ):\nActions: o => open, f => flag, ? => unknown\n";
cin >> c >> r >> a;
if( c[0] > 'Z' ) c[0] -= 32; if( a[0] > 'Z' ) a[0] -= 32;
col = c[0] - 65; row = r[0] - 49;
makeMove( col, row, a );
}
}
private:
void makeMove( int x, int y, string a )
{
fieldData* fd = &field[wid * y + x];
if( fd->open && fd->value < CLOSED )
{
cout << "This cell is already open!";
Sleep( 3000 ); return;
}
if( a[0] == 'O' ) openCell( x, y );
else if( a[0] == 'F' )
{
fd->open = true;
fd->value = FLAG;
mMines++;
checkWin();
}
else
{
fd->open = true;
fd->value = UNKNOWN;
}
}
bool openCell( int x, int y )
{
if( !isInside( x, y ) ) return false;
if( field[x + y * wid].mine ) boom();
else
{
if( field[x + y * wid].value == FLAG )
{
field[x + y * wid].value = CLOSED;
field[x + y * wid].open = false;
mMines--;
}
recOpen( x, y );
checkWin();
}
return true;
}
void drawBoard()
{
system( "cls" );
cout << "Marked mines: " << mMines << " from " << oMines << "\n\n";
for( int x = 0; x < wid; x++ )
cout << " " << ( char )( 65 + x ) << " ";
cout << "\n"; int yy;
for( int y = 0; y < hei; y++ )
{
yy = y * wid;
for( int x = 0; x < wid; x++ )
cout << "+---";
cout << "+\n"; fieldData* fd;
for( int x = 0; x < wid; x++ )
{
fd = &field[x + yy]; cout<< "| ";
if( !fd->open ) cout << ( char )graphs[1] << " ";
else
{
if( fd->value > 9 )
cout << ( char )graphs[fd->value - 9] << " ";
else
{
if( fd->value < 1 ) cout << " ";
else cout << ( char )(fd->value + 48 ) << " ";
}
}
}
cout << "| " << y + 1 << "\n";
}
for( int x = 0; x < wid; x++ )
cout << "+---";
cout << "+\n\n";
}
void checkWin()
{
int z = wid * hei - oMines, yy;
fieldData* fd;
for( int y = 0; y < hei; y++ )
{
yy = wid * y;
for( int x = 0; x < wid; x++ )
{
fd = &field[x + yy];
if( fd->open && fd->value != FLAG ) z--;
}
}
if( !z ) lastMsg( "Congratulations, you won the game!");
}
void boom()
{
int yy; fieldData* fd;
for( int y = 0; y < hei; y++ )
{
yy = wid * y;
for( int x = 0; x < wid; x++ )
{
fd = &field[x + yy];
if( fd->value == FLAG )
{
fd->open = true;
fd->value = fd->mine ? MINE : ERR;
}
else if( fd->mine )
{
fd->open = true;
fd->value = MINE;
}
}
}
lastMsg( "B O O O M M M M M !" );
}
void lastMsg( string s )
{
go = true; drawBoard();
cout << s << "\n\n";
}
bool isInside( int x, int y ) { return ( x > -1 && y > -1 && x < wid && y < hei ); }
void recOpen( int x, int y )
{
if( !isInside( x, y ) || field[x + y * wid].open ) return;
int bc = getMineCount( x, y );
field[x + y * wid].open = true;
field[x + y * wid].value = bc;
if( bc ) return;
for( int yy = -1; yy < 2; yy++ )
for( int xx = -1; xx < 2; xx++ )
{
if( xx == 0 && yy == 0 ) continue;
recOpen( x + xx, y + yy );
}
}
int getMineCount( int x, int y )
{
int m = 0;
for( int yy = -1; yy < 2; yy++ )
for( int xx = -1; xx < 2; xx++ )
{
if( xx == 0 && yy == 0 ) continue;
if( isInside( x + xx, y + yy ) && field[x + xx + ( y + yy ) * wid].mine ) m++;
}
return m;
}
int wid, hei, mMines, oMines;
fieldData* field; bool go;
int graphs[6];
};
int main( int argc, char* argv[] )
{
srand( GetTickCount() );
game g( 4, 6 ); g.gameLoop();
return system( "pause" );
}
- Output:
Marked mines: 0 from 5 A B C D +---+---+---+---+ | | | 1 | . | 1 +---+---+---+---+ | | | 1 | * | 2 +---+---+---+---+ | | | 1 | . | 3 +---+---+---+---+ | 1 | 2 | 2 | . | 4 +---+---+---+---+ | . | * | * | . | 5 +---+---+---+---+ | . | . | * | * | 6 +---+---+---+---+ B O O O M M M M M ! Marked mines: 1 from 7 A B C D E F G H +---+---+---+---+---+---+---+---+ | | | | | 1 | . | . | . | 1 +---+---+---+---+---+---+---+---+ | 1 | 1 | | 1 | 2 | . | . | . | 2 +---+---+---+---+---+---+---+---+ | ! | 1 | | 1 | . | . | . | . | 3 +---+---+---+---+---+---+---+---+ | 1 | 1 | | 1 | 2 | . | 2 | 1 | 4 +---+---+---+---+---+---+---+---+ | | | | | 1 | . | 1 | | 5 +---+---+---+---+---+---+---+---+ | 1 | 1 | | | 1 | 1 | 1 | | 6 +---+---+---+---+---+---+---+---+ | . | 1 | 1 | 1 | 1 | | | | 7 +---+---+---+---+---+---+---+---+ | . | . | . | . | 1 | | | | 8 +---+---+---+---+---+---+---+---+ Enter column, row and an action( c r a ): Actions: o => open, f => flag, ? => unknown
Ceylon
Be sure to import ceylon.random in your module.ceylon file.
import ceylon.random {
DefaultRandom
}
class Cell() {
shared variable Boolean covered = true;
shared variable Boolean flagged = false;
shared variable Boolean mined = false;
shared variable Integer adjacentMines = 0;
string =>
if (covered && !flagged)
then "."
else if (covered && flagged)
then "?"
else if (!covered && mined)
then "X"
else if (!covered && adjacentMines > 0)
then adjacentMines.string
else " ";
}
"The main function of the module. Run this one."
shared void run() {
value random = DefaultRandom();
value chanceOfBomb = 0.2;
value width = 6;
value height = 4;
value grid = Array { for (j in 1..height) Array { for (i in 1..width) Cell() } };
function getCell(Integer x, Integer y) => grid[y]?.get(x);
void initializeGrid() {
for (row in grid) {
for (cell in row) {
cell.covered = true;
cell.flagged = false;
cell.mined = random.nextFloat() < chanceOfBomb;
}
}
function countAdjacentMines(Integer x, Integer y) => count {
for (j in y - 1 .. y + 1)
for (i in x - 1 .. x + 1)
if (exists cell = getCell(i, j))
cell.mined
};
for (j->row in grid.indexed) {
for (i->cell in row.indexed) {
cell.adjacentMines = countAdjacentMines(i, j);
}
}
}
void displayGrid() {
print(" " + "".join(1..width));
print(" " + "-".repeat(width));
for (j->row in grid.indexed) {
print("``j + 1``|``"".join(row)``|``j + 1``");
}
print(" " + "-".repeat(width));
print(" " + "".join(1..width));
}
Boolean won() =>
expand(grid).every((cell) => (cell.flagged && cell.mined) || (!cell.flagged && !cell.mined));
void uncoverNeighbours(Integer x, Integer y) {
for (j in y - 1 .. y + 1) {
for (i in x - 1 .. x + 1) {
if (exists cell = getCell(i, j), cell.covered, !cell.flagged, !cell.mined) {
cell.covered = false;
if (cell.adjacentMines == 0) {
uncoverNeighbours(i, j);
}
}
}
}
}
while (true) {
print("Welcome to minesweeper!
-----------------------");
initializeGrid();
while (true) {
displayGrid();
print("
The number of mines to find is ``count(expand(grid)*.mined)``.
What would you like to do?
[1] reveal a free space (or blow yourself up)
[2] mark (or unmark) a mine");
assert (exists instruction = process.readLine());
print("Please enter the coordinates. eg 2 4");
assert (exists line2 = process.readLine());
value coords = line2.split().map(Integer.parse).narrow<Integer>().sequence();
if (exists x = coords[0], exists y = coords[1], exists cell = getCell(x - 1, y - 1)) {
switch (instruction)
case ("1") {
if (cell.mined) {
print("=================
=== You lose! ===
=================");
expand(grid).each((cell) => cell.covered = false);
displayGrid();
break;
}
else if (cell.covered) {
cell.covered = false;
uncoverNeighbours(x - 1, y - 1);
}
}
case ("2") {
if (cell.covered) {
cell.flagged = !cell.flagged;
}
}
else { print("bad choice"); }
if (won()) {
print("****************
*** You win! ***
****************");
break;
}
}
}
}
}
Clojure
(defn take-random [n coll]
(->> (repeatedly #(rand-nth coll))
distinct
(take n ,)))
(defn postwalk-fs
"Depth first post-order traversal of form, apply successive fs at each level.
(f1 (map f2 [..]))"
[[f & fs] form]
(f
(if (and (seq fs) (coll? form))
(into (empty form) (map (partial postwalk-fs fs) form))
form)))
(defn neighbors [x y n m pred]
(for [dx (range (Math/max 0 (dec x)) (Math/min n (+ 2 x)))
dy (range (Math/max 0 (dec y)) (Math/min m (+ 2 y)))
:when (pred dx dy)]
[dx dy]))
(defn new-game [n m density]
(let [mines (set (take-random (Math/floor (* n m density)) (range (* n m))))]
(->> (for [y (range m)
x (range n)
:let [neighbor-mines (count (neighbors x y n m #(mines (+ %1 (* %2 n)))))]]
(#(if (mines (+ (* y n) x)) (assoc % :mine true) %) {:value neighbor-mines}))
(partition n ,)
(postwalk-fs [vec vec] ,))))
(defn display [board]
(postwalk-fs [identity println #(condp % nil
:marked \?
:opened (:value %)
\.)] board))
(defn boom [{board :board}]
(postwalk-fs [identity println #(if (:mine %) \* (:value %))] board)
true)
(defn open* [board [[x y] & rest]]
(if-let [value (get-in board [y x :value])] ; if nil? value -> nil? x -> nil? queue
(recur
(assoc-in board [y x :opened] true)
(if (pos? value)
rest
(concat rest
(neighbors x y (count (first board)) (count board)
#(not (get-in board [%2 %1 :opened]))))))
board))
(defn open [board x y]
(let [x (dec x), y (dec y)]
(condp (get-in board [y x]) nil
:mine {:boom true :board board}
:opened board
(open* board [[x y]]))))
(defn mark [board x y]
(let [x (dec x), y (dec y)]
(assoc-in board [y x :marked] (not (get-in board [y x :marked])))))
(defn done? [board]
(if (:boom board)
(boom board)
(do (display board)
(->> (flatten board)
(remove :mine ,)
(every? :opened ,)))))
(defn play [n m density]
(let [board (new-game n m density)]
(println [:mines (count (filter :mine (flatten board)))])
(loop [board board]
(when-not (done? board)
(print ">")
(let [[cmd & xy] (.split #" " (read-line))
[x y] (map #(Integer. %) xy)]
(recur ((if (= cmd "mark") mark open) board x y)))))))
Common Lisp
(defclass minefield ()
((mines :initform (make-hash-table :test #'equal))
(width :initarg :width)
(height :initarg :height)
(grid :initarg :grid)))
(defun make-minefield (width height num-mines)
(let ((minefield (make-instance 'minefield
:width width
:height height
:grid (make-array
(list width height)
:initial-element #\.)))
(mine-count 0))
(with-slots (grid mines) minefield
(loop while (< mine-count num-mines)
do (let ((coords (list (random width) (random height))))
(unless (gethash coords mines)
(setf (gethash coords mines) T)
(incf mine-count))))
minefield)))
(defun print-field (minefield)
(with-slots (width height grid) minefield
(dotimes (y height)
(dotimes (x width)
(princ (aref grid x y)))
(format t "~%"))))
(defun mine-list (minefield)
(loop for key being the hash-keys of (slot-value minefield 'mines) collect key))
(defun count-nearby-mines (minefield coords)
(length (remove-if-not
(lambda (mine-coord)
(and
(> 2 (abs (- (car coords) (car mine-coord))))
(> 2 (abs (- (cadr coords) (cadr mine-coord))))))
(mine-list minefield))))
(defun clear (minefield coords)
(with-slots (mines grid) minefield
(if (gethash coords mines)
(progn
(format t "MINE! You lose.~%")
(dolist (mine-coords (mine-list minefield))
(setf (aref grid (car mine-coords) (cadr mine-coords)) #\x))
(setf (aref grid (car coords) (cadr coords)) #\X)
nil)
(setf (aref grid (car coords) (cadr coords))
(elt " 123456789"(count-nearby-mines minefield coords))))))
(defun mark (minefield coords)
(with-slots (mines grid) minefield
(setf (aref grid (car coords) (cadr coords)) #\?)))
(defun win-p (minefield)
(with-slots (width height grid mines) minefield
(let ((num-uncleared 0))
(dotimes (y height)
(dotimes (x width)
(let ((square (aref grid x y)))
(when (member square '(#\. #\?) :test #'char=)
(incf num-uncleared)))))
(= num-uncleared (hash-table-count mines)))))
(defun play-game ()
(let ((minefield (make-minefield 6 4 5)))
(format t "Greetings player, there are ~a mines.~%"
(hash-table-count (slot-value minefield 'mines)))
(loop
(print-field minefield)
(format t "Enter your command, examples: \"clear 0 1\" \"mark 1 2\" \"quit\".~%")
(princ "> ")
(let ((user-command (read-from-string (format nil "(~a)" (read-line)))))
(format t "Your command: ~a~%" user-command)
(case (car user-command)
(quit (return-from play-game nil))
(clear (unless (clear minefield (cdr user-command))
(print-field minefield)
(return-from play-game nil)))
(mark (mark minefield (cdr user-command))))
(when (win-p minefield)
(format t "Congratulations, you've won!")
(return-from play-game T))))))
(play-game)
D
EasyLang
len cell[] 56
len cnt[] 56
len flag[] 56
#
subr initvars
state = 0
ticks = 0
indx = -1
no_time = 0
.
func getind r c .
ind = -1
if r >= 0 and r <= 6 and c >= 0 and c <= 7
ind = r * 8 + c + 1
.
return ind
.
proc draw_cell ind h . .
ind -= 1
r = ind div 8
c = ind mod 8
x = c * 12 + 2.5
y = r * 12 + 2.5
move x y
rect 11 11
if h > 0
# count
move x + 3 y + 3
color 000
text h
elif h = -3
# flag
x += 4
color 000
linewidth 0.8
move x y + 3
line x y + 8
color 600
linewidth 2
move x + 0.5 y + 7
line x + 2 y + 7
elif h <> 0
# mine
color 333
if h = -2
color 800
.
move x + 5 y + 5
circle 3
line x + 8 y + 9
.
.
proc open ind . .
if ind <> -1 and cell[ind] = 0
cell[ind] = 2
flag[ind] = 0
color 686
draw_cell ind cnt[ind]
if cnt[ind] = 0
ind -= 1
r0 = ind div 8
c0 = ind mod 8
for r = r0 - 1 to r0 + 1
for c = c0 - 1 to c0 + 1
if r <> r0 or c <> c0
open getind r c
.
.
.
.
.
.
proc show_mines m . .
for ind to 56
if cell[ind] = 1
color 686
if m = -1
color 353
.
draw_cell ind m
.
.
.
proc outp col s$ . .
move 2.5 87
color col
rect 59 11
color 000
move 5 90
text s$
.
proc upd_info . .
for i to 56
nm += flag[i]
if cell[i] < 2
nc += 1
.
.
if nc = 8
outp 484 "Well done"
show_mines -1
state = 1
else
outp 464 8 - nm & " mines left"
.
.
proc test ind . .
if cell[ind] < 2 and flag[ind] = 0
if cell[ind] = 1
show_mines -1
color 686
draw_cell ind -2
outp 844 "B O O M !"
state = 1
else
open ind
upd_info
.
.
.
background 676
proc start . .
clear
color 353
for ind to 56
cnt[ind] = 0
cell[ind] = 0
flag[ind] = 0
draw_cell ind 0
.
n = 8
while n > 0
c = random 8 - 1
r = random 7 - 1
ind = r * 8 + c + 1
if cell[ind] = 0
n -= 1
cell[ind] = 1
for rx = r - 1 to r + 1
for cx = c - 1 to c + 1
ind = getind rx cx
if ind > -1
cnt[ind] += 1
.
.
.
.
.
initvars
outp 464 ""
textsize 4
move 5 93
text "Minesweeper - 8 mines"
move 5 88.2
text "Long-press for flagging"
textsize 6
timer 0
.
on mouse_down
if state = 0
if mouse_y > 86 and mouse_x > 60
no_time = 1
move 64.5 87
color 464
rect 33 11
.
indx = getind ((mouse_y - 2) div 12) ((mouse_x - 2) div 12)
ticks0 = ticks
elif state = 3
start
.
.
on mouse_up
if state = 0 and indx <> -1
test indx
.
indx = -1
.
on timer
if state = 1
state = 2
timer 1
elif state = 2
state = 3
elif no_time = 0 and ticks > 3000
outp 844 "B O O M !"
show_mines -2
state = 2
timer 1
else
if indx > 0 and ticks = ticks0 + 2
if cell[indx] < 2
color 353
flag[indx] = 1 - flag[indx]
opt = 0
if flag[indx] = 1
opt = -3
.
draw_cell indx opt
upd_info
.
indx = -1
.
if no_time = 0 and ticks mod 10 = 0
move 64.5 87
color 464
if ticks >= 2500
color 844
.
rect 33 11
color 000
move 66 90
text "Time:" & 300 - ticks / 10
.
ticks += 1
timer 0.1
.
.
start
FreeBASIC
Mouse version
Based On BeBeliOuS's original code (www.bebelious.fr)
'--- Declaration of global variables ---
Type mina
Dim mina As Byte
Dim flag As Byte
Dim ok As Byte
Dim numero As Byte
End Type
Dim Shared As Integer size = 16, NX = 20, NY = 20
Dim Shared As Double mina = 0.10
Dim Shared tablero(NX+1,NY+1) As mina
Dim Shared As Integer GameOver, ddx, ddy, kolor, nbDESCANSO
Dim Shared As Double temps
Dim As String tecla
'--- SUBroutines and FUNCtions ---
Sub VerTodo
Dim As Integer x, y
For x = 1 To NX
For y = 1 To NY
With tablero(x,y)
If .mina = 1 Or .flag > 0 Then .ok = 1
End With
Next y
Next x
End Sub
Sub ShowGrid
Dim As Integer x, y
Line(ddx-1,ddy-1)-(1+ddx+size*NX,1+ddy+size*NY),&hFF0000,B
For x = 1 To NX
For y = 1 To NY
With tablero(x,y)
'Si la casilla no se hace click
If .ok = 0 Then
Line(ddx+x*size,ddy+y*size)-(ddx+(x-1)*size,ddy+(y-1)*size),&h888888,BF
Line(ddx+x*size,ddy+y*size)-(ddx+(x-1)*size,ddy+(y-1)*size),&h444444,B
'bandera verde
If .flag = 1 Then
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h00FF00,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h0
End If
'bandera azul
If .flag = 2 Then
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h0000FF,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h0
End If
'Si se hace clic
Else
If .mina = 0 Then
If .numero > 0 Then
Select Case .numero
Case 1: kolor = &h3333FF
Case 2: kolor = &h33FF33
Case 3: kolor = &hFF3333
Case 4: kolor = &hFFFF33
Case 5: kolor = &h33FFFF
Case 6: kolor = &hFF33FF
Case 7: kolor = &h999999
Case 8: kolor = &hFFFFFF
End Select
Draw String(ddx+x*size-size/1.5,ddy+y*size-size/1.5),Str(.numero),kolor
End If
If GameOver = 1 Then
'Si no hay Mina y una bandera verde >> rojo completo
If .flag = 1 Then
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h330000,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h555555
End If
'Si no hay Mina y una bandera azul >> azul oscuro
If .flag = 2 Then
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h000033,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h555555
End If
End If
Else
'Si hay una Mina sin bandera >> rojo
If .flag = 0 Then
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&hFF0000,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&hFFFF00
Else
'Si hay una Mina con bandera verde >>> verde
If .flag = 1 Then
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h00FF00,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&hFFFF00
End If
If .flag = 2 Then
'Si hay una Mina con bandera azul >>>> verde oscuro
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&h003300,,,,F
Circle (ddx+x*size-size/2,ddy+y*size-size/2),size/4,&hFFFF00
End If
End If
End If
End If
End With
Next y
Next x
End Sub
Sub Calcul
Dim As Integer x, y
For x = 1 To NX
For y = 1 To NY
tablero(x,y).numero = _
Iif(tablero(x+1,y).mina=1,1,0) + Iif(tablero(x-1,y).mina=1,1,0) + _
Iif(tablero(x,y+1).mina=1,1,0) + Iif(tablero(x,y-1).mina=1,1,0) + _
Iif(tablero(x+1,y-1).mina=1,1,0) + Iif(tablero(x+1,y+1).mina=1,1,0) + _
Iif(tablero(x-1,y-1).mina=1,1,0) + Iif(tablero(x-1,y+1).mina=1,1,0)
Next y
Next x
End Sub
Sub Inicio
size = 20
If NX > 720/size Then size = 720/NX
If NY > 520/size Then size = 520/NY
Redim tablero(NX+1,NY+1) As mina
Dim As Integer x, y
For x = 1 To NX
For y = 1 To NY
With tablero(x,y)
.mina = Iif(Rnd > (1-mina), 1, 0)
.ok = 0
.flag = 0
.numero = 0
End With
Next y
Next x
ddx = (((800/size)-NX)/2)*size
ddy = (((600/size)-NY)/2)*size
Calcul
End Sub
Function isGameOver As Integer
Dim As Integer x, y, nbMINA, nbOK, nbAZUL, nbVERT
For x = 1 To NX
For y = 1 To NY
If tablero(x,y).ok = 1 And tablero(X,y).mina = 1 Then
Return -1
End If
If tablero(x,y).ok = 0 And tablero(x,y).flag = 1 And tablero(X,y).mina = 1 Then
nbOK += 1
End If
If tablero(X,y).mina = 1 Then
nbMINA + =1
End If
If tablero(X,y).flag = 2 Then
nbAZUL += 1
'End If
Elseif tablero(X,y).flag = 1 Then
nbVERT += 1
End If
Next y
Next x
If nbMINA = nbOK And nbAZUL = 0 Then Return 1
nbDESCANSO = nbMINA - nbVERT
End Function
Sub ClicRecursivo(ZX As Integer, ZY As Integer)
If tablero(ZX,ZY).ok = 1 Then Exit Sub
If tablero(ZX,ZY).flag > 0 Then Exit Sub
'CLICK
tablero(ZX,ZY).ok = 1
If tablero(ZX,ZY).mina = 1 Then Exit Sub
If tablero(ZX,ZY).numero > 0 Then Exit Sub
If ZX > 0 And ZX <= NX And ZY > 0 And ZY <= NY Then
ClicRecursivo(ZX+1,ZY)
ClicRecursivo(ZX-1,ZY)
ClicRecursivo(ZX,ZY+1)
ClicRecursivo(ZX,ZY-1)
ClicRecursivo(ZX+1,ZY+1)
ClicRecursivo(ZX+1,ZY-1)
ClicRecursivo(ZX-1,ZY+1)
ClicRecursivo(ZX-1,ZY-1)
End If
End Sub
'--- Main Program ---
Screenres 800,600,24
Windowtitle"Minesweeper game"
Randomize Timer
Cls
Dim As Integer mx, my, mb, fmb, ZX, ZY, r, tt
Dim As Double t
Inicio
GameOver = 1
Do
tecla = Inkey
If GameOver = 1 Then
Select Case Ucase(tecla)
Case Chr(Asc("X"))
NX += 5
If NX > 80 Then NX = 10
Inicio
Case Chr(Asc("Y"))
NY += 5
If NY > 60 Then NY = 10
Inicio
Case Chr(Asc("M"))
mina += 0.01
If mina > 0.26 Then mina = 0.05
Inicio
Case Chr(Asc("S"))
Inicio
GameOver = 0
temps = Timer
End Select
End If
Getmouse mx,my,,mb
mx -= ddx-size
my -= ddy-size
ZX = (MX-size/2)/size
ZY = (MY-size/2)/size
If GameOver = 0 And zx > 0 And zx <= nx And zy > 0 And zy <= ny Then
If MB=1 And fmb=0 Then ClicRecursivo(ZX,ZY)
If MB=2 And fmb=0 Then
tablero(ZX,ZY).flag += 1
If tablero(ZX,ZY).flag > 2 Then tablero(ZX,ZY).flag = 0
End If
fmb = mb
End If
r = isGameOver
If r = -1 And GameOver = 0 Then
VerTodo
GameOver = 1
End If
If r = 1 And GameOver = 0 Then GameOver = 1
If GameOver = 0 Then tt = Timer-temps
Screenlock
Cls
ShowGrid
If GameOver = 0 Then
Draw String (210,4), "X:" & NX & " Y:" & NY & " MINA:" & Int(mina*100) & "% TIMER : " & Int(TT) & " S REMAINING:" & nbDESCANSO,&hFFFF00
Else
Draw String (260,4), "PRESS: 'S' TO START X,Y,M TO SIZE" ,&hFFFF00
Draw String (330,17), "X:" & NX & " Y:" & NY & " MINA:" & Int(mina*100) & "%" ,&hFFFF00
If r = 1 Then
t += 0.01
If t > 1000 Then t = 0
Draw String (320,280+Cos(t)*100),"!! CONGRATULATION !!",&hFFFF00
End If
End If
Screenunlock
Loop Until tecla = Chr(27)
Bsave "Minesweeper.bmp",0
End
- Output:
Go
... though altered somewhat.
package main
import (
"bufio"
"fmt"
"math"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
type cell struct {
isMine bool
display byte // display character for cell
}
const lMargin = 4
var (
grid [][]cell
mineCount int
minesMarked int
isGameOver bool
)
var scanner = bufio.NewScanner(os.Stdin)
func makeGrid(n, m int) {
if n <= 0 || m <= 0 {
panic("Grid dimensions must be positive.")
}
grid = make([][]cell, n)
for i := 0; i < n; i++ {
grid[i] = make([]cell, m)
for j := 0; j < m; j++ {
grid[i][j].display = '.'
}
}
min := int(math.Round(float64(n*m) * 0.1)) // 10% of tiles
max := int(math.Round(float64(n*m) * 0.2)) // 20% of tiles
mineCount = min + rand.Intn(max-min+1)
rm := mineCount
for rm > 0 {
x, y := rand.Intn(n), rand.Intn(m)
if !grid[x][y].isMine {
rm--
grid[x][y].isMine = true
}
}
minesMarked = 0
isGameOver = false
}
func displayGrid(isEndOfGame bool) {
if !isEndOfGame {
fmt.Println("Grid has", mineCount, "mine(s),", minesMarked, "mine(s) marked.")
}
margin := strings.Repeat(" ", lMargin)
fmt.Print(margin, " ")
for i := 1; i <= len(grid); i++ {
fmt.Print(i)
}
fmt.Println()
fmt.Println(margin, strings.Repeat("-", len(grid)))
for y := 0; y < len(grid[0]); y++ {
fmt.Printf("%*d:", lMargin, y+1)
for x := 0; x < len(grid); x++ {
fmt.Printf("%c", grid[x][y].display)
}
fmt.Println()
}
}
func endGame(msg string) {
isGameOver = true
fmt.Println(msg)
ans := ""
for ans != "y" && ans != "n" {
fmt.Print("Another game (y/n)? : ")
scanner.Scan()
ans = strings.ToLower(scanner.Text())
}
if scanner.Err() != nil || ans == "n" {
return
}
makeGrid(6, 4)
displayGrid(false)
}
func resign() {
found := 0
for y := 0; y < len(grid[0]); y++ {
for x := 0; x < len(grid); x++ {
if grid[x][y].isMine {
if grid[x][y].display == '?' {
grid[x][y].display = 'Y'
found++
} else if grid[x][y].display != 'x' {
grid[x][y].display = 'N'
}
}
}
}
displayGrid(true)
msg := fmt.Sprint("You found ", found, " out of ", mineCount, " mine(s).")
endGame(msg)
}
func usage() {
fmt.Println("h or ? - this help,")
fmt.Println("c x y - clear cell (x,y),")
fmt.Println("m x y - marks (toggles) cell (x,y),")
fmt.Println("n - start a new game,")
fmt.Println("q - quit/resign the game,")
fmt.Println("where x is the (horizontal) column number and y is the (vertical) row number.\n")
}
func markCell(x, y int) {
if grid[x][y].display == '?' {
minesMarked--
grid[x][y].display = '.'
} else if grid[x][y].display == '.' {
minesMarked++
grid[x][y].display = '?'
}
}
func countAdjMines(x, y int) int {
count := 0
for j := y - 1; j <= y+1; j++ {
if j >= 0 && j < len(grid[0]) {
for i := x - 1; i <= x+1; i++ {
if i >= 0 && i < len(grid) {
if grid[i][j].isMine {
count++
}
}
}
}
}
return count
}
func clearCell(x, y int) bool {
if x >= 0 && x < len(grid) && y >= 0 && y < len(grid[0]) {
if grid[x][y].display == '.' {
if !grid[x][y].isMine {
count := countAdjMines(x, y)
if count > 0 {
grid[x][y].display = string(48 + count)[0]
} else {
grid[x][y].display = ' '
clearCell(x+1, y)
clearCell(x+1, y+1)
clearCell(x, y+1)
clearCell(x-1, y+1)
clearCell(x-1, y)
clearCell(x-1, y-1)
clearCell(x, y-1)
clearCell(x+1, y-1)
}
} else {
grid[x][y].display = 'x'
fmt.Println("Kaboom! You lost!")
return false
}
}
}
return true
}
func testForWin() bool {
isCleared := false
if minesMarked == mineCount {
isCleared = true
for x := 0; x < len(grid); x++ {
for y := 0; y < len(grid[0]); y++ {
if grid[x][y].display == '.' {
isCleared = false
}
}
}
}
if isCleared {
fmt.Println("You won!")
}
return isCleared
}
func splitAction(action string) (int, int, bool) {
fields := strings.Fields(action)
if len(fields) != 3 {
return 0, 0, false
}
x, err := strconv.Atoi(fields[1])
if err != nil || x < 1 || x > len(grid) {
return 0, 0, false
}
y, err := strconv.Atoi(fields[2])
if err != nil || y < 1 || y > len(grid[0]) {
return 0, 0, false
}
return x, y, true
}
func main() {
rand.Seed(time.Now().UnixNano())
usage()
makeGrid(6, 4)
displayGrid(false)
for !isGameOver {
fmt.Print("\n>")
scanner.Scan()
action := strings.ToLower(scanner.Text())
if scanner.Err() != nil || len(action) == 0 {
continue
}
switch action[0] {
case 'h', '?':
usage()
case 'n':
makeGrid(6, 4)
displayGrid(false)
case 'c':
x, y, ok := splitAction(action)
if !ok {
continue
}
if clearCell(x-1, y-1) {
displayGrid(false)
if testForWin() {
resign()
}
} else {
resign()
}
case 'm':
x, y, ok := splitAction(action)
if !ok {
continue
}
markCell(x-1, y-1)
displayGrid(false)
if testForWin() {
resign()
}
case 'q':
resign()
}
}
}
- Output:
Sample session:
h or ? - this help, c x y - clear cell (x,y), m x y - marks (toggles) cell (x,y), n - start a new game, q - quit/resign the game, where x is the (horizontal) column number and y is the (vertical) row number. Grid has 2 mine(s), 0 mine(s) marked. 123456 ------ 1:...... 2:...... 3:...... 4:...... >c 1 1 Grid has 2 mine(s), 0 mine(s) marked. 123456 ------ 1: 2: 11 3: 1111. 4: 1.... >m 6 3 Grid has 2 mine(s), 1 mine(s) marked. 123456 ------ 1: 2: 11 3: 1111? 4: 1.... >c 6 4 Grid has 2 mine(s), 1 mine(s) marked. 123456 ------ 1: 2: 11 3: 1111? 4: 1...1 >c 5 4 Grid has 2 mine(s), 1 mine(s) marked. 123456 ------ 1: 2: 11 3: 1111? 4: 1..11 >c 4 4 Grid has 2 mine(s), 1 mine(s) marked. 123456 ------ 1: 2: 11 3: 1111? 4: 1.111 >m 3 4 Grid has 2 mine(s), 2 mine(s) marked. 123456 ------ 1: 2: 11 3: 1111? 4: 1?111 You won! 123456 ------ 1: 2: 11 3: 1111Y 4: 1Y111 You found 2 out of 2 mine(s). Another game (y/n)? : y Grid has 3 mine(s), 0 mine(s) marked. 123456 ------ 1:...... 2:...... 3:...... 4:...... >c 1 1 Grid has 3 mine(s), 0 mine(s) marked. 123456 ------ 1:1..... 2:...... 3:...... 4:...... >c 1 3 Kaboom! You lost! 123456 ------ 1:1..... 2:N..... 3:x..... 4:.N.... You found 0 out of 3 mine(s). Another game (y/n)? : n
Icon and Unicon
The following solution implements the required task and additionally error checking, and several additional commands.
Sample output:
Creating 6x4 mine field with 9 (40.9903518170081%). ---- 1 : .... 2 : .... 3 : .... 4 : .... 5 : .... 6 : .... 0 marked mines and 9 mines left to be marked. h h or ? - this help n - start a new game c i j - clears x,y and displays the grid m i j - marks (toggles) x,y p - displays the grid k i j - clears adjecent unmarked cells if #marks = count x - clears ALL unmarked flags at once r - resign the game q - quit the game where i is the (vertical) row number and j is the (horizontal) column number. c 1 1 ---- 1 : 1.. 2 : 12.. 3 : .... 4 : .... 5 : .... 6 : .... 0 marked mines and 9 mines left to be marked.
Haskell
Game Module
{-# LANGUAGE TemplateHaskell #-}
module MineSweeper
( Board
, Cell(..)
, CellState(..)
, Pos
-- lenses / prisms
, pos
, coveredLens
, coveredFlaggedLens
, coveredMinedLens
, xCoordLens
, yCoordLens
-- Functions
, emptyBoard
, groupedByRows
, displayCell
, isLoss
, isWin
, exposeMines
, openCell
, flagCell
, mineBoard
, totalRows
, totalCols )
where
import Control.Lens ((%~), (&), (.~), (^.), (^?), Lens', Traversal', _1, _2,
anyOf, filtered, folded, lengthOf, makeLenses, makePrisms,
preview, to, view)
import Data.List (find, groupBy, nub, delete, sortBy)
import Data.Maybe (isJust)
import System.Random (getStdGen, getStdRandom, randomR, randomRs)
type Pos = (Int, Int)
type Board = [Cell]
data CellState = Covered { _mined :: Bool, _flagged :: Bool }
| UnCovered { _mined :: Bool }
deriving (Show, Eq)
data Cell = Cell
{ _pos :: Pos
, _state :: CellState
, _cellId :: Int
, _adjacentMines :: Int }
deriving (Show, Eq)
makePrisms ''CellState
makeLenses ''CellState
makeLenses ''Cell
-- Re-useable lens.
coveredLens :: Traversal' Cell (Bool, Bool) --'
coveredLens = state . _Covered
coveredMinedLens, coveredFlaggedLens, unCoveredLens :: Traversal' Cell Bool --'
coveredMinedLens = coveredLens . _1
coveredFlaggedLens = coveredLens . _2
unCoveredLens = state . _UnCovered
xCoordLens, yCoordLens :: Lens' Cell Int --'
xCoordLens = pos . _1
yCoordLens = pos . _2
-- Adjust row and column size to your preference.
totalRows, totalCols :: Int
totalRows = 4
totalCols = 6
emptyBoard :: Board
emptyBoard = (\(n, p) -> Cell { _pos = p
, _state = Covered False False
, _adjacentMines = 0
, _cellId = n }) <$> zip [1..] positions
where
positions = (,) <$> [1..totalCols] <*> [1..totalRows]
updateCell :: Cell -> Board -> Board
updateCell cell = fmap (\c -> if cell ^. cellId == c ^. cellId then cell else c)
updateBoard :: Board -> [Cell] -> Board
updateBoard = foldr updateCell
okToOpen :: [Cell] -> [Cell]
okToOpen = filter (\c -> c ^? coveredLens == Just (False, False))
openUnMined :: Cell -> Cell
openUnMined = state .~ UnCovered False
openCell :: Pos -> Board -> Board
openCell p b = f $ find (\c -> c ^. pos == p) b
where
f (Just c)
| c ^? coveredFlaggedLens == Just True = b
| c ^? coveredMinedLens == Just True = updateCell
(c & state .~ UnCovered True)
b
| isCovered c = if c ^. adjacentMines == 0 && not (isFirstMove b)
then updateCell (openUnMined c) $ expandEmptyCells b c
else updateCell (openUnMined c) b
| otherwise = b
f Nothing = b
isCovered = isJust . preview coveredLens
expandEmptyCells :: Board -> Cell -> Board
expandEmptyCells board cell
| null openedCells = board
| otherwise = foldr (flip expandEmptyCells) updatedBoard (zeroAdjacent openedCells)
where
findMore _ [] = []
findMore exclude (c : xs)
| c `elem` exclude = findMore exclude xs
| c ^. adjacentMines == 0 = c : adjacent c <>
findMore (c : exclude <> adjacent c) xs
| otherwise = c : findMore (c : exclude) xs
adjacent = okToOpen . flip adjacentCells board
openedCells = openUnMined <$> nub (findMore [cell] (adjacent cell))
zeroAdjacent = filter (view (adjacentMines . to (== 0)))
updatedBoard = updateBoard board openedCells
flagCell :: Pos -> Board -> Board
flagCell p board = case find ((== p) . view pos) board of
Just c -> updateCell (c & state . flagged %~ not) board
Nothing -> board
adjacentCells :: Cell -> Board -> [Cell]
adjacentCells Cell {_pos = c@(x1, y1)} = filter (\c -> c ^. pos `elem` positions)
where
f n = [pred n, n, succ n]
positions = delete c $ [(x, y) | x <- f x1, x > 0, y <- f y1, y > 0]
isLoss, isWin, allUnMinedOpen, allMinesFlagged, isFirstMove :: Board -> Bool
isLoss = anyOf (traverse . unCoveredLens) (== True)
isWin b = allUnMinedOpen b || allMinesFlagged b
allUnMinedOpen = (== 0) . lengthOf (traverse . coveredMinedLens . filtered (== False))
allMinesFlagged b = minedCount b == flaggedMineCount b
where
minedCount = lengthOf (traverse . coveredMinedLens . filtered (== True))
flaggedMineCount = lengthOf (traverse . coveredLens . filtered (== (True, True)))
isFirstMove = (== totalCols * totalRows) . lengthOf (folded . coveredFlaggedLens . filtered (== False))
groupedByRows :: Board -> [Board]
groupedByRows = groupBy (\c1 c2 -> yAxis c1 == yAxis c2)
. sortBy (\c1 c2 -> yAxis c1 `compare` yAxis c2)
where
yAxis = view yCoordLens
displayCell :: Cell -> String
displayCell c
| c ^? unCoveredLens == Just True = "X"
| c ^? coveredFlaggedLens == Just True = "?"
| c ^? (unCoveredLens . to not) == Just True =
if c ^. adjacentMines > 0 then show $ c ^. adjacentMines else "ā¢"
| otherwise = "."
exposeMines :: Board -> Board
exposeMines = fmap (\c -> c & state . filtered (\s -> s ^? _Covered . _1 == Just True) .~ UnCovered True)
updateMineCount :: Board -> Board
updateMineCount b = go b
where
go [] = []
go (x : xs) = (x & adjacentMines .~ totalAdjacentMines b) : go xs
where
totalAdjacentMines =
foldr (\c acc -> if c ^. (state . mined) then succ acc else acc) 0 . adjacentCells x
-- IO
mineBoard :: Pos -> Board -> IO Board
mineBoard p board = do
totalMines <- randomMinedCount
minedBoard totalMines >>= \mb -> pure $ updateMineCount mb
where
mines n = take n <$> randomCellIds
minedBoard n = (\m ->
fmap (\c -> if c ^. cellId `elem` m then c & state . mined .~ True else c)
board) . filter (\c -> openedCell ^. cellId /= c)
<$> mines n
openedCell = head $ filter (\c -> c ^. pos == p) board
randomCellIds :: IO [Int]
randomCellIds = randomRs (1, totalCols * totalRows) <$> getStdGen
randomMinedCount :: IO Int
randomMinedCount = getStdRandom $ randomR (minMinedCells, maxMinedCells)
where
maxMinedCells = floor $ realToFrac (totalCols * totalRows) * 0.2
minMinedCells = floor $ realToFrac (totalCols * totalRows) * 0.1
Text Play
module Main
where
import MineSweeper (Board, Cell (..), Pos, coveredLens, coveredMinedLens,
coveredFlaggedLens, xCoordLens, yCoordLens, pos, emptyBoard,
groupedByRows, displayCell, isLoss, isWin, exposeMines,
openCell, flagCell, mineBoard, totalRows, totalCols)
import Text.Printf (printf)
import Text.Read (readMaybe)
import System.IO (hSetBuffering, stdout, BufferMode(..))
import Control.Monad (join, guard)
import Control.Lens (lengthOf, filtered, view, preview)
data Command = Open Pos | Flag Pos | Invalid
parseInput :: String -> Command
parseInput s | length input /= 3 = Invalid
| otherwise = maybe Invalid command parsedPos
where
input = words s
parsedPos = do
x <- readMaybe (input !! 1)
y <- readMaybe (input !! 2)
guard (x <= totalCols && y <= totalRows)
pure (x, y)
command p = case head input of
"o" -> Open p
"f" -> Flag p
_ -> Invalid
cheat :: Board -> String
cheat = show . fmap (view pos) . filter ((== Just True) . preview coveredMinedLens)
totalMines, totalFlagged, totalCovered :: Board -> Int
totalMines = lengthOf (traverse . coveredMinedLens . filtered (==True))
totalFlagged = lengthOf (traverse . coveredFlaggedLens . filtered (==True))
totalCovered = lengthOf (traverse . coveredLens)
-- IO
drawBoard :: Board -> IO ()
drawBoard b = do
-- printf "Cheat: %s\n" $ cheat b
printf " Mines: %d Covered: %d Flagged: %d\n\n"
(totalMines b) (totalCovered b) (totalFlagged b)
printf "%3s" ""
mapM_ (printf "%3d") $ view xCoordLens <$> head rows
printf "\n"
mapM_ (\row -> do
printf "%3d" $ yCoord row
mapM_ (printf "%3s" . displayCell) row
printf "\n" ) rows
where
rows = groupedByRows b
yCoord = view yCoordLens . head
gameLoop :: Board -> IO ()
gameLoop b
| isLoss b = drawBoard (exposeMines b) >> printf "\nYou Lose.\n"
| isWin b = drawBoard b >> printf "\nYou Win.\n"
| otherwise = do
drawBoard b
putStr "\nPick a cell: "
c <- getLine
case parseInput c of
Open p -> gameLoop $ openCell p b
Flag p -> gameLoop $ flagCell p b
Invalid -> gameLoop b
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
drawBoard emptyBoard
putStr "\nPick a cell: "
c <- getLine
case parseInput c of
Open p -> join $ startGame p emptyBoard
_ -> main
where
startGame p b = gameLoop . openCell p <$> mineBoard p b
- Output:
Mines: 0 Covered: 24 Flagged: 0 1 2 3 4 5 6 1 . . . . . . 2 . . . . . . 3 . . . . . . 4 . . . . . . Pick a cell: o 1 1 Mines: 2 Covered: 23 Flagged: 0 1 2 3 4 5 6 1 ā¢ . . . . . 2 . . . . . . 3 . . . . . . 4 . . . . . . Pick a cell: f 1 2 Mines: 2 Covered: 23 Flagged: 1 1 2 3 4 5 6 1 ā¢ . . . . . 2 ? . . . . . 3 . . . . . . 4 . . . . . . Pick a cell: f 6 6 Mines: 2 Covered: 23 Flagged: 1 1 2 3 4 5 6 1 ā¢ . . . . . 2 ? . . . . . 3 . . . . . . 4 . . . . . . Pick a cell: f 6 4 Mines: 2 Covered: 23 Flagged: 2 1 2 3 4 5 6 1 ā¢ . . . . . 2 ? . . . . . 3 . . . . . . 4 . . . . . ? Pick a cell: f 1 2 Mines: 2 Covered: 23 Flagged: 1 1 2 3 4 5 6 1 ā¢ . . . . . 2 . . . . . . 3 . . . . . . 4 . . . . . ? Pick a cell: o 1 2 Mines: 2 Covered: 14 Flagged: 1 1 2 3 4 5 6 1 ā¢ 1 . . . . 2 ā¢ 1 . . . . 3 ā¢ 1 2 . . . 4 ā¢ ā¢ 1 . . ? Pick a cell: o 6 1 Mines: 2 Covered: 4 Flagged: 1 1 2 3 4 5 6 1 ā¢ 1 . 1 ā¢ ā¢ 2 ā¢ 1 . 1 ā¢ ā¢ 3 ā¢ 1 2 2 1 ā¢ 4 ā¢ ā¢ 1 . 1 ? Pick a cell: f 4 4 Mines: 2 Covered: 4 Flagged: 2 1 2 3 4 5 6 1 ā¢ 1 . 1 ā¢ ā¢ 2 ā¢ 1 . 1 ā¢ ā¢ 3 ā¢ 1 2 2 1 ā¢ 4 ā¢ ā¢ 1 ? 1 ? Pick a cell: o 3 2 Mines: 0 Covered: 2 Flagged: 1 1 2 3 4 5 6 1 ā¢ 1 . 1 ā¢ ā¢ 2 ā¢ 1 X 1 ā¢ ā¢ 3 ā¢ 1 2 2 1 ā¢ 4 ā¢ ā¢ 1 X 1 ? You Lose.
J
Solution
NB. minefield.ijs script
NB. =========================================================
NB. Game engine
NB.require 'guid'
NB.([ 9!:1) _2 (3!:4) , guids 1 NB. randomly set initial random seed
coclass 'mineswpeng'
newMinefield=: 3 : 0
if. 0=#y do. y=. 9 9 end.
Marked=: Cleared=: y$0
NMines=: <. */(0.01*10+?20),y NB. 10..20% of tiles are mines
mines=. (i. e. NMines ? */) y NB. place mines
Map=: (9*mines) >. y{. (1,:3 3) +/@,;.3 (-1+y){.mines
)
markTiles=: 3 : 0
Marked=: (<"1 <:y) (-.@{)`[`]} Marked NB. toggle marked state of cell(s)
)
clearTiles=: clearcell@:<: NB. decrement coords - J arrays are 0-based
clearcell=: verb define
if. #y do.
free=. (#~ (Cleared < 0 = Map) {~ <"1) y
Cleared=: 1 (<"1 y)} Cleared NB. set cell(s) as cleared
if. #free do.
clearcell (#~ Cleared -.@{~ <"1) ~. (<:$Map) (<."1) 0 >. getNbrs free
end.
end.
)
getNbrs=: [: ,/^:(3=#@$) +"1/&(<: 3 3#: i.9)
eval=: verb define
if. 9 e. Cleared #&, Map do. NB. cleared mine(s)?
1; 'KABOOM!!'
elseif. *./ 9 = (-.Cleared) #&, Map do. NB. all cleared except mines?
1; 'Minefield cleared.'
elseif. do. NB. else...
0; (": +/, Marked>Cleared),' of ',(":NMines),' mines marked.'
end. NB. result: isEnd; message
)
showField=: 4 : 0
idx=. y{ (2 <. Marked + +:Cleared) ,: 2
|: idx} (11&{ , 12&{ ,: Map&{) x NB. transpose result - J arrays are row,column
)
NB. =========================================================
NB. User interface
Minesweeper_z_=: conew&'mineswp'
coclass 'mineswp'
coinsert 'mineswpeng' NB. insert game engine locale in copath
Tiles=: ' 12345678**.?'
create=: verb define
smoutput Instructions
startgame y
)
destroy=: codestroy
quit=: destroy
startgame=: update@newMinefield
clear=: update@clearTiles
mark=: update@markTiles
update=: 3 : 0
'isend msg'=. eval ''
smoutput msg
smoutput < Tiles showField isend
if. isend do.
msg=. ('K'={.msg) {:: 'won';'lost'
smoutput 'You ',msg,'! Try again?'
destroy ''
end.
empty''
)
Instructions=: 0 : 0
=== MineSweeper ===
Object:
Uncover (clear) all the tiles that are not mines.
How to play:
- the left, top tile is: 1 1
- clear an uncleared tile (.) using the command:
clear__fld <column index> <row index>
- mark and uncleared tile (?) as a suspected mine using the command:
mark__fld <column index> <row index>
- if you uncover a number, that is the number of mines adjacent
to the tile
- if you uncover a mine (*) the game ends (you lose)
- if you uncover all tiles that are not mines the game ends (you win).
- quit a game before winning or losing using the command:
quit__fld ''
- start a new game using the command:
fld=: MineSweeper <num columns> <num rows>
)
Example Usage
load 'minefield.ijs'
fld=: Minesweeper 6 4
=== MineSweeper ===
Object:
Uncover (clear) all the tiles that are not mines.
How to play:
- the left, top tile is: 1 1
- clear an uncleared tile (.) using the command:
clear__fld <column index> <row index>
- mark and uncleared tile (?) as a suspected mine using the command:
mark__fld <column index> <row index>
- if you uncover a number, that is the number of mines adjacent
to the tile
- if you uncover a mine (*) the game ends (you lose)
- if you uncover all tiles that are not mines the game ends (you win).
- quit a game before winning or losing using the command:
quit__fld ''
- start a new game using the command:
fld=: MineSweeper <num columns> <num rows>
0 of 5 mines marked.
āāāāāāāā
ā......ā
ā......ā
ā......ā
ā......ā
āāāāāāāā
clear__fld 1 1
0 of 5 mines marked.
āāāāāāāā
ā2.....ā
ā......ā
ā......ā
ā......ā
āāāāāāāā
clear__fld 6 4
0 of 5 mines marked.
āāāāāāāā
ā2..2 ā
ā...2 ā
ā..31 ā
ā..1 ā
āāāāāāāā
mark__fld 3 1 ,: 3 2 NB. mark and clear both accept lists of coordinates
2 of 5 mines marked.
āāāāāāāā
ā2.?2 ā
ā..?2 ā
ā..31 ā
ā..1 ā
āāāāāāāā
clear__fld 1 2 , 1 3 ,: 1 4
2 of 5 mines marked.
āāāāāāāā
ā2.?2 ā
ā3.?2 ā
ā2.31 ā
ā1.1 ā
āāāāāāāā
clear__fld 2 1
KABOOM!!
āāāāāāāā
ā2**2 ā
ā3**2 ā
ā2*31 ā
ā111 ā
āāāāāāāā
You lost! Try again?
fld=: Minesweeper 20 10 NB. create bigger minefield
... NB. instructions elided
0 of 50 mines marked.
āāāāāāāāāāāāāāāāāāāāāā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
ā....................ā
āāāāāāāāāāāāāāāāāāāāāā
clear__fld >: 4$.$. 9 > Map__fld NB. Autosolve ;-)
Minefield cleared.
āāāāāāāāāāāāāāāāāāāāāā
ā1***2**2**2*2*2*1111ā
ā123222222221212222*1ā
ā11 1223*421ā
ā*3321 11223**5**1 ā
ā3***211 1*2**6**432 ā
ā2*433*222223**423*2 ā
ā1111*4*4*2 13*2 3*41ā
ā11112*3**321211 2**1ā
ā3*3112334*3*211 1332ā
ā***1 1*12*312*1 1*1ā
āāāāāāāāāāāāāāāāāāāāāā
You won! Try again?
Java
--------------------------------- START of Main.java ---------------------------------
//By xykmz. Enjoy!
import java.util.Scanner;
public class Main {
static int intErrorTrap (int x, int y){
int max, min;
if (x < y) {
min = x;
max = y;
} else {
min = y;
max = x;
}
int input;
boolean loopEnd;
do {
System.out.println("Please enter an integer between " + min + " to " + max + ".");
Scanner userInput = new Scanner(System.in); //Player inputs a guess
try
{
input = userInput.nextInt();
if(input > max) //Input is too high
{
loopEnd = false;
System.out.println("Input is invalid.");
return -1;
}
else if(input < min) //Input is too low
{
loopEnd = false;
System.out.println("Input is invalid.");
return -1;
}
else //Input is within acceptable range
{
loopEnd = true;
System.out.println(input + " is a valid input.");
return input;
}
}
catch (Exception e)
{
loopEnd = false;
userInput.next();
System.out.println("Input is invalid.");
return 0;
}
} while (loopEnd == false);
}
public static void main(String[] args) {
System.out.println ("Enter width.");
int x = intErrorTrap (0,60);
System.out.println ("Enter height.");
int y = intErrorTrap (0,30);
System.out.println ("Enter difficulty.");
int d = intErrorTrap (0,100);
new Minesweeper(x, y, d); //Suggested: (60, 30, 15)
}
}
//--------------------------------- END of Main.java ---------------------------------
//--------------------------------- START of Cell.java ---------------------------------
public class Cell{ //This entire class is quite self-explanatory. All we're really doing is setting booleans.
private boolean isMine, isFlagged, isCovered;
private int number;
public Cell(){
isMine = false;
isFlagged = false;
isCovered = true;
number = 0;
}
public void flag(){
isFlagged = true;
}
public void unflag(){
isFlagged = false;
}
public void setMine(){
isMine = true;
}
public boolean isMine(){
return isMine;
}
public void reveal(){
isCovered = false;
}
public void setNumber(int i){ //Set the number of the cell
number = i;
}
public int getNumber(){ //Request the program for the number of the cell
return number;
}
public boolean isFlagged(){
return isFlagged;
}
public boolean isCovered(){
return isCovered;
}
}
//--------------------------------- END of Cell.java ---------------------------------
//--------------------------------- START of Board.java ---------------------------------
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Board extends JPanel{
private static final long serialVersionUID = 1L; //Guarantees consistent serialVersionUID value across different java compiler implementations,
//Auto-generated value might screw things up
private Minesweeper mine;
private Cell[][] cells;
public void paintComponent(Graphics g){
cells = mine.getCells();
for (int i = 0; i < mine.getx(); i++){
for (int j = 0; j < mine.gety(); j++){
Cell current = cells[i][j];
//Flagged cells
if (current.isFlagged()){
if (current.isMine() && mine.isFinished()){
g.setColor(Color.ORANGE); //Let's the player know which mines they got right when the game is finished.
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
g.drawLine(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.drawLine(i * 20, j * 20 + 20, i * 20 + 20, j * 20);
}
else if (mine.isFinished()){ //Shows cells that the player incorrectly identified as mines.
g.setColor(Color.YELLOW);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
else{
g.setColor(Color.GREEN); //Flagging a mine.
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
}
//Unflagged cells
else if (current.isCovered()){ //Covered cells
g.setColor(Color.DARK_GRAY);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
else if (current.isMine()){ //Incorrect cells are shown when the game is over.=
g.setColor(Color.RED);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
g.drawLine(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.drawLine(i * 20, j * 20 + 20, i * 20 + 20, j * 20);
}
else{ //Empty cells or numbered cells
g.setColor(Color.LIGHT_GRAY);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
//The following part is very self explanatory - drawing the numbers.
//Not very interesting work.
//Not a fun time.
//Rating: 0/10. Would not recommend.
if (!current.isCovered()){
if (current.getNumber() == 1){
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
}
else if (current.getNumber() == 2){
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 7, j * 20 + 11, i * 20 + 7, j * 20 + 15); //5
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 3){
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 4){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
}
else if (current.getNumber() == 5){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 6){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 7, j * 20 + 11, i * 20 + 7, j * 20 + 15); //5
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 7){
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
}
else if (current.getNumber() == 8){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 7, j * 20 + 11, i * 20 + 7, j * 20 + 15); //5
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
}
g.setColor(Color.BLACK);
g.drawRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
}
}
}
public Board(Minesweeper m){ //Creating a new game so we can draw a board for it
mine = m;
cells = mine.getCells();
addMouseListener(new Actions(mine));
setPreferredSize(new Dimension(mine.getx() * 20, mine.gety() * 20));
}
}
//--------------------------------- END of Board.java ---------------------------------
//--------------------------------- START of Actions.java ---------------------------------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Actions implements ActionListener, MouseListener{
private Minesweeper mine;
//These following four are not important. We simply tell the machine to pay no mind when mouse is pressed, released, etc.
//If these weren't here the computer would freak out and panic over what to do because no instructions were given.
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public void mousePressed(MouseEvent e){
}
public void mouseReleased(MouseEvent e){
}
//Where the fun begins
public Actions(Minesweeper m){
mine = m;
}
//Any time an action is performed, redraw the board and keep it up to date.
public void actionPerformed(ActionEvent e){
mine.reset();
mine.refresh();
}
//Mouse clicky clicky
public void mouseClicked(MouseEvent e){
//Left click - opens mine
if (e.getButton() == 1)
{
int x = e.getX() / 20;
int y = e.getY() / 20;
mine.select(x, y);
}
//Right click - marks mine
if (e.getButton() == 3)
{
int x = e.getX() / 20;
int y = e.getY() / 20;
mine.mark(x, y);
}
mine.refresh(); //Gotta keep it fresh
}
}
//--------------------------------- END of Actions.java ---------------------------------
//--------------------------------- START of Minesweeper.java ---------------------------------
import java.awt.BorderLayout;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Minesweeper extends JFrame
{
private static final long serialVersionUID = 1L;
private int width, height;
private int difficulty;
private Cell[][] cells;
private Board board;
private JButton reset;
private boolean finished;
public void select(int x, int y){ //Select a mine on the board
if (cells[x][y].isFlagged()) //Is the mine flagged?
return;
cells[x][y].reveal(); //Reveal the cell
resetMarks(); //Reset marks and redraw board
refresh();
if (cells[x][y].isMine()) //If a mine is revealed, you lose.
{
lose();
}
else if (won()) //If the game has been won, you win. Hahahahahaha.
{
win();
}
}
private void setNumbers(){ //For each cell, count the amount of mines in surrounding squares.
//Because the board is modeled as a 2D array, this is relatively easy - simply check the surrounding addresses for mines.
//If there's a mine, add to the count.
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
int count = 0;
if (i > 0 && j > 0 && cells[i - 1][j - 1].isMine())
count++;
if (j > 0 && cells[i][j - 1].isMine())
count++;
if (i < width - 1 && j > 0 && cells[i + 1][j - 1].isMine())
count++;
if (i > 0 && cells[i - 1][j].isMine())
count++;
if (i < width - 1 && cells[i + 1][j].isMine())
count++;
if (i > 0 && j < height - 1 && cells[i - 1][j + 1].isMine())
count++;
if (j < height - 1 && cells[i] [j + 1].isMine())
count++;
if (i < width - 1 && j < height - 1 && cells[i + 1][j + 1].isMine())
count++;
cells[i][j].setNumber(count);
if (cells[i][j].isMine())
cells[i][j].setNumber(-1);
if (cells[i][j].getNumber() == 0)
cells[i][j].reveal();
}
}
for (int i = 0; i < width; i++){ //This is for the beginning of the game.
//If the 8 cells around certain cell have no mines beside them (hence getNumber() = 0) beside them, this cell is empty.
for (int j = 0; j < height; j++){
if (i > 0 && j > 0 && cells[i - 1][j - 1].getNumber() == 0)
cells[i][j].reveal();
if (j > 0 && cells[i][j - 1].getNumber() == 0)
cells[i][j].reveal();
if (i < width - 1 && j > 0 && cells[i + 1][j - 1].getNumber() == 0)
cells[i][j].reveal();
if (i > 0 && cells[i - 1][j].getNumber() == 0)
cells[i][j].reveal();
if (i < width - 1 && cells[i + 1][j].getNumber() == 0)
cells[i][j].reveal();
if (i > 0 && j < height - 1 && cells[i - 1][j + 1].getNumber() == 0)
cells[i][j].reveal();
if (j < height - 1 && cells[i][j + 1].getNumber() == 0)
cells[i][j].reveal();
if (i < width - 1 && j < height - 1 && cells[i + 1][j + 1].getNumber() == 0)
cells[i][j].reveal();
}
}
}
public void mark(int x, int y){ //When a player wants to flag/unflag a cell
if (cells[x][y].isFlagged()) //If the cell is already flagged, unflag it.
cells[x][y].unflag();
else if (cells[x][y].isCovered()) //If the cell has nothing on it and is covered, flag it.
cells[x][y].flag();
resetMarks();
}
private void resetMarks(){ //If a cell is not covered, then it cannot be flagged.
//Every time a player does something, this should be called to redraw the board.
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
if (!cells[i][j].isCovered())
cells[i][j].unflag();
}
}
}
public void reset(){ //Reset the positions of the mines on the board
//If randomly generated number from 0 to 100 is less than the chosen difficulty, a mine is placed down.
//This will somewhat accurately reflect the mine percentage that the user requested - the more cells, the more accurate.
Random random = new Random();
finished = false;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Cell c = new Cell();
cells[i][j] = c;
int r = random.nextInt(100);
if (r < difficulty)
{
cells[i][j].setMine(); //Put down a mine.
}
}
}
setNumbers(); //Set the numbers after mines have been put down.
}
public int getx() {
return width;
}
public int gety() {
return height;
}
public Cell[][] getCells() {
return cells;
}
public void refresh(){ //Refresh the drawing of the board
board.repaint();
}
private void win(){ //Winning a game
finished = true;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
cells[i][j].reveal();//Reveal all cells
if (!cells[i][j].isMine())
cells[i][j].unflag();
}
}
refresh();
JOptionPane.showMessageDialog(null, "Congratulations! You won!"); //Tell the user they won. They probably deserve to know.
reset();
}
private void lose(){ //Losing a game...basically the same as above.
finished = true;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (!cells[i][j].isCovered())
cells[i][j].unflag();
cells[i][j].reveal();
}
}
refresh();
JOptionPane.showMessageDialog(null, "GAME OVER."); //Dialogue window letting the user know that they lost.
reset();
}
private boolean won(){ //Check if the game has been won
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
if (cells[i][j].isCovered() && !cells[i][j].isMine())
{
return false;
}
}
}
return true;
}
public boolean isFinished(){ //Extremely self-explanatory.
return finished;
}
public Minesweeper(int x, int y, int d){
width = x; //Width of the board
height = y; //Height of the board
difficulty = d; //Percentage of mines in the board
cells = new Cell[width][height];
reset(); //Set mines on the board
board = new Board(this); //Create new board
reset = new JButton("Reset"); //Reset button
add(board, BorderLayout.CENTER); //Put board in the center
add(reset, BorderLayout.SOUTH); //IT'S A BUTTON! AND IT WORKS! VERY COOL
reset.addActionListener(new Actions(this)); //ActionListener to watch for mouse actions
//GUI window settings
setTitle("Minesweeper");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
pack();
setVisible(true);
}
}
//--------------------------------- END of Minesweeper.java ---------------------------------
Julia
Save the following code to a file (for example "minesweeper.jl") and include it in the Julia REPL to play the game.
# Minesweeper:
mutable struct Field
size::Tuple{Int, Int}
numbers::Array{Int, 2}
possible_mines::Array{Bool, 2}
actual_mines::Array{Bool, 2}
visible::Array{Bool, 2}
end
function Field(x, y)
size = (x, y)
actual_mines = convert(Array{Bool, 2}, rand(x, y) .< 0.15)
possible_mines = zeros(Bool, x, y)
numbers = zeros(Int, x, y)
visible = zeros(Bool, x, y)
for i = 1:x
for j = 1:y
n = 0
for di = -1:1
for dj = -1:1
n += (0 < di+i <= x && 0 < dj+j <= y) ?
(actual_mines[di+i, dj+j] ? 1 : 0) : 0
end
end
numbers[i, j] = n
end
end
return Field(size, numbers, possible_mines, actual_mines, visible)
end
function printfield(f::Field; showall = false)
spaces = Int(floor(log(10, f.size[2])))
str = " "^(4+spaces)
for i in 1:f.size[1]
str *= string(" ", i, " ")
end
str *= "\n" * " "^(4+spaces) * "___"^f.size[1] * "\n"
for j = 1:f.size[2]
str *= " " * string(j) * " "^(floor(log(10, j)) > 0 ? 1 : spaces+1) * "|"
for i = 1:f.size[1]
if showall
str *= f.actual_mines[i, j] ? " * " : " "
else
if f.visible[i, j]
str *= " " * string(f.numbers[i, j] > 0 ? f.numbers[i, j] : " ") * " "
elseif f.possible_mines[i, j]
str *= " ? "
else
str *= " . "
end
end
end
str *= "\r\n"
end
println("Found " * string(length(f.possible_mines[f.possible_mines.==true])) *
" of " * string(length(f.actual_mines[f.actual_mines.==true])) * " mines.\n")
print(str)
end
function parse_input(str::String)
input = split(chomp(str), " ")
mode = input[1]
println(str)
coords = length(input) > 1 ? (parse(Int,input[2]), parse(Int,input[3])) : (0, 0)
return mode, coords
end
function eval_input(f::Field, str::String)
mode, coords = parse_input(str)
(coords[1] > f.size[1] || coords[2] > f.size[2]) && return true
if mode == "o"
reveal(f, coords...) || return false
elseif mode == "m" && f.visible[coords...] == false
f.possible_mines[coords...] = !f.possible_mines[coords...]
elseif mode == "close"
error("You closed the game.")
end
return true
end
function reveal(f::Field, x::Int, y::Int)
(x > f.size[1] || y > f.size[2]) && return true # check for index out of bounds
f.actual_mines[x, y] && return false # check for mines
f.visible[x, y] = true
if f.numbers[x, y] == 0
for di = -1:1
for dj = -1:1
if (0 < di+x <= f.size[1] && 0 < dj+y <= f.size[2]) &&
f.actual_mines[x+di, y+dj] == false &&
f.visible[x+di, y+dj] == false
reveal(f, x+di, y+dj)
end
end
end
end
return true
end
function play()
print("\nWelcome to Minesweeper\n\nEnter the gridsize x y:\n")
s = split(readline(), " ")
f = Field(parse.(Int,s)...)
won = false
while true
printfield(f)
print("\nWhat do you do? (\"o x y\" to reveal a field; \"m x y\" to toggle a mine; close)\n")
eval_input(f, readline()) || break
print("_"^80 * "\n")
(f.actual_mines == f.possible_mines ||
f.visible == .!f.actual_mines) && (won = true; break)
end
println(won ? "You won the game!" : "You lost the game:\n")
printfield(f, showall = true)
end
play()
typical game:
julia> include("minesweeper.jl")
Welcome to Minesweeper
Enter the gridsize x y:
6 4
Found 0 of 5 mines.
1 2 3 4 5 6
__________________
1 | . . . . . .
2 | . . . . . .
3 | . . . . . .
4 | . . . . . .
What do you do? ("o x y" to reveal a field; "m x y" to toggle a mine; close)
o 1 1
________________________________________________________________________________
Found 0 of 5 mines.
1 2 3 4 5 6
__________________
1 | 1 . . . . .
2 | . . . . . .
3 | . . . . . .
4 | . . . . . .
What do you do? ("o x y" to reveal a field; "m x y" to toggle a mine; close)
o 1 2
________________________________________________________________________________
Found 0 of 5 mines.
1 2 3 4 5 6
__________________
1 | 1 . . . . .
2 | 2 . . . . .
3 | . . . . . .
4 | . . . . . .
What do you do? ("o x y" to reveal a field; "m x y" to toggle a mine; close)
o 2 1
________________________________________________________________________________
Found 0 of 5 mines.
1 2 3 4 5 6
__________________
1 | 1 2 . . . .
2 | 2 . . . . .
3 | . . . . . .
4 | . . . . . .
What do you do? ("o x y" to reveal a field; "m x y" to toggle a mine; close)
o 6 4
________________________________________________________________________________
Found 0 of 5 mines.
1 2 3 4 5 6
__________________
1 | 1 2 . . . .
2 | 2 . 2 1 1 1
3 | . . 2
4 | . . 1
What do you do? ("o x y" to reveal a field; "m x y" to toggle a mine; close)
m 2 2
________________________________________________________________________________
Found 1 of 5 mines.
1 2 3 4 5 6
__________________
1 | 1 2 . . . .
2 | 2 ? 2 1 1 1
3 | . . 2
4 | . . 1
What do you do? ("o x y" to reveal a field; "m x y" to toggle a mine; close)
close
ERROR: You closed the game.
Locomotive Basic
10 mode 1:randomize time
20 defint a-z
30 boardx=6:boardy=4
40 dim a(boardx,boardy)
50 dim c(boardx+2,boardy+2)
60 dim c2(boardx+2,boardy+2)
70 nmines=int((rnd/3+.1)*boardx*boardy)
80 for i=1 to nmines
90 ' place random mines
100 xp=int(rnd*(boardx-1)+1)
110 yp=int(rnd*(boardy-1)+1)
120 if a(xp,yp) then 100
130 a(xp,yp)=64
140 for xx=xp to xp+2
150 for yy=yp <