Lecture 6

Chapter 6 - MATLAB Programs

Synopsis
In the past lectures, we've seen many of the fundamental elements of programming. We can now put these elements together to build more elaborate MATLAB programs. One method for piecing these elements together is called Modular Programming. In this lecture, we will outline the idea for a modular game of Tic-Tac-Toe. The topics involved in this game can be found in Chapter 6 - MATLAB Programs.


Daily Quiz

Quiz 9


Modular Programming

Modular Programming is a design technique aimed at making programs more flexible and easier to develop. The ideas is that we break apart the different tasks of our algorithm into separate functions. Each function is built to handle a particular job, and the group of functions are used together to accomplish some larger task. Modifying a particular aspect of your program becomes much easier because you only need to change the particular function that performs that job. Let's design a game of Tic-Tac-Toe to see the benefits of Modular Programming.

Tic-Tac-Toe

Tic-Tac-Toe is a classic game where two players attempt to fill an entire row, column, or diagonal of a 3x3 grid. Our goal is to build a human vs. computer version of Tic-Tac-Toe. So where do we begin? As always, we should sketch out an algorithm.

  1. Prompt the user for their move or ask the computer for its move
  2. Add move to the game board and display
  3. Check for a winner
  4. If there is no winner and there are moves to be played, switch to next user and repeat

We could write this entire program as a single script, but it would likely become very complicated and difficult to read. If we did write this game as one script, how would we modify the code in the future to, for example, create a better looking display? It would likely be very difficult. This is where Modular Programming can help. If we write each step of our algorithm as a separate function, updating and changing those functions is easy and less likely to break the overall workings of our game. So, let's get started. We will check out how to convert each step of our algorithm to a function.

1. Prompt the user for their move or ask the computer for its move

This step actually consists of two different tasks: prompting the user for a move and asking the computer for a move. Therefore, we should break this step into two different functions--one called getmove (for the human) and the other called makemove (for the computer).

A critical aspect of Modular Programming is determining what arguments each function requires and returns. Since the getmove and makemove functions do not depend on any prior information, neither function requires an input argument. And since their jobs are to provide the row and column index of a move, they should both return two values (row index and column index). Let's define these two functions without actually writing the action.

1
2
3
4
function [ r, c ] = getmove()
%prompts the user for a move and returns the row, column index of that move
    action
end
1
2
3
4
function [ r, c ] = makemove()
%generates a move and returns the row, column index of that move
    action
end

In the function definitions, we have two return values and zero input arguments. The beauty of Modular Programming is that we don't need to worry about how getmove and makemove accomplish their task. We only need to know that they require zero input arguments and will return two values. This means that we can alter the code within either function as much as we like, and it will not break any other parts of our program as long as the function continues to take zero input arguments and return two values.

2. Add move to the game board and display

Again, this step contains two separate tasks: add move to the game board and display the updated game board. Since the two tasks are always performed together, we can write one as a function and the other as a subfunction. The main function will be called showmove, and it will add the current move to the game board. showmove will also call a subfunction named drawboard to display the game board. In order for showmove to update the game board with the new move, it will require some input arguments. Specifically, it will need to know the previous game board, the row and column index of the current move, and the player that made the move. Since showmove updates the game board, it should return the updated board. What about drawboard? Well, its job is to display the game board. So it requires the game board as the input arguments and returns nothing. Here is how we can define showmove and drawboard.

1
2
3
4
5
6
7
8
9
10
11
12
function [ board ] = showmove(board, r, c, player)
%adds move to the game board and displays result
    action
    
    %call subfunction drawboard to display graphic
    drawboard(board)
end

function drawboard(board)
%displays the game baord as a figure
    action
end

Unlike the functions we've seen in the past, drawboard lives inside the file showmove.m. Typically, subfunctions perform a very specific task required by the main function. Keeping the tasks separated will make it easier to modify our code in the future. This is likely for drawboard as we may think of better graphical displays in the future.

3. Check for a winner

After each move, we must check the game board to see if the move resulted in a win. We can write a function called checkwinner that will check the game board and return true if there is no winner and false if there is a winner. We don't need to worry about how this function works at the moment, but we do need to determine the input arguments and output values. As we mentioned, the checkwinner will need the game board as an input argument, and it will return a boolean as output. Here is the function definition for checkwinner.

1
2
3
4
function [ nowinner ] = checkwinner(board)
%check the game board for a winning row, column, or diagonal
    action
end

As we can see, the function takes one argument and returns one value. By isolating this code in a function, we can easily call checkwinner after each move without making our program unreadable.

4. If there is no winner and there are moves to be played, switch to next user and repeat

Great! We've reached the last part of our program. As you can probably sense, this step requires some higher-order access. We need to know the current state of the game, and determine how to proceed. These actions are best handled by a main script, often called a wrapper. They are called wrappers because they wrap up all of the functions that we've written into one program.

Since this step includes the word 'repeat', we are going to need some type of loop. Since the number of repeats depends on conditions, we are going to need a while loop. One of the conditions is the number of moves. There are nine spaces in a 3x3 grid, so we can only have nine possible moves. We also want to stop the game if the current move results in a win. If the loop executes, we need to run steps 1-3 and then switch players. Let's define our wrapper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
%initialize game variables
player = 1;      %we will start the game with player 1
nmoves = 0;      %we have not made any moves yet
nowinner = true; %there are no winners yet
board = nan(3);  %create the game board

%play game
while nmoves < 9 && nowinner
    %prompt player 1 for input
    if player == 1
        %get users move
        [r, c] = getmove();     
    else
        %make move
        [r, c] = makemove();
    end
    
    %display move
    board = showmove(board, r, c, player);
    
    %check if move won
    nowinner = checkwinner(board);
    
    %update counter and switch player
    nmoves = nmoves + 1;
    
    if player == 1
        player = -1;
    else
        player = 1;
    end
end

This might look a bit intimidating, but we've already defined many of the elements in this program. First, the program defines a set of game variables to get us started. Then we use a while loop to control how many turns the game is played. Based on the conditions of the while loop, we will play until there have been 9 moves or until there is a winner. Then, depending on the player, we either prompt the user for input or generate a move with getmove and makemove, respectively. After the move has been selected, we call showmove to update the game board and display the move. We then need to check for a winner using checkwinner. Lastly, we increment the number of moves and switch players.

That's it! We just built the architecture for a game of Tic-Tac-Toe. The only thing we have to do now is write the code for each function, and our game will be operational.


Final Words

Modular Programming is an extremely useful technique when designing large MATLAB programs. It allows us to break apart the problem into smaller, reusable, and flexible chunks. In the next lecture, we will work out the code for the functions outlined above.