function slick(s)
% slick.m is a function that plays Slick Slack Sloe
% argument s (if present) overrides the default board dimension of 4 x 4

% Record of revisions:
%     Programmer - Charles Riedesel
%     Date       - 9/30/2013
%
% Parameter:
%     s     - dimensions of the board
% Variables:
%     names - cell array of the two player names
%     t     - starting player (alternates each game)
%     again - another game? (same players and dimensions)

if nargin == 0                % if no input argument, set default to 4 x 4
   s = 4;
end

names = getNames();

t = 1;                        % first game starts with player 1
again = true;
while again                   % allow for repeat games
   playGame(names, s, t)      % actually play the game
   t = 3 - t;                 % tricky code to alternate starting player
   again = input('another round (1 or 0)? ');
end
end

% ------------------------      Play a Single Game    -----------------------

function playGame(names, s, t)
% Parameters:
%     names - cell array with player names
%     s     - dimensions of the board
%     t     - starting player (1 or 2) 
% Variables:
%     b     - s x s board for the game (of char)
%     m     - struct for player's move
%        m.type     - type of move, char (n s e w shifts or q for quit or m)
%        m.row      - row affected by move
%        m.col      - column affected by move
%     dxy   - struct to indicated win by .x or .y (or both!)

b = char(zeros(s)+' ');       % tricky way to make s x s array of spaces
% b = randi([1,3], s);          use this code to speed up testing!
% b(b == 1) = 'X';                 it initializes board to a random 2/3 
% b(b == 2) = 'O';                 completed game
% b(b == 3) = ' ';

m.type = ' ';                 % defaults for m and dxy
dxy.x = false;
dxy.y = false;

showBoard(b);                             % loop alternates players in a game
while m.type ~= 'q' && ~dxy.x && ~dxy.y   % repeat if not quit and neither has won
   m = getMove(names{t});
   while ~validMove(b, m, t)              % idiot proofing
      disp('oops - try again')
      m = getMove(names{t});
   end
   b = makeMove(b, m, t);
   dxy = checkRowsCols(b);                % anyone win yet?
   t = 3 - t;                             % alternate player's turns
   showBoard(b);
end

if dxy.x && dxy.y                         % game over!  tie?
   fprintf('Tie game! - both scored\n')
elseif dxy.x
   fprintf('Congrats %s!  You win!\n', names{1});
elseif dxy.y
   fprintf('Congrats %s!  You win!\n', names{2});
end
end


% --------------------------     Get Player's Names   -----------------------
function names = getNames()
names{1} = input('first player''s name: ','s');
names{2} = input('second player''s name: ','s');
end

% --------------------------    Show the Game Board   -----------------------
function showBoard(b)
% Parameter b is the s x s game board to show
% Variables: 
%    s     - length of a side
%    col   - column number
%    row   - row number

fprintf('\n')
s = length(b);
for col = 1:s                 % top border
   fprintf('+-')
end
fprintf('+\n')
for row = 1:s                 % board row and border pair
   fprintf('|%c', b(row,:))   % display entire row
   fprintf('|\n')
   for col = 1:s              % border
      fprintf('+-')
   end
   fprintf('+\n')
end
fprintf('\n')
end

% ---------------------------    Get Player's Move   -------------------------
function m = getMove(n)
% Parameter n is the current player's name
% Return Variable m is the move (in a struct)
%    .type  - type of move:  see input function below
%    .row   - affected row
%    .col   - affected column

fprintf('%s, your move - ', n)
m.type = input('shift (nsew) place marker (m) or quit (q): ','s');
if m.type == 'm'                          % place a marker move
   m.row = input('row: ');
   m.col = input('col: ');
elseif m.type  == 'n' | m.type == 's'     % vertical shift, need column
   m.row = 0;
   m.col = input('col: ');
elseif m.type == 'e' | m.type  == 'w'     % horizontal shift, need row
   m.row = input('row: ');
   m.col = 0;
end
end

% --------------------------    Is the Requested Move Valid?  -----------------
function v = validMove(b, m, t)
% Parameters:
%    b  - the game board
%    m  - the requested move (a struct)
%    t  - the current player number (1 or 2)
% Variables:
%    s  - length of a side
%    mark - players' marks, X and O
%    v? - subdivided logical queries for validity
%       v1 - legal move selection letter
%       v2 - legal place a marker move
%       v3 - legal vertical shift
%       v4 - legal horizontal shift

s = length(b);
mark = 'XO';
v1 = any('nsewmq' == m.type);
v2 = m.type == 'm' && m.row > 0 && m.col > 0 && m.row <= s && m.col <= s && b(m.row,m.col)==' ';
v3 = (m.type == 'n' || m.type == 's') && m.col > 0 && m.col <= s && any(b(:,m.col) == mark(t));
v4 = (m.type == 'e' || m.type == 'w') && m.row > 0 && m.row <= s && any(b(m.row,:) == mark(t));
v5 = m.type == 'q';
v = v1 && (v2 || v3 || v4 || v5);

% Display appropriate error message if needed

if ~v1
   disp('invalid command')
elseif ~v && m.type == 'm'
   disp('illegal placement')
elseif ~v && m.type == 'n' || m.type == 's'
   disp('illegal column')
elseif ~v && m.type == 'e' || m.type == 'w'
   disp('illegal row')
end 
end

% ---------------------------    Make the Requested Move   --------------------
function b = makeMove(b, m, t)
% Parameters:
%    b  - the game board
%    m  - the requested move (a struct)
%    t  - current player number (1 or 2)

mark = 'XO';
if m.type == 'm'
   b(m.row, m.col) = mark(t);
elseif m.type == 'n'
   b(:,m.col) = [b(2:end,m.col);b(1,m.col)];
elseif m.type == 's'
   b(:,m.col) = [b(end,m.col);b(1:end-1,m.col)];
elseif m.type == 'e'
   b(m.row,:) = [b(m.row,end),b(m.row,1:end-1)];
elseif m.type == 'w'
   b(m.row,:) = [b(m.row,2:end),b(m.row,1)];
elseif m.type == 'q'
   disp('quitting!')
end
end
 
% ---------------------------    Check for a Win Configuration   ----------------
function d = checkRowsCols(b)
% Parameter:
%    b  - The game board
% Return Value:
%    d  - Struct, .x for player 1 win, .y for player 2 win (could be both!)

mark = 'XO';
d.x = straight(b, mark(1));
d.y = straight(b, mark(2));
end

% ---------------------------    Is There a Win for Player m?   -----------------
% Parameters:
%    b  - The game board
%    m  - Player number (1 or 2)

function d = straight(b, m)
eyeb = eye(length(b));
d = any(all(b' == m)) || ...
    any(all(b == m)) || ...
    all(sum(b .* eyeb) == m) || ...
    all(sum(fliplr(b) .* eyeb) == m);
end

