Project - Tic Tac Toe

The course is part of this learning path

Start course
1h 19m

This course looks at methods, which are named, self-contained blocks of code that you can call upon to help solve problems for you. We'll then take a look at some projects which will put methods into practice.

Learning Objectives

  • Understand the fundamentals of writing your own methods
  • Learn how to classify methods by their return types as well as their parameters
  • Learn about parameter passing schemes that are used by programming languages to determine how methods treat their parameters
  • Explore method overloading and two-dimensional arrays

Intended Audience

  • Beginner coders or anyone new to Java
  • Experienced Java programmers who want to maintain their Java knowledge
  • Developers looking to upskill for a project or career change
  • College students and anyone else studying Java


This is a beginner-level course and can be taken by anyone with an interest in learning about Java.


This project is going to be quite a challenge. In fact, this might be the kind of project I would give one of my beginning programming classes that they get a week or two to work on, because there's a lot to think about. I strongly recommend you sketch out how you might go about doing it. Before you start on it, though, I will give some basic recommendations and hints to make your life a little bit easier. I'll show you some basic ideas and methods I wrote to solve the problem, but you can choose to solve it a different way. For this project you will implement the classic game Tic-tac-toe, also popularly called Noughts-and-crosses in some other places. 

It is a simple game to do with pencil-and-paper, and each game itself can be finished very quickly. But it is a pretty solid challenge for programmers, especially those who are just now learning. In fact, this project is so much more challenging than the other projects we've done so far that I am providing a full help document that you can review while working on it or preparing. You can think of this project as a very strong checkpoint to see how you're doing. Don't be discouraged if you have trouble with it or even if you get through some of it and feel like it's overwhelming. A lot of projects feel that way sometimes. I would say take your time and don't try to rush through the project, even though it's longer and more complex. I'd strongly recommend going through it and trying to solve it before looking at my full solution. Let's look at the tic-tac-toe help document together and go through it a little to see what we need to do. Tic-tac-toe, also called noughts-and-crosses, is a very popular and simple game. It involves two players, one player using symbol X and the other player using an O. 

The players take turns placing their symbol on a 3 x 3 game board like this. So, it's very simple to do on pencil and paper. When a player gets three in a row. And that term is used very loosely, technically rose only go this way, and these are columns, but in this game, a row is this way, this way, or even diagonally. Okay, those are called three in a row if you get three of your symbol in a row. If you have a tie, if you end up where no one can move any more, but no one has won, that's a tie. And in tic-tac-toe, that's called the cat's game. And there's many stories about why that term is used, but one of the most popular is that it's like how a cat plays with its own tail. It chases it around and has fun but no one really wins. So, that's why we call it the cat's game. The purpose of the game is simple, yet two-fold. You want to get three in a row yourself, but you also want to prevent your opponent from getting three in a row because then they would win. So, you can block your opponent to prevent them from taking a given strategy to win. And then I give you some examples of what it looks like if X were to win, for example.

So, that's the up and down, across, diagonally, the other diagonal. And then this right here is an example of the cat's game where no one wins. See, no one's got three in a row, and all the possible spaces are exhausted, but no one's won. So, that's the cat's game. And again, you don't have to be a domain expert who knows the ins-and-outs of the topic or fill the study. But it's important for any software developer to understand the fundamentals of how something works when you're writing software for it. So, if you get hired by a company to do biochemistry, you probably would benefit from knowing a little bit about biochemistry, or at least biology, a little bit about chemistry. But you don't really have to have a degree in biology, biochemistry or genetics to write that software. 

Luckily for us, tic-tac-toe is a little bit easier than biochemistry, and it's a simple paper and pencil game. So, we can all pretty much become domain experts in it very quickly as we are also software engineers. So, now that you understand how the basic game works, let's take a look at the design ideas for how we might go about structuring our program to handle this type of game. All right, down here, design and hints. So, in this section of the document, I described the way I went about solving the project. This does not mean that you have to do it exactly the way I did or at all. However, I do expect you to use good modularization and divide some of the complexity out into multiple methods. 

So, don't try to get everything stuffed in main. You're going to have a really big headache and it's going to be kind of ugly code. So, the way I did it is I implemented the following methods. Now, there's dozens and dozens and dozens of ways to implement it. And this is not necessarily the best way. But I just gave myself this project and win at it and finished it in a, for myself probably a couple hours I thought about it and went through it and just created all these methods and you've got run game, that's the game loop. You'll notice that would actually just be called in main. So, you would just call runGame from Main. And InitializeGame would set the cells of the 2D array to spaces. So, it just makes it empty. Print, these are all obviously stubbed out, meaning they don't really have any implementations. They're just there so you can see what's going on. And so, it will compile. So, this is a good starting point if you want to start like this. So, printCurrentBoard will just take a board and print the board. That's its only job; it doesn't take user input; it doesn't do anything like that.

It just prints the board no matter what the board is. getUserInput that gets the user input, and if it's valid, sets the game board appropriately. So, already occupied returns a boolean, so it returns true if a given cell is already occupied. So, that's a little help method that you can use as you're working on the other methods. getWinner will return X, O if there's a clear winner or a space if there's no winner yet. And then IsBoardFull returns the board is full or not. Okay. You could do a space or I think in my implementation I actually put an empty string, but I might have put a space, I don't remember right off the bat. But it doesn't really matter. As long as you make it consistent, that looks like a, that looks empty to me. So, I don't think you need a space. 

You could use a space, though. So, it doesn't really matter. So, runGame. This is more details on the methods. runGame is the primary, what we call game loop, you would actually just call it from main. So, main is going to be pretty bare bones. We like to keep main clean and minimalist as possible and that improves readability of the code. So, main is just going to basically call runGame and that's it. And in a lot of games, the main method either directly contains or calls a method that contains the so-called actual game loop which keeps running until the game is complete. For tic-tac-toe this would mean the game loop will run until X wins, O wins, or the board is full, which is called the cat's game. All right. So, and then I explain different details about runGame. You can read that on your own and I tell you how I used runGame and how I implemented it and what it calls in order to get it to work, how I did it. 

And then InitializeGame talks about stuff. Now, I think, I think this is wrong, but we'll see. So, printCurrentBoard, getUserInput. It doesn't really matter as long as you are consistent in using either an empty string or a space. It doesn't really matter if you're doing that to test if we keep going, it doesn't really matter. So, printCurrentBoard takes the current board and prints the lines and the board as necessary. getUserInput takes a parameter representing whose turn it is in the game board. So, you can go through that. cellAlreadyOccupied tells you how to do that. getWinner as you have to implement that a little bit and then isBoardFull also helps and that will determine if the board is actually full, which would indicate all nine cells are occupied. So, you could use maybe that information to help you out.

So, I strongly recommend not trying to write all the methods all at once. For example, maybe try some of the easier methods that don't depend as much on the others and then build your program up from there, that would be like a bottom-up design and you test your program incrementally. So, at least write these to get started. Maybe you could just do simple tests with those without worrying about building the whole program. So, once you've got those, start using them and creating more as you can. So, it's like a puzzle in some ways and you can figure out how the different pieces go together and test your program incrementally meaning little pieces at a time. 

And then once you verify these little parts are working properly for whatever your implementation is, you can build it up. So, again, this is just, these are just how I went through it. Even the mention about the space up here, it does not matter as long as you do it consistently. And as long as you've implemented it however you want. And you might decide that you don't even like the way I did it or that I'm recommending, that's totally fine. So, let's look at the finished game running so that you can see what the user interaction might look like and how the game might work. All right, so let's run this so you can get an idea of how it would work. Right click and run, and you'll notice it prints out the game board right here. You'll see it's X's turn. Please enter the row THEN the column, from 0, 1, and 2. So, let's try 0, 0 with X's turn. And you'll see it prints out the X right here at 0, 0. Now, if I want O to go to 1, 0 that will go in the next row down. It's X's turn again. 

So, let's go, we'll say 1, 1 and that's at the diagonal. Let's say O didn't really think very much and didn't see that X was about to win and maybe O goes 0, if they try 0, 0, it'll say that cells already occupied so they can't go there. It'll wait again. So, 0, 1 and O's up there and then X. If X goes 2, 2, it'll say the winner is X because they just got the diagonal right there. So, that's how that works. So, before pausing the video and working on the project, I want to mention that you should again make sure to break your program up into methods, even if you don't use the exact same methods that I used. So, that's a major purpose of this section, right?

Breaking stuff up into methods. So again, make sure you sit down and design it. Work on it. Don't start coding right away unless you're just playing around with some ideas. But I would say maybe even make a help project or a help file just to kind of mess around with some code and see what works and then make the primary file, nice and clean, and organized. So again, pause the video, give this one your best shot, come back when you're done or if you need some help. Well, that was a big project, wasn't it? I hope you did well.

For this one, I suspect a lot of students will struggle because this is a large and complex project compared to others we have done. Even if you were able to get some parts of it working, I am really proud of you. Regardless of how you did, don't give up and keep going. So, if you really had a hard time with this project or it looked impossible to you, that's okay also. I'd recommend that you keep moving through the material in this course and then maybe in a couple of sections from now, swing back around and try the project again and see if you understand it better. Experience is an important part of learning how to code. So, before moving on, I'm going to show you my code, which you will also have access to through the course resources. So, right here, if I open the code here, you'll notice that I start with main, all it has is runGame and then runGame has the primary game loop in it, which would be this while loop here.

And I did just use an empty string, not a space. So, I use this nice empty string right here and I also keep track of whose turn it is. I start with X's turn and you notice I switched the turn very easily. Later on, I just say xTurn= ! xTurn, that just toggles and it flips it. But if it's xTurn, println it's xTurn, X's turn. If it's O's turn, printIn its O's turn. And then I call upon my methods to help here. So, I passed on the game board that I declared up here, which is just a  3 * 3 string game board. You might have decided to use char. I mean that's fine too. And right here, we've got the get user input, I pass whose turn it is essentially, is it X's turn or is it not? And we know that if it's not X's turn, it's got to be O's turn in the game board. So, getUserInput does that. And that was obviously appear after the initializeGameBoard and the printCurrentBoard even when in the loop.

We do printCurrentBoard after each turn is handled with a little bit of extra spacing. And then we test if there's a winner. Now if the winner is empty and the board is full, we set winner equal to C. So, we're never going to actually set the winner to C inside getWinner. But you might have done that, you might have determined a way to do that. If the winner is C rather, it will break out of this loop because this loop only continues as long as the winner is empty string or you might have used space or something else. And then after it breaks out of the loop, if the winner is C, it will say it was the cat's game. Otherwise we know that once we've broken out of the loop, someone had to have won or it had to be the cat's game. So, if it wasn't the cat's game, it'll say the winner is and then the winner, right, which we got from getWinner.

Couple other things, initializeGameBoard and printGameBoard are pretty straightforward. So, initializeGameBoard takes the game board and we just go through a double loop and set that equal to spaces. And then printCurrentBoard is also fairly simple. We go through here. The things that might be a little bit trickier, you might have found a little bit challenging is when do I print the bar. Well, I did this little trick here, I didn't want to always print the bar around the side because then it would surround the whole thing. So, it would put like bars here as well. I just want them in the middle. So, when it's through the first iteration zero and then one but I don't want to print on two, I wanted to go to the next line. So, for the the inner loop here that handles columns, I put spaces and then this bar symbol right here, which is usually on most keyboards;

The standard keyboards at least in the United States. Above the enter key, there's a bar and then a backslash. If you hold down shift, you can do the bar. It's the same symbol you would use for the OR operator but you'd use two of them for the OR operator in Java. Notice that I am also using print not println. I do a println after that particular row. And as long as we're in the first two rows, this one and this one, then I do print these as well. Good. All right. So, get user inputs pretty straightforward. As I keep asking, I asked for the row and the column, and I said keep asking a false as long as they find that it's a valid row and column. And then if it is already occupied, I print out that cell is already occupied. And then I determine if it's X's turn by this point, we know that we have a valid row and column. So, if it's X's turn, I place an X at that location on the game board.

And then if it's not, I put an O in the location, and you'll notice that is called right before a printCurrentBoard. So, then we reprint whatever's in the board. So, these methods all do something very specific and they try not to step on one another's toes too much unless they're trying to help another method. So, printGameBoard doesn't need to know what get user inputs doing. That's part of the modular design that we're working on. So, getWinner goes through here and determines whether we have a match. So, this is determining row and then column, and then these are the two different diagonals. So, upper left to bottom right, going here to there, so that would be upper left to bottom right and then lower left to upper right is the other one. 

So, we determine if we have a diagonal match. So, at any point, if it returns whoever's there at that cell, we know that we have a winner there. If all three in a row have a match, and otherwise if it gets past all of those, then we're going to return our empty string or our space or whatever we want. isBoardFull, this one was not super hard. I think you could have figured out pretty couple different ways to do it, but this is a pretty simple way to do it. I call it countFill. And I go through, and I look to see if each cell of the game board that is not equal to a space. I know it has to have an X or an O in there, I don't really care which one, and then I increment countFill. Once it gets through testing the whole board, if it determines that all of them are filled, then the countFill will be nine. So, if countFill is nine, this is going to return true. And that will tell me that the board is in fact full. Great job, everyone. So, keep learning and improving your skills, no matter where you're at right now.

And don't let this be a big discouragement if you had a really hard time with it, that's totally fine. That's very much to be expected. Especially with people who are just beginning, this was a very, very big challenge. So, in the next lecture though, we'll do the section wrap up. I'll see you there.


About the Author
Learning Paths

John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.

Covered Topics