How to resolve the algorithm Minesweeper game step by step in the AutoHotkey programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Minesweeper game step by step in the AutoHotkey programming language

Table of Contents

Problem Statement

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 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)

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Minesweeper game step by step in the AutoHotkey programming language

Source code in the autohotkey programming language

; 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
*/


  

You may also check:How to resolve the algorithm Deconvolution/1D step by step in the Julia programming language
You may also check:How to resolve the algorithm Sequence: smallest number with exactly n divisors step by step in the Wren programming language
You may also check:How to resolve the algorithm Operator precedence step by step in the Furor programming language
You may also check:How to resolve the algorithm Perfect shuffle step by step in the Rust programming language
You may also check:How to resolve the algorithm Monads/Maybe monad step by step in the JavaScript programming language