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)

:- use_module(library(clpfd)).
 
% Play - run the minesweeper game with a specified width and height
play(W,H) :-
	format('
Welcome to prolog minesweeper!
: o X Y  exposes a cell of the grid
: m X Y  marks bombs
 
Any else to quit.
'),
 
	make_grid(W, H, Grid),
	!,
	play(Grid),
	!.
 
play(Grid) :- % win condition is true
	map_grid(won, Grid),
	map_grid(print_cell, Grid),
	writeln('you won!').
 
play(Grid) :- % lose condition is true
	\+ map_grid(still_playing, Grid),
	map_grid(print_cell, Grid),
	writeln('you hit a bomb!').
 
play(Grid) :- % stil playing
	map_grid(print_cell, Grid),
	parse_input(Op, X, Y),
	do_op(Op, p(X,Y), Grid, Grid2),
	!,
	play(Grid2).
 
/* Create a new Grid
 *
 * The grid is created initially as a flat list, and after everything
 * has been populated is converted into a 2 dimensional array.
 */
make_grid(W, H, grid(W,H,MappedCells)) :-
	% create a flat list
	Len is W * H,
	length(Cells, Len),
 
	% create a list of bombs that is 20% of the grid list
	NBombs is W * H / 5,
	floor(NBombs, NBint),
	format('There are ~w bombs on the grid~n', NBint),
	length(AllBombs, NBint),
	maplist(=('?'), AllBombs),
	% add the bombs to the start of the grid list
	map_bombs_to_cells(Cells, AllBombs, NewC),
 
	% randomise and convert to a 2D array
	random_permutation(NewC, RCells),
	convert_col(RCells, W, H, CreatedCells),
 
	% populate the hidden part of the grid with number of bombs next to each cell
	map_grid(adj_bomb(grid(W,H,CreatedCells)), grid(W,H,CreatedCells), grid(W,H,MappedCells)).
 
% puts the bombs at the start of the flat list before shuffling.
map_bombs_to_cells(C, [], C).
map_bombs_to_cells([_|Ct], [B|Bt], [B|R]) :- map_bombs_to_cells(Ct, Bt, R).
 
convert_row(T, 0, [], T).
convert_row([H|T], W, [H|R], Rem) :-
	dif(W, 0), succ(W1, W),
	convert_row(T, W1, R, Rem).
 
convert_col([], _, 0, []).
convert_col(C, W, H, [Row|MoreCells]) :- dif(H, 0), succ(H1, H),
	convert_row(C, W, Row, Rem),
	convert_col(Rem, W, H1, MoreCells).
 
% determine the number of bombs next to a cell (use mapgrid)
adj_bomb(_, _, _, C, cell('.',C)) :- C =@= '?'.
adj_bomb(Grid, p(X,Y), D, Cell, cell('.',NBombs)) :-
	dif(Cell, '?'),
	findall(p(Ax,Ay), (
		adj(p(X,Y), D, p(Ax,Ay)),
		indomain(Ax), indomain(Ay),
		grid_val_xy(Grid, p(Ax,Ay), Val),
		Val =@= '?'
	), Bombs),
	length(Bombs, NBombs).
 
% Print the grid (use mapgrid)
print_cell(p(X,_), dim(X,_), cell(C,A), cell(C,A)) :- format("~w~n", C).
print_cell(p(X,_), dim(W,_), cell(C,A), cell(C,A)) :- dif(X,W), format("~w ", C).
 
% determine if we have lost yet or not (use mapgrid).
still_playing(_,_,cell(A,_),_) :- A \= '*'.
 
% determine if we have won yet or not (use mapgrid).
won(_,_,cell(N,N),_) :- integer(N).
won(_,_,cell('?','?'),_).
 
% Operate on all cells in a grid, this is a meta predicate that is
% applied several times throughout the code
map_grid(Goal, G) :- map_grid(Goal, G, G).
 
map_grid(Goal, grid(W,H,Cells), grid(W,H,OutCells)) :-
	map_grid_col(Cells, 1, dim(W,H), Goal, OutCells).
 
map_grid_col([], _, _, _, []).
map_grid_col([H|T], Y, D, Goal, [NRow|NCol]) :-
	map_grid_row(H, p(1, Y), D, Goal, NRow),
	succ(Y, Y1),
	map_grid_col(T, Y1, D, Goal, NCol).
 
map_grid_row([], _, _, _, []).
map_grid_row([H|T], p(X, Y), D, Goal, [Cell|R]) :-
	call(Goal, p(X, Y), D, H, Cell),
	succ(X, X1),
	map_grid_row(T, p(X1, Y), D, Goal, R).
 
% Get a value from the grid by X Y
grid_val_xy(grid(_,_,Cells), p(X,Y), Val) :-
	nth1(Y, Cells, Row),
	nth1(X, Row, Val).
 
% Set a value on the grid by X Y
grid_set_xy(grid(W,H,Cells), p(X,Y), Val, grid(W,H,NewCells)) :- grid_set_col(Cells, X, Y, Val, NewCells).
grid_set_col([H|T], X, 1, Val, [Row|T]) :- grid_set_row(H, X, Val, Row).
grid_set_col([H|T], X, Y, Val, [H|New]) :- dif(Y, 0), succ(Y1, Y), grid_set_col(T, X, Y1, Val, New).
grid_set_row([_|T], 1, Val, [Val|T]).
grid_set_row([H|T], X, Val, [H|New]) :- dif(X, 0), succ(X1, X), grid_set_row(T, X1, Val, New).
 
% All coordinates adjacent to an x,y position
adj(p(X,Y), dim(W,H), p(Ax,Ay)) :-
 
	dif(p(X,Y),p(Ax,Ay)),
 
	% adjacent X
	Ax in 1..W,
	Xmin #= X-1, Xmax #= X+1,
	Ax in Xmin..Xmax,
 
	% adjacent Y
	Ay in 1..H,
	Ymin #= Y-1, Ymax #= Y+1,
	Ay in Ymin..Ymax.
 
% get user operation from input
parse_input(Op, X, Y) :-
	read_line_to_codes(user_input, In),
	maplist(char_code, InChars, In),
	phrase(mine_op(Op, X, Y), InChars, []).
 
mine_op(mark, X, Y) --> [m], [' '], coords(X, Y).
mine_op(open, X, Y) --> [o], [' '], coords(X, Y).
coords(Xi, Yi) --> number_(X), { number_chars(Xi, X) }, [' '], number_(Y), { number_chars(Yi, Y) }.
 
number_([D|T]) --> digit(D), number_(T).
number_([D]) --> digit(D).
digit(D) --> [D], { char_type(D, digit) }.
 
 
% Do mark operation
do_op(mark, P, G, Ng) :-
	grid_val_xy(G, P, cell(_,A)),
	grid_set_xy(G, P, cell('?',A), Ng).
 
% Do open operation, opening a bomb
do_op(open, P, G, Ng) :-
	grid_val_xy(G, P, cell(_,'?')),
	grid_set_xy(G, P, cell('*','?'), Ng).
 
% Do open operation, not a bomb
do_op(open, P, G, Ng) :-
	grid_val_xy(G, P, cell(_,A)),
	dif(A, '?'),
	grid_set_xy(G, P, cell(A,A), Ng1),
	expose_grid(P, Ng1, Ng).
 
% expose the grid by checking all the adjacent cells and operating
% appropriately
expose_grid(p(X,Y), grid(W,H,Cells), Ng2) :-
	findall(p(Ax,Ay), (
		    adj(p(X,Y), dim(W,H), p(Ax,Ay)),
		    indomain(Ax), indomain(Ay)
		), Coords),
	expose_grid_(Coords, grid(W,H,Cells), Ng2).
 
expose_grid_([], G, G).
 
expose_grid_([H|T], G, Ng) :-  % this cell has already been exposed, continue
	grid_val_xy(G, H, cell(A,B)),
	member(A, [B,'?']),
	expose_grid_(T, G, Ng).
 
expose_grid_([H|T], G, Ng) :- % ignore bombs
	grid_val_xy(G, H, cell(_,'?')),
	expose_grid_(T, G, Ng).
 
expose_grid_([H|T], G, Ng) :- % is an integer, expose and continue
	grid_val_xy(G, H, cell(_,N)),
	integer(N),
	N #> 0,
	grid_set_xy(G, H, cell(N,N), Ng1),
	expose_grid_(T, Ng1, Ng).
 
expose_grid_([H|T], G, Ng) :- % is a space, recurse
	grid_val_xy(G, H, cell('.',0)),
	grid_set_xy(G, H, cell(0,0), Ng1),
	expose_grid(H, Ng1, Ng2),
	expose_grid_(T, Ng2, Ng).