LESSER THE MARKS MORE IS THE HUNGER TO DO WELL AND YOU EXPLORE NEW WAYS TO DO THINGS BETTER. SO DONT WORRY ABOUT MARKS


Saturday, June 25, 2011

mineSweeper game run using matlab

function mineSweeper(varargin);

%
%The game is probably familiar to everyone, but the game has a slight
%twist. You may play the game such that there may be up to 7 mines per
%square instead of just one.
%
%Left click:
% - If the square is not a mine then a number will appear counting the
% number of mines in the uncovered surrounding squares.
%
% - If the square is a mine, the game is over.
%
% - If the square was already uncovered, and all the flags have been set,
% then it will uncover the surrounding squares - however, if any of
% those are actually a mine, the game is over.
%
% - If the square was flagged, one flag will be removed.
%
%Right click:
% - Set and cycle through the flags, each right click will add another
% flag.
%
% - To clear all the flags from the square, continue to right click and
% cycle back to the reset position. (Or, left click down to 0 flags.)
%
% - If the square was uncovered, nothing will happen.

%
%% Initial set up

%Change MATLAB's rand state
rand('twister',sum(100*clock));

%Number of board columns
gameBoard.gameCols = 30;
%Number of board rows
gameBoard.gameRows = 16;
gameBoard.numberBoxes = gameBoard.gameRows * gameBoard.gameCols;
%Maximum number of mines per square, default value of 1 if not set
gameBoard.maxMines = 1;
if nargin == 1;
gameBoard.maxMines = varargin{1};
if ischar(gameBoard.maxMines);%Surprise me
gameBoard.maxMines = ceil(7 * rand);
end;
end;

%Initial mine distribution matrix, each column represents a maxMines
%value and each row represents the number of mines to be placed on the
%board
mineDistribution = [...
100,60,50,40,40,35,30;...
0, 40,30,30,24,25,24;...
0, 0, 20,20,18,16,18;...
0, 0, 0, 10,12,11,12;...
0, 0, 0, 0, 6, 8, 8;...
0, 0, 0, 0, 0, 5, 5;...
0, 0, 0, 0, 0, 0, 3];

%Initial distribution vector for the case we will play
mineDistribution = mineDistribution(:,gameBoard.maxMines);

%Will be used later to determine the number of flags left to mark
flagsLeft = 0 * mineDistribution;

%gameBoard will hold the user clicks and the state of the board:
%The first page:
%0 means covered, 1 means uncovered, -X means X flags set
%The second page:
%The handles to the text representing the flags set
%The third page:
%The handles to the boxes drawn
%The fourth page:
%The mine values
temp = zeros(gameBoard.gameRows,gameBoard.gameCols);
gameBoard.mineValues = temp;
gameBoard.boxHandles = temp;
gameBoard.flagHandle = temp;
gameBoard.userClicks = temp;

%% Create game board display

%Create figure
gameHandle = figure;
figColor = [0.80 0.80 0.80];
set(gameHandle,'Units','Normalized',...
'Position',[0.15 0.20 0.70 0.50],...
'Color',figColor,...
'Name','Mine Sweeper',...
'NumberTitle','Off',...
'Resize','Off');

%Add game menu
set(gameHandle,'MenuBar','None');
menuHandle = uimenu('Label','Start New Game');
uimenu(menuHandle,'Label','At most 1 mine,',...
'Callback','closereq;mineSweeper(1);','Accelerator','1');
uimenu(menuHandle,'Label','At most 2 mines,',...
'Callback','closereq;mineSweeper(2);','Accelerator','2');
uimenu(menuHandle,'Label','At most 3 mines,',...
'Callback','closereq;mineSweeper(3);','Accelerator','3');
uimenu(menuHandle,'Label','At most 4 mines,',...
'Callback','closereq;mineSweeper(4);','Accelerator','4');
uimenu(menuHandle,'Label','At most 5 mines,',...
'Callback','closereq;mineSweeper(5);','Accelerator','5');
uimenu(menuHandle,'Label','At most 6 mines,',...
'Callback','closereq;mineSweeper(6);','Accelerator','6');
uimenu(menuHandle,'Label','At most 7 mines,',...
'Callback','closereq;mineSweeper(7);','Accelerator','7');
uimenu(menuHandle,'Label','Surprise me,',...
'Callback','closereq;mineSweeper(''s'');','Accelerator','S');
uimenu(menuHandle,'Label','End game,',...
'Callback','closereq;','Accelerator','Q');

%Add box to count number of flags remaining
flagHandle = uicontrol(gameHandle,'Style','text');
set(flagHandle,'Units','Normalized',...
'Position',[0.02 0.55 0.14 0.40],...
'BackgroundColor',figColor);

%Add message dialog box
talkHandle = uicontrol(gameHandle,'Style','text');
set(talkHandle,'Units','Normalized',...
'Position',[0.02 0.35 0.14 0.10],...
'BackgroundColor',figColor,...
'String','Good luck!','FontSize',16);

%Add game board axes
axisHandle = axes;
set(axisHandle,'Units','Normalized',...
'Position',[0.18 0.05 0.80 0.90],...
'XLim',[0 gameBoard.gameCols],'YLim',[0 gameBoard.gameRows],...
'YDir','Reverse',...
'XTick',[],'YTick',[],'Box','On');

%% Create game board values

%The indices into the board which contain a mine will be randomly
%selected using randperm
gameBoardIndex = randperm(gameBoard.numberBoxes);
mineCumulative = cumsum(mineDistribution);

%Set the first set as containing one mine
gameBoard.mineValues(gameBoardIndex(1:mineCumulative)) = -1;

%Loop through remaining random spots for next mine increment
for dMine = 2:gameBoard.maxMines;
gameBoard.mineValues(gameBoardIndex...
(mineCumulative(dMine - 1) + 1:mineCumulative(dMine))) = -dMine;
end;

%For each non-mine position, need to count the number of mines around,
%start by computing all the rows and columns
[dIndexRow,dIndexCol] = ind2sub...
([gameBoard.gameRows gameBoard.gameCols],...
1:gameBoard.numberBoxes);
dIndexRow = repmat(dIndexRow',[1 3]);
dIndexCol = repmat(dIndexCol',[1 3]);
%The rows and columns that surround are +/- 1 from the middle
surroundingPoints = repmat([-1 0 1],[size(dIndexRow,1),1]);
dIndexRow = dIndexRow + surroundingPoints;
dIndexCol = dIndexCol + surroundingPoints;

for dIndex = 1:gameBoard.numberBoxes;
if gameBoard.mineValues(dIndex) < 0;
%Nothing to count, index is flagged
continue;
end;
%Find the surrounding squares
surroundingIndices = surroundingSquares(...
dIndexRow(dIndex,:),gameBoard.gameRows,...
dIndexCol(dIndex,:),gameBoard.gameCols);

%Find these gameBoard values
temp = gameBoard.mineValues(surroundingIndices);
%Only count the flags (negative values)
k = temp < 0;
%Sum the number of flags surrounding this index
gameBoard.mineValues(dIndex) = abs(sum(sum(temp .* k)));
end;

%Display the game board in the background of the figure
gameBoard.xOffset = -0.75;%Offset from edge of row/col
gameBoard.yOffset = -0.50;%Offset from edge of row/col
[x,y] = meshgrid(...
1 + gameBoard.xOffset:1:gameBoard.gameCols + gameBoard.xOffset,...
1 + gameBoard.yOffset:1:gameBoard.gameRows + gameBoard.yOffset);

%Display text
textHandles = text(x(:),y(:),int2str(gameBoard.mineValues(:)),...
'FontSize',8);
set(textHandles,'Color',[0.00 0.00 1.00]);
%The 0 values should not display, and mines are to be colored red
k = gameBoard.mineValues(:);
set(textHandles(k == 0),'Visible','Off');
set(textHandles(k < 0),'Color',[1.00 0.00 0.00]);

%Now, cover the number of mines (otherwise the game would be pointless)
gameBoard.uncoveredColor = [0.40 0.40 0.40];%dark gray covering square
gameBoard.markedColor = [1.00 1.00 0.50];%yellow indicating flagged
%For each grid point, create a patch of a box covering the area
for dRow = 0:gameBoard.gameRows - 1;
for dCol = 0:gameBoard.gameCols - 1;
gameBoard.boxHandles(dRow + 1,dCol + 1) = patch(...
[dCol, dCol, dCol + 1, dCol + 1, dCol],...
[dRow, dRow + 1, dRow + 1, dRow, dRow],...
gameBoard.uncoveredColor);
set(gameBoard.boxHandles(dRow + 1,dCol + 1),...
'EdgeColor',[0.00 0.00 0.00]);
end;
end;

%Setup the text containing the flag information
flagsLeftString = char(zeros(gameBoard.maxMines,6));
for dMine = 1:gameBoard.maxMines;
switch dMine;
case 1;
flagsLeftString(dMine,:) = ' I - ';
case 2;
flagsLeftString(dMine,:) = 'II - ';
case 3;
flagsLeftString(dMine,:) = 'III - ';
case 4;
flagsLeftString(dMine,:) = 'IV - ';
case 5;
flagsLeftString(dMine,:) = ' V - ';
case 6;
flagsLeftString(dMine,:) = 'VI - ';
case 7;
flagsLeftString(dMine,:) = 'VII - ';
end;
end;

%% Play game
gameContinues = true;
gameWon = false;

while gameContinues;

%Update mine count
for dMine = 1:gameBoard.maxMines;
flagsLeft(dMine,1) = mineDistribution(dMine) -...
sum(sum(gameBoard.userClicks == -dMine));
end;
set(flagHandle,...
'String',[flagsLeftString,...
int2str(flagsLeft(1:gameBoard.maxMines))],'FontSize',12);

%Determine if game has been won
k = gameBoard.mineValues < 0;
if all(all(gameBoard.mineValues(k) == gameBoard.userClicks(k))) &&...
all(all(gameBoard.userClicks(~k) == 1));
%All flags set and match the mine value, and all boxes uncovered
gameContinues = false;
gameWon = true;
continue;
end;

%Get user click
try;
m = waitforbuttonpress;
if m == 1;
continue;
end;
catch;
%User closed game
return;
end;

%Return mouse position
thisPoint = get(axisHandle,'CurrentPoint');
thisRow = floor(thisPoint(1,2)) + 1;
thisCol = floor(thisPoint(1,1)) + 1;
%Return mouse button used
thisButton = get(gameHandle,'SelectionType');

if thisRow <= 0 || thisRow > gameBoard.gameRows ||...
thisCol <= 0 || thisCol > gameBoard.gameCols;
%Clicked outside the board, try again
continue;
end;

switch thisButton;
case 'normal';%Left button

%Square has a flag on it, left click void
if gameBoard.userClicks(thisRow,thisCol) < 0;
gameBoard.userClicks(thisRow,thisCol) =...
gameBoard.userClicks(thisRow,thisCol) + 1;
%Place/update flag on board
gameBoard = setFlag(gameBoard,thisRow,thisCol);
continue;
end;

%Square was not clear, but is a mine
if gameBoard.mineValues(thisRow,thisCol) < 0 &&...
gameBoard.userClicks(thisRow,thisCol) == 0;
%Oops, stepped on a mine
set(gameBoard.boxHandles(thisRow,thisCol),...
'FaceColor','None');
gameContinues = false;
continue;
end;

%Square was not clear, not a mine
if gameBoard.mineValues(thisRow,thisCol) >= 0 &&...
gameBoard.userClicks(thisRow,thisCol) == 0;
set(gameBoard.boxHandles(thisRow,thisCol),...
'FaceColor','None');
gameBoard.userClicks(thisRow,thisCol) = 1;
newlyUncovered = sub2ind...
([gameBoard.gameRows gameBoard.gameCols],...
thisRow,thisCol);
end;

%Square clear, number of flags set, clear surrounding area
surroundingIndices = surroundingSquares(...
thisRow,gameBoard.gameRows,...
thisCol,gameBoard.gameCols);

temp = gameBoard.userClicks(surroundingIndices);
k = temp < 0;
flagsSet = abs(sum(sum(temp .* k)));

if flagsSet == gameBoard.mineValues(thisRow,thisCol) &&...
flagsSet > 0 &&...
gameBoard.userClicks(thisRow,thisCol) == 1;
%Find all surrounding points that aren't flagged
k = gameBoard.userClicks(surroundingIndices) < 0;
set(gameBoard.boxHandles(surroundingIndices(~k)),...
'FaceColor','None');
gameBoard.userClicks(surroundingIndices(~k)) = 1;
newlyUncovered = surroundingIndices(~k);
if any(gameBoard.mineValues(surroundingIndices(~k)) < 0);
%Oops, set a flag incorrectly and uncovered a mine
gameContinues = false;
continue;
end;
end;

%Call a recursive function to uncover all the connected 0
%values, this will happen if the square was a 0 or if the
%flags were set and a 0 was uncovered
[newlyUncovered,gameBoard] = uncoverZeros...
(newlyUncovered,gameBoard,[]);

case 'alt';%Right button

%Box already cleared, right click void
if gameBoard.userClicks(thisRow,thisCol) == 1;
continue;
end;

%Mark flag, subtract 1 to mean another flag added
gameBoard.userClicks(thisRow,thisCol) =...
gameBoard.userClicks(thisRow,thisCol) - 1;

%Place/update flag on board
gameBoard = setFlag(gameBoard,thisRow,thisCol);
end;
end;

%Game over, display end game message
if gameWon;
newColor = [0.30 0.30 1.00];
set(talkHandle,'String','Well Done!');
else;
newColor = [1.00 0.50 0.00];
set(talkHandle,'String',' O O P S! ');
%Display flags set incorrectly
k = gameBoard.userClicks < 0 &...
gameBoard.mineValues ~= gameBoard.userClicks;
set(gameBoard.boxHandles(k),'FaceColor',[1.00 0.00 0.00]);
%Display mines not flagged
k = gameBoard.mineValues < 0 & gameBoard.userClicks >= 0;
set(gameBoard.boxHandles(k),'FaceColor','None');
end;
set(gameHandle,'Color',newColor);
set(flagHandle,'BackgroundColor',newColor);
set(talkHandle,'BackgroundColor',newColor);

end

%% Set Flags

%Function to place/update the correct flag on the board
function gameBoard = setFlag(gameBoard,thisRow,thisCol);

%Use mod arithmetic only
temp = mod(-gameBoard.userClicks(thisRow,thisCol),...
gameBoard.maxMines + 1);
gameBoard.userClicks(thisRow,thisCol) = -temp;
%If text has previously been set...
if gameBoard.flagHandle(thisRow,thisCol) ~= 0;
%...Clear flag string
set(gameBoard.flagHandle(thisRow,thisCol),'String','');
end;
%Set square color to indicate it is marked
thisColor = gameBoard.markedColor;
switch temp;%Number of flags set
case 0;%Reset square to uncovered state
flagString = '';
thisColor = gameBoard.uncoveredColor;
case 1;
flagString = ' I ';
case 2;
flagString = ' I I ';
case 3;
flagString = 'I I I';
case 4;
flagString = 'I V ';
case 5;
flagString = ' V ';
case 6;
flagString = 'V I ';
case 7;
flagString = 'VI I';
end;
%Display flags, set square color
gameBoard.flagHandle(thisRow,thisCol) =...
text(thisCol + gameBoard.xOffset,thisRow + gameBoard.yOffset,...
flagString,'FontName','Arial','FontSize',6);
set(gameBoard.boxHandles(thisRow,thisCol),...
'FaceColor',thisColor);

end

%% Surrounding Squares Indices

%Return the indices surrounding the current square(s)
function surroundingIndices = surroundingSquares(rowIndices,gameRows,...
colIndices,gameCols);

if length(rowIndices) == 1;
rowIndices = rowIndices + [-1 0 1];
end;
if length(colIndices) == 1;
colIndices = colIndices + [-1 0 1];
end;

%For one index at a time, determine the rows and columns that
%surround the index, removing those off the game board.
m = ~(rowIndices <= 0 | rowIndices > gameRows);
n = ~(colIndices <= 0 | colIndices > gameCols);
%Compute the "all-possible" pairings of rows and columns
[surroundingRows,surroundingCols] = meshgrid...
(rowIndices(m),colIndices(n));
%Easier to use indices
surroundingIndices = sub2ind...
([gameRows gameCols],surroundingRows,surroundingCols);

end

%% Uncover 0 Squares

%Recursive function to uncover squares with a 0 value
function [newlyUncovered,gameBoard] =...
uncoverZeros(newlyUncovered,gameBoard,alreadyCleared);

%Game size
[gameRows,gameCols] = size(gameBoard.mineValues);

%Keep only the new squares that are a 0
newlyUncovered = newlyUncovered...
(gameBoard.mineValues(newlyUncovered) == 0);

while ~isempty(newlyUncovered);
%Take the first index
[rowIndex,colIndex] = ind2sub...
([gameRows gameCols],newlyUncovered(1));
%Return all points the surround this square
temp = surroundingSquares(rowIndex,gameRows,colIndex,gameCols);

%Add to a list of squares already dealt with to avoid an infinite
%recursion
alreadyCleared = unique([alreadyCleared;newlyUncovered(1)]);

%Remove squares marked as already clear
temp = setdiff(temp(:),alreadyCleared);
%Remove point currently being cleared
newlyUncovered(1) = [];
%Add the surrounding squares to this list
newlyUncovered = unique([newlyUncovered;temp(:)]);

%If the user set a 0 as a flag don't uncover
temp = temp(gameBoard.userClicks(temp) >= 0);

%Mark these squares as cleared
gameBoard.userClicks(temp) = 1;
set(gameBoard.boxHandles(temp),'FaceColor','None');

%Recursive call, will continue until the trail of 0s has been
%uncovered
[newlyUncovered,gameBoard] = uncoverZeros...
(newlyUncovered,gameBoard,alreadyCleared);
end;

end

No comments:

Post a Comment