Matlab Connect4 Code
Matlab Connect4 Code
% Play Connect 4!
%
% If you want to edit the board colors, you may input your own.
%
%
Acceptable Input Properties:
%
'BoardColor'
%
'BoardAccentColor'
%
'Player1Color'
%
'Player2Color'
%
'Bounce'
%
'AnimateDrop'
%
%
Acceptable Inputs are the standard color strings
%
white
-- 'w'
%
black
-- 'k'
%
yellow
-- 'y'
%
magenta -- 'm'
%
cyan
-- 'c'
%
red
-- 'r'
%
green
-- 'g'
%
blue
-- 'b'
%
%
or RGB color vectors.
%
% You may also turn off:
%
The physical dynamics by specifying 'Bounce','off'
%
The Board Clearing Animation by specifying 'AnimateDrop','off'
%
%
% Created By: Steven Terrana
%
Date: March 19th, 2015
end
else % the user is trying to do a RGB vector
if ~isequal(size(varargin{i}),[1 3]) || sum( varargin{i} < 0 ) || sum( varargin{i} > 1 )
error('RGB Vectors are [1X3] vectors containing values between [0,1]')
end
end
else % the input is for bounce or animatedrop
if ~sum(strcmp({'on','off'},varargin{i}))
error('''%s'' is not an option.
end
end
end
% -- Set the properties if specified, otherwise set defaults -- %
if sum(strcmpi('BoardColor',varargin))
callback.colors.BoardColor = varargin{find(strcmpi('BoardColor',varargin)==1)+1};
else
callback.colors.BoardColor = [0 0.5 0.8];
end
if sum(strcmpi('BoardAccentColor',varargin))
callback.colors.BoardAccentColor =
varargin{find(strcmpi('BoardAccentColor',varargin)==1)+1};
else
callback.colors.BoardAccentColor = 'k';
end
if sum(strcmpi('Player1Color',varargin))
callback.colors.Player1Color = varargin{find(strcmpi('Player1Color',varargin)==1)+1};
else
callback.colors.Player1Color = 'r';
end
if sum(strcmpi('Player2Color',varargin))
callback.colors.Player2Color = varargin{find(strcmpi('Player2Color',varargin)==1)+1};
else
callback.colors.Player2Color = 'y';
end
if sum(strcmpi('Bounce',varargin))
callback.bounce = varargin{find(strcmpi('bounce',varargin)==1)+1};
else
callback.bounce = 'on';
end
if sum(strcmpi('AnimateDrop',varargin))
callback.AnimateDrop = varargin{find(strcmpi('AnimateDrop',varargin)==1)+1};
else
callback.AnimateDrop = 'on';
end
%-------------------------------------------------------------------------%
% Define a custom "invisible" pointer
%
"callback" is a structure containing the variables that must be
%
accessible in the various callbacks.
callback.pointer_array = nan(16);
i, top_y
+ j,callback.colors.BoardColor,'EdgeAlpha',0);
i, bottom_y + j,callback.colors.BoardColor,'EdgeAlpha',0);
%
%
%
%
%
players turn
number of chips in each column
array with player one's chips
array with player two's chips
index for storing marker handles
function mousemoving(~,~,fig)
% grab the data
callback = guidata(fig);
% get the mouse location in units of the axes
coords = get_coords(callback.hAxes);
% find the axis bounds
xl = xlim;
yl = ylim;
% this if-statement structure:
%
- keeps the circle inside the figure but above the game board
%
- changes what type of pointer is displayed
%
- invisible if moving the circle
%
- a hand if otherwise
set(fig,'Pointer','custom','PointerShapeCData',callback.pointer_array)
if coords(1) < (xl(1)+callback.radius)
set(fig,'Pointer','hand')
coords(1) = xl(1) + callback.radius;
end
if coords(1) > (xl(2) - callback.radius);
set(fig,'Pointer','hand')
coords(1) = xl(2) - callback.radius;
end
if coords(2) < 7.5
set(fig,'Pointer','hand')
coords(2) = 7.5;
end
if coords(2) > (yl(2) - callback.radius);
set(fig,'Pointer','hand')
coords(2) = yl(2) - callback.radius;
end
end
set(callback.current_marker,'YData', i + callback.radius * sin(callback.t));
pause(0.01)
end
else
bounce(y_start,callback.column_count(col), callback.current_marker, callback.radius,col)
end
guidata(fig,callback)
uiwait
end
callback.turn = 2;
color = callback.colors.Player2Color;
else
callback.player2(7-callback.column_count(col),col) = 1;
result = CheckForWinner(callback.player2,7-callback.column_count(col),col);
if result == 1
callback.messagehandle = CreateMessage(fig,'Player 2 Wins!!',callback.colors.Player2Color);
guidata(fig,callback)
uiwait
end
callback.turn = 1;
color = callback.colors.Player1Color;
end
% assuming there was no victory and play continues then add the
% previous marker to the column count
callback.column_count(col) = callback.column_count(col) + 1;
guidata(fig,callback)
uiwait
end
if strcmp(callback.AnimateDrop,'on')
drop(fig)
end
delete(callback.markers)
callback = rmfield(callback,'markers');
callback.marker_idx = 1;
callback.player1 = zeros(6,7);
callback.player2 = zeros(6,7);
callback.column_count = ones(1,7);
callback.turn = 1;
end
% if the game's continuing, createa new marker and place it where
% the mouse is
coords = get_coords(callback.hAxes);
% find the axis bounds
xl = xlim;
yl = ylim;
% this if-statement structure:
%
- keeps the circle inside the figure but above the game board
%
- changes what type of pointer is displayed
%
- invisible if moving the circle
%
- a hand if otherwise
set(fig,'Pointer','custom','PointerShapeCData',callback.pointer_array)
if coords(1) < (xl(1)+callback.radius)
set(fig,'Pointer','hand')
coords(1) = xl(1) + callback.radius;
end
if coords(1) > (xl(2) - callback.radius);
set(fig,'Pointer','hand')
coords(1) = xl(2) - callback.radius;
end
if coords(2) < 7.5
set(fig,'Pointer','hand')
coords(2) = 7.5;
end
if result
set(callback.current_marker,
'FaceColor',callback.colors.Player1Color,
'EdgeColor','k',
'LineWidth',1.5);
end
uistack(callback.current_marker,'bottom')
end
end
% update the data
guidata(fig,callback)
% to avoid a delay in marker visibility and being able to move the marker
% make the marker visible now.
set(callback.current_marker,'Visible','on')
end
function button_push(~,~,fig)
% reset the game
% grab the data
callback = guidata(fig);
set(callback.button,'Visible','off')
callback.reset_flag = 0;
if strcmp(callback.AnimateDrop,'on')
set(callback.current_marker,'Visible','off')
drop(fig)
set(callback.current_marker,'Visible','on')
end
pause(0.1)
% delete all the markers and set game vars to new
delete(callback.markers) % clear the board
callback = rmfield(callback,'markers');
callback.player1 = zeros(6,7); % reset the game data
callback.player2 = zeros(6,7);
callback.column_count = ones(1,7);
callback.turn = 1;
callback.marker_idx = 1;
% change the marker to red if it was player 2's turn when you reset
set(callback.current_marker,'FaceColor',callback.colors.Player1Color,'EdgeColor','k','Lin
eWidth',1.5);
% update the game data
guidata(fig,callback);
end
function play_again(~,~,fig)
% grab the data
callback = guidata(fig);
coords = (coords-offset).*axesScale+axesLimits(1,:);
function [value]=get_in_units(hObject,propName,unitType)
oldUnits = get(hObject,'Units');
set(hObject,'Units',unitType);
value = get(hObject,propName);
set(hObject,'Units',oldUnits);
%#
%#
%#
%#
Get the
Set the
Get the
Restore
end
end
function t = CreateMessage(fig,str,bgColor)
callback = guidata(fig);
t(1) = text(4.55,9.55,str,'HorizontalAlignment','center','FontSize',18);
x = xlim; y = ylim;
t(2) = line( [ (0.37 * diff(x))+x(1) - 0.9, 0.04 * diff(x) + 6.28] , ((0.87 *
diff(y)) + y(1)+0.6)*ones(1,2)
,'color','k','LineWidth',2);
t(3) = line( ( (0.37 * diff(x))+x(1) - 0.94) * ones(1,2), [ (0.87 * diff(y)) +
y(1)+0.6,(0.87 * diff(y)) + y(1)+1.11 ]
,'color','k','LineWidth',2);
t(4) = line( (0.04 * diff(x) + 6.315) * ones(1,2), [ (0.87 * diff(y)) +
y(1)+0.6,(0.87 * diff(y)) + y(1)+1.1 ]
,'color','k','LineWidth',2);
t(5) = line( [ (0.37 * diff(x))+x(1) - 0.93, 0.04 * diff(x) + 6.31] , ((0.87 *
diff(y)) + y(1)+1.14)*ones(1,2)
,'color','k','LineWidth',2);
if str(1) == 'P'
cx = 2.95 + 0.2 * cos(linspace(0,2*pi));
cy = 9.57 + 0.2 * sin(linspace(0,2*pi));
t(6) = patch(cx,cy,bgColor,'EdgeColor','k','LineWidth',1.5);
end
set(callback.play_again,'Visible','on')
set(callback.button,'Visible','off')
set(fig,'Pointer','arrow')
set(fig,'WindowButtonMotionFcn',[],'WindowButtonDownFcn',[])
end
function result = CheckForWinner(player,row,col)
% The purpose of this script is to determine whether or not
% the marker that was just placed results in a player victory.
%
% The methodology for determining this is simple.
%
Each marker can be a part of 16 different
%
vectors that result in 4 in a row.
%
%
These 16 combinations are created and then checked
%
against the players marker position matrix to see if
%
they have four markers in any of these vectors.
%
If they do, they win.
% defines the location of the last placed marker
% inside the frame array.
%
% Framing the board with zeros is necessary as to
% avoid errors when searching for a winning vector
% that falls outside the board (on the edges)
pos = [row + 3,col + 3];
frame = zeros(3 * 2 + 6, 3 * 2 + 7);
frame(4:9,4:10) = player;
% Initializes the directions matrix.
% This will store the 16 possible array vectors
% that could result in a victory.
directions = zeros(4,2,16);
i = 1; % an index to successfully add to this array.
%
0
%
|
%
|
%
|
a =[-3 0 ; -2 0 ; -1 0];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
%% Possible Horizontal Combinations For Victory
% 0 | | |
a =[0 1 ; 0 2 ; 0 3];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
% | 0 | |
a =[0 -1 ; 0 1 ; 0 2];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
% | | 0 |
a =[0 -2 ; 0 -1 ; 0 1];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
% | | | 0
a =[0 -3 ; 0 -2 ; 0 -1];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
%% Possible Diagonal (/) Combinations For Victory
%
|
%
|
%
|
%
0
a =[1 1 ; 2 2 ; 3 3];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
%
|
%
|
%
0
%
|
a =[-1 -1 ; 1 1 ; 2 2];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
%
|
%
0
%
|
%
|
a =[-2 -2 ; -1 -1 ; 1 1];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
%
0
%
|
%
|
%
|
a =[-3 -3 ; -2 -2 ; -1 -1];
directions(:,:,i) = [pos; [pos(1) + a(:,1), pos(2) + a(:,2)]];
i = i + 1;
%% Possible Diagonal (\) Combinations For Victory
% |
%
|
%
|
%
0
a =[-1 1 ; -2 2 ; -3 3];
end
if sum(winning_combination) == 4
result = 1;
end
end
end
function bounce(H,endloc,ball,radius,col)
% coefficient of restitution.
% max cor based on how high you hold the marker before you drop it
Max_COR = 0.3 + (0.1)/(9.6-7.5) * (H - 7.5);
% cor reduced based on how many markers are already in the column
COR = Max_COR - (Max_COR - 0.05)/6 * (endloc-1);
% grab the bouncing data
h = ballbounce(H,COR,3);
h = h + endloc + 0.49;
h = h( h<=H );
for i = 1:19:length(h)
if h(i) <= 7.3
max_row = 0;
for i = 1:length(callback.markers)
H = mean(callback.markers(i).YData);
idx = round(H - 0.5,0);
if idx > max_row
max_row = idx;
Max_COR = 0.5;
COR = Max_COR - (Max_COR - 0.1)/6 * (H-1.5);
h = ballbounce(H,COR,3);
h = h + callback.radius;
Row_h(idx) = {h( h <= H )};
L(idx) = length(Row_h{idx});
end
Row{idx} = [Row{idx}, callback.markers(i)];
end
ideal_pause_length = [ 1/200 1/175 1/150 1/125 1/100 1/50];
a = tic;
for h_idx = 1:max(L)
for r = 1:max_row
if h_idx <= L(r)
h = Row_h{r};
set(Row{r},'YData',h(h_idx) + callback.radius*sin(linspace(0,2*pi)))
if toc(a) > ideal_pause_length(max_row)
pause(0.001)
a = tic;
end
end
end
end