HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMajor

TicTacToe logic in Java

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
javatictactoelogic

Problem

I'm currently studying Java by myself. The book I'm using (while very good)
lacks feedback (obviously). While trying to write this program, I found myself solving most of the problems I've encountered by trial and error and makeshift solutions, which I fear might have led to some haphazard code. A good example would be the initialization of several variables: bestline and bestrow in the Testing class, and bestMove in the Board class.

The code also feels very cumbersome. I would really appreciate it if you could review my code and comment on where I can improve, best practices I should adopt, etc...

```
import java.util.Scanner;

public class Testing
{
public static void main(String[] args) {
int turn = 0; // turn is either 0 or 1 , changed with Math.abs(turn-1).
Board board = new Board ();
board.initializeBoard(board);
Scanner myScanner = new Scanner(System.in);

for( ; board.scoreBoard(board) == 1000 ; ){

if (turn == 0){ //X's turn
int line = myScanner.nextInt(); // get a line
int row = myScanner.nextInt(); // get a row
for ( ; board.Square[line][row] != 0 ;line = myScanner.nextInt() , row = myScanner.nextInt() ){ // run while the square at [line][row] is taken,print the line and pick another.
System.out.println("Square Taken , please pick another.");
}
board.Square[line][row] = 1; //make the move (1 means X)
board.printBoard(board);
}

if (turn == 1){ //O's turn
int lastboard = 100; // O's worst case scenario so it has somthing to compare in the first round. // this will keep the best score that player can get
int bestline=0; // no meaning because I had to initialize
int bestrow =0; // no meaning because I had to initialize

for(int line = 0 ; line <3 ; line++){ //
for(int row = 0 ; row < 3 ; row ++){ //go over the entire board
if(board.Square[line][row]==0){ //if

Solution

Welcome to CodeReview. Your concern for the quality of your code is warranted, but don't let that put you down: digging through that book on your own, writing your own code and even exposing it to the Internet's critical eyes are impressive first steps in the right direction.

You seem interested in writing clean code. I can warmly recommend the book Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin). For me, it was a very thought-provoking book that helped me really understand why internal software quality matters (and how to achieve it).

Now, let's get to the Review.

Pulazzo's suggestions are spot on. (As he pointed out, you may want to revisit the concept of object instances and the this keyword).

In addition to the points already made, I'd like to explain some more abstract topics that really drive code quality.

Theoretical Background
The fundamental problem

Programmers like to think of themselves as smart. We tend to solve a problem, marvel at our cleverness, and leave a mess behind without even realizing it. It's only when we revisit our code later (often in the process of trying to add a feature or fix a bug), we realize that reading and understanding code is a lot harder than writing it. There's often too much information to comfortably wrap our heads around.

We then write comments (in your case, almost a comment per line) to help make sense of the whole thing, but in doing so, we add even more information, and this time there isn't even a guarantee for reliability or truthfullness: comments go off faster than milk left out of the fridge at 40°C, rendering the "information" they contain obsolete.

Even worse, comments are often redundant. It's as if we are writing everything twice! A good programmer is a lazy programmer in that she never repeats herself.

(Comments do have their place, for instance in the documentation of public APIs and explaining particularly weird decisions we made.)
The solution

In order to cope with complexity, we need to split it up and simplify it as far as possible. In other words: Divide and conquer. If our classes are small, we can easily spot the responsibilities. If our methods are small, we can easily discern the flow of control. If our lines are short, we can make sense of the statements they contain.

Conciseness isn't the only thing that matters. It's even more important to choose proper names. Without proper names, we are constantly decoding and reconstructing the information that should be apparent from reading the code alone.

Applying it to your Tic Tac Toe
The entry point

Look at your main method. It spans nearly 50 lines and contains six levels of nesting. I tried really hard, but my head nearly exploded trying to comprehend everything you were doing in there.

What if that method were to look like this?

private static final Scanner in = new Scanner(System.in);
private static final PrintStream out = System.out;
private static final Game game = new Game();

public static void main(String args[]) {
    out.println("Welcome to TicTacToe.");
    out.println(game);
    while (game.isRunning()) {
        informPlayersOfNextTurn();
        makeNextMove();
        out.println(game);
    }
    out.println(game.getResult());
}


A lot of effort went into transforming your original code into this readable and expressive form. But where, you ask, has all the code gone? Let's go step by step.

Because I have split everything into separate methods that each do one thing only, I've promoted the local variables to private static final fields. There's your familiar Scanner, plus a reference to System.out so I can use the short form out.println(...) and drop the System (I find it more convenient, but go with what you feel most comfortable with).

The methods that are called from main all look pretty much how you expected them:

private static void informPlayersOfNextTurn() {
    String message = "Player %s, it's your turn. Make your move!\n";
    out.printf(message, game.getCurrentPlayer());
}


We simply ask the game for the current player and prompt them to make their move.

Note the high level of these method calls... there's not much of an implementation visible here, just a high level view of how the program flows. We'll keep asking for a move until the user enters a valid one:

private static void makeNextMove() {
    Move move = askForMove();
    while (!game.isMoveAllowed(move)) {
        out.println("Illegal move, try again.");
        move = askForMove();
    }
    game.makeMove(move);
}


Now, we delve a little deeper. Users aren't usually accustomed to the programming convention of counting from zero, so we'll allow them to enter their moves in familiar terms and subtract 1 to transform them to an appropriate format for use with Java.

private static Move askForMove() {
    int x = askForCoordinate("horizontal");
    int y = askForCoordinate("vertical");
    return new Move(x - 1, y - 1);
}

Code Snippets

private static final Scanner in = new Scanner(System.in);
private static final PrintStream out = System.out;
private static final Game game = new Game();

public static void main(String args[]) {
    out.println("Welcome to TicTacToe.");
    out.println(game);
    while (game.isRunning()) {
        informPlayersOfNextTurn();
        makeNextMove();
        out.println(game);
    }
    out.println(game.getResult());
}
private static void informPlayersOfNextTurn() {
    String message = "Player %s, it's your turn. Make your move!\n";
    out.printf(message, game.getCurrentPlayer());
}
private static void makeNextMove() {
    Move move = askForMove();
    while (!game.isMoveAllowed(move)) {
        out.println("Illegal move, try again.");
        move = askForMove();
    }
    game.makeMove(move);
}
private static Move askForMove() {
    int x = askForCoordinate("horizontal");
    int y = askForCoordinate("vertical");
    return new Move(x - 1, y - 1);
}
private static int askForCoordinate(String coordinate) {
    out.printf("Enter the %s coordinate of the cell [1-3]: ", coordinate);
    while (!in.hasNextInt()) {
        out.print("Invalid number, re-enter: ");
        in.next();
    }
    return in.nextInt();
}

Context

StackExchange Code Review Q#15911, answer score: 20

Revisions (0)

No revisions yet.