Programming Tic-Tac-Toe in Java


<<Previous | ToC | Next >>
 

Better Code

Most programming languages give (or in the case of Java, force on) you a way to tell the computer what kind of data you are willing to accept as input. In English you do that with an extra number after the variable name that is to receive the input; for number input you put -1 there, and anything the use types that is not a number, it ignores. The user sees that their input is not echoing back to the console panel, so they have a clue that it's not accepted. The computer also flashes a prompt. Java simply crashes (throws an exception) and the program is required to catch it and do something (or nothing) about it. But what if the input value is a number, but not in the range 1 to 9? For this we need extra code. In a user-friendly program like a game, you need to do this inside a small loop which exits when the data is acceptable, and keeps trying if not. Something like this:
repeat
  Input play -1
  if play>0 if play<10 exit
  Next
Or if you really wanted to be careful (because the user could enter a fractional number in English; Java prevents that by throwing an exception, which you would need to catch) round the input value to a whole number before doing anything else with it:
repeat
  Input play -1
  Round play
  if play>0 if play<10 exit
  Next


That covers inputs out of range, but what about overstrike? It's easy enough to look at that position in the board to see if it has already been played:

  if play>0 if play<10 if board[play]<"A" exit
The English version of that takes a couple extra lines, but you can do it, if you feel so inclined. You could also add a message to the user that the square has been taken, or (in this case) they can see it easily enough, your choice.

Another problem is if nobody won, a "Cat's game," the iteration of this program will complete when the board is full, then proudly announce that "O won" which isn't true. I'd bet you are skilled enough to fix that one, right?

The program so far is a little slow in the English computer (you probably won't notice it in Java, which runs at machine speed) but we do not need to test for a win until after X has played at least three squares. The easy way to do that is to add a turns counter, initialized to zero, then increment it each time through the main loop (this comes for free in the Java for-loop). Then you don't need to "Test for win" until after the fifth play:

Initialize board
let turns = 0
Repeat 9 times
  let turns = turns+1
  Show current board
  print "input for " who
  Repeat
    Input play -1
    Round play
    if play>0 if play<10 if board[play]<"A" exit
    Next
  Update board
  if turns>4 Test for win
  If won, exit
  ...


Showing the current board is a subroutine, but if it involved some complexity at the calling site, you might consider arranging the code so there's only one call. Sometimes this involves rethinking some of the processing, but that is minimal in this case. Just move everything after the Update board call and before the end of the iteration (about four lines) to be after the "Show current board" call near the top of the loop, but before it takes in the next play. The turns counter gets incremented one more time before it should first allow a test for win (or else move the increment to after it is tested), and the player who gets flipped before the first play, so it needs to be initialized with "O" rather than "X", then you can remove the final board showing:

Initialize board
let who = "O"
let turns = 0
Repeat 9 times
  Show current board
  if turns>4 Test for win
  let turns = turns+1
  If won, exit
  if who = "X" let who = "O"
    otherwise let who = "X"
  print "input for " who
  Repeat
    Input play -1
    Round play
    if play>0 if play<10 if board[play]<"A" exit
    Next
  Update board
  Next
If won, Say who " won"


If you have been following along in English, you might want to run it again to verify that everything works correctly in the English computer, including the new error checks, before we move on to Java in the next page.

<<Previous | ToC | Next >>

Revised: 2021 August 30