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