Programming Tic-Tac-Toe in Java

<<Previous | ToC | Next >>

The program displays a nice board, and takes input and ... announced that "X won"! Why would it think that, after only one play? Besides, the subroutine to decide who won is still a stub. Nobody won. And yet, here it is, saying X won.

Can you find where in this program it decides if somebody won? Here is the main program again, for ease of viewing:

Initialize board
Repeat 9 times
  Show current board
  Input play
  Update board
  Test for win
  If so, exit
Say who " won"
Show current board

Right there, in the first line after the iteration finishes, it says (whatever is in variable who) won. So obviously it didn't repeat nine times.

So why did it quit out of the iteration early? That's pretty obvious too: Right there, in the next-last line of the iteration, it says

If so, exit

If what is so? We didn't say. Well, in English English, the previous line says to "Test for win" but the English computer doesn't know what those words mean, it just looked around and found a subroutine with that name, so that whole line is nothing but the name of a subroutine that does nothing at all except print a single line with its own name. The English computer has no idea what that subroutine does, it just looks for something that might be true, and apparently found it, so it exited the loop. Java would have saved your bacon here, and refused to run at all. Then you would know that you had to do something to that line before it will run correctly.

In English (and most every other human language) you tell somebody something like that -- especially if you are the one doing the telling, and they are (as they say in the movies), "paid to do, not ask questions," then they will figure out something and do that. That's what the English computer did today. And that's why we program in Java and not English or C or Python. Well, maybe C or Python might catch this particular error too, but certainly not neural nets*: they just do what you told it to, even if you didn't want to tell it to do that. Every programming language does that, but some languages (like Java) are better than others (like C or Python) when it comes to helping us find our mistakes.

What we need here is a variable -- I called it "won" in my program -- that the subroutine sets to true when somebody won, but otherwise it is (initialized) false. You can probably figure out where to initialize it. Do that now. Then we change the erroneous line to:

If won, exit
Try it again in English.

Now it takes your input and displays the updated board, just like we wanted. Except where are the Os?

What do we know about that? The variable who was initialized to "X" and later used to update the board when X played. What happened when O played? Right. Nothing. Or rather the same value in who was played also for O, because that variable never changed. What we need is in the outer iteration, just before it goes back to display the board and take another input, we need to toggle who from X to O and back again. The easy way to do that is with a Conditional:

if who = "X" let who = "O"
otherwise let who = "X"

Who Won

Now we are ready to do the most complicated part of the TTT scoring: deciding if somebody won. It's not that bad, we need to check each row, column, and diagonal for three in a row. Using our 1-to-9 square numbering, we need to look for all X's or all O's in squares 1+2+3 or 4+5+6 or 7+8+9 (the rows) or 1+4+7, 2+5+8, or 3+6+9 (the columns) or 1+5+9 or 3+5+7 (the diagonals). There's no simple way to do that in an iteration or two because the sequences are all different, with different step sizes. They are sequences that each one can be rolled up into an iteration, but they have different starting squares and step sizes. Later we can look at how to make our code more efficient and/or "elegant."

For now, perhaps the simplest way to code it is to make a single subroutine ThreeInaRow, which we give it three square numbers first, mid, last, and it looks in each of those three to see if all three are equal to a given player, the current player who, which just played (we do not need to check the other team until after they played). In principle we only need to check the row, column and diagonal that goes through the square most recently played, but the logic to do that is so messy (and the computer is so fast) that it's simpler to do all eight checks every time.

Why don't you try writing ThreeInaRow yourself, before you turn the page.

<<Previous | ToC | Next >>

* Neural Nets (NNs) are programmed to do just exactly what the programmer told them to, just like every other computer program. The difference between NNs  is that only a very small part of the program is in C or Python, the rest is in the form of a thousand pictures (or sonnets, or whatever the NN is supposed to "learn"), each with a one-line label, plus a million or so "random" numbers to initialize the NN. If you don't pay careful attention to what's in that training data, or if you get the initialization numbers wrong, then the NN still does what it was programmed to do, even if that's not what you wanted. See Melanie Mitchell's Artificial Intelligence, pp.38,106,113.

Revised: 2021 August 30a