function winner = tictactoe (player1, player2, watch, move)
% TICTACTOE     Playing a single game of tic-tac-toe
%
% function tictactoe (player1, player2, watch, move)
%
% Input parameters:
%
%   player1 and player2:
%       can be any combination of  'user', 'monkey', or 'your name'
%       If your name is smith then we expect to find a function smith_move.m
%       in the current directory.  The functions for 'user' (you graphically
%       select your next move by clicking with the mouse) and 'monkey'
%       (randomly picks an unoccupied board space) are already supplied.
%   watch:
%       is either 1 (display the game board) or 0 (just return the result
%       of the game.  This is useful when you want to simulate many games
%       and you don't care to see all the games).
%   move:
%       Can be:
%           0: Matlab will ask if player 1 or player 2 should make first move.
%           1: Player 1 starts the game
%           2: Player 2 starts the game
%           3: Randomly determine who starts the game
%
%   The game ends when one of the players wins or there is a draw.
%
%   winner is return as -1 (player 1), +1 (player 2), or 0 (draw).
%
% Paul Wessel, November 2004, for GG 250

% Set function handles to the chosen player functions and determine who
% should go first (mover is either -1 (player 1) or +1 (player 2).  wait is
% used to determine if we should simulate 1-second 'thinking' or not.

[P1_func, P2_func, wait] = initialize_players (player1, player2, watch);
[board, mover] = initialize_game (watch, move, player1, player2);

% We will loop over moves until the game ends.  The variable game_over will
% be initialized to 0 (FALSE) and will be set to TRUE (1) when someone wins or
% there is a draw.

game_over = 0;      % FALSE
while ~game_over  % We will play until the game is over
    if mover == -1   % Player 1's turn to move
        [row, col] = feval (P1_func, board, mover); % Player 1's move is returned
        check_row_col (row, col, player1);
        pretend_to_think = wait(1);                     % Do we wait 1 second?
    else
        [row, col] = feval (P2_func, board, mover); % Player 2's move is returned
        check_row_col (row, col, player2);
        pretend_to_think = wait(2);                     % Do we wait 1 second?
    end
    
    if watch    % OK, we want to see the moves graphically
        if pretend_to_think == 1
            pause (1)           % wait a second to pretend this was hard work
        end
        place_marker_on_board (row, col, mover);    % Plot the X (player 1) or O (Player 2)
    end
    board(row, col) = mover;    % Update the bord with the current mover ID (-1, +1)
    mover = -mover;   % Next time it is the other guy's turn
   [game_over, winner] = tictactoe_game_status (board); % Are we done yet?
end

% OK, we get here when the game is over.  Update title to reflect the
% winner (if we are watching this game).

if watch
    hold off
	if winner == -1
        title (['Player ' player1 ' won!'])
	elseif winner == +1
        title (['Player ' player2 ' won!'])
	else
        title ('Draw... How about another game?')
	end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Here lies the subfunctions used by the main function
%

function [row, col] = monkey_move (board, mover)
% This is how the monkey makes its move.  We have trained a monkey
% to randomly pick one of the available slots on the board.  No thinking
% goes into this choice.
[rows, cols] = find (board == 0);  % Find all the available slots
n_slots = length (rows);             % How many such slots are there
choice = floor (rand (1) * n_slots) + 1; % Select one at random
col = cols(choice); % Return the corresponding row, col values
row = rows(choice);

function [ P1_func, P2_func, wait ] = initialize_players (player1, player2, watch)
% Creates the function handles for the two functions so that we can easily
% set up matches between any two players, including playing against
% ourself.  We also set wait to TRUE if it is the monkey's choice AND we are
% watching the game.
P1_func = str2func (sprintf ('%s_move', player1));
P2_func = str2func (sprintf ('%s_move', player2));
wait = watch * ones(2,1);
if strcmp (player1, 'monkey'); wait(1) = 1; end
if strcmp (player2, 'monkey'); wait(2) = 1; end

function [board, first] = initialize_game (watch, order, player1, player2)
% We create a 3x3 table that represents the tic-tac-toe board that is
% initially all set to zero, meaning unused.  We then initialize who should
% go first - this depends on what order we chose.
board = zeros (3);
if order == 0   % We must manually tell Matlab who should go first
	first = input (['Who should move first (1 = ' player1 ', 2 = ' player2 '): ']);
	while ~(first == 1 | first == 2)
        disp ('Please enter 1 or 2 only!')
	    first = input (['Who should move first (1 = ' player1 ', 2 = ' player2 '): ']);
	end
    first = 2 * first - 3;  % This clever trick turns 1 into -1 and 2 into +1
elseif order == 3   % Random choice
    first = 2 * floor (rand (1)*2) - 1; % This turns 0 into -1 and 1 into+1
else
    first = 2 * order - 3;   % This again turns 1 or 2 into -1 or +1
end
% Draw the game board (if watching)
if watch
	figure (1)
	clf
	axis ([0 3 0 3])
	hold on
	axis square
	plot ([1 1], [0 3]) % Draw the lines
	plot ([2 2], [0 3])
	plot ([0 3], [1 1])
	plot ([0 3], [2 2])
    title ([ player1 ' is X and ' player2 ' is O'])
end

function [row, col] = user_move (board, mover)
% The user's choice is implemented by using the mouse.
% ginput will return the coordinates of the click which we
% turn into a row and column on the board.

title ('Your turn, human!')
[x, y] = get_valid_xy;
col = floor (x) + 1;
row = floor (y) + 1;
while board(row, col) ~= 0   % Must check the square is available
    title ('That square is already taken')
    [x, y] = get_valid_xy;
    col = floor (x) + 1;
    row = floor (y) + 1;
    end
title ('Player1 is X and player2 is O')

function [x, y] = get_valid_xy
% We need to guard against clicks outside the allowable range so that the
% code that calculates row and col is fool-proof.

[x, y] = ginput (1);
while x < 0 | x >= 3 | y < 0 | y >= 3   % Bad clicking
    title ('Please click within the boundaries of the board!')
    [x, y] = ginput (1);
end

function check_row_col (row, col, player)
% Make sure row and col are OK

if length (row) ~= 1
        disp ([player ' returned more than one row!'])
end
if length (col) ~= 1
        disp ([player ' returned more than one column!'])
end
if row < 1 | row > 3
        disp ([player ' returned row outside the range! (' num2str(row) ')'])
end
if col < 1 | col > 3
        disp ([player ' returned column outside the range! (' num2str(row) ')'])
end
 
function place_marker_on_board (row, col, mover)
% Place the marker on the board: X for Player 1, O for Player 2
if mover == -1
    text (col - 0.5, row - 0.5, 'X', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'FontSize', 48)
else
    text (col - 0.5, row - 0.5, 'O', 'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'FontSize', 48)
end

