Frixoe
Frixoe

Reputation: 85

Method Not Executing When Button is Clicked [JavaFX]

I am working on my first JavaFX application, which is a Tic Tac Toe game. I have gotten everything to work except the buttons. The Buttons upon clicking, should invoke the checkTurn() method and the checkState() method. The checkTurn() method, displays either an X or an O, depending on if it is X's turn or not. The checkState() method, check's if any of the players have won or if it is a draw, upon which, a new stage is displayed.

However, the checkState() method just won't work. I was wondering if it was unable to instantiate the new stage so, for debugging purposes, I replaced the instantiation with a print statement, which didn't work either. I came to the conclusion that the method isn't even being called.

Searching for a fix I stumbled upon some other Tic Tac Toe programs, only to find that my approach wasn't wrong in any way. I just can't seem to figure out why the checkState() method isn't being called.

Here's my code(arranged by class name):

Note: All the classes except Main.java are in a folder called "model", and I have put a big comment showing you where the "checkState()" method is, in Tiles.java.

Main.java:

package com.frixoe;

import com.frixoe.model.Window;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
    launch(args);
    }

    @Override
    public void start(Stage window) throws Exception {
        Window gameScreen = new Window();

        window.setScene(new Scene(gameScreen));
        window.setTitle("Tic Tac Toe");
        window.setResizable(false);
        window.show();
    }
}

Window.java:

package com.frixoe.model;

import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;

public class Window extends GridPane {
    private final int HEIGHT = 600;
    private final int WIDTH = 600;
    private Tiles tiles;

    public Window() {
        setPrefSize(WIDTH, HEIGHT);
        setAlignment(Pos.CENTER);

        this.tiles = new Tiles();

        placeTiles();
    }

    private void placeTiles() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                Button tile = this.tiles.getTiles()[i][j];
                setConstraints(tile, j, i);
                getChildren().add(tile);
            }
        }
    }
}

Tiles.java:

package com.frixoe.model;

import javafx.application.Platform;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;

public class Tiles extends StackPane {
    private boolean isTurnX = true;
    private Button[][] tiles = new Button[3][3];
    private Font helvetica = new Font("Helvetica", 70);

    public Button[][] getTiles() {
        generateTiles();
        return this.tiles;
    }

    private void generateTiles() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                Button newBtn = new Button("");
                newBtn.setFont(helvetica);
                newBtn.setPrefSize(600 / 3, 600 / 3);
                newBtn.setOnAction(event -> {
                    checkTurn(newBtn);
                    checkState(); // <================================THIS METHOD
                });
                this.tiles[i][j] = newBtn;
            }
        }
    }

    private void checkState() {
        if (hasWon("X")) {
            new AlertBox("Yay! Player X has Won!", "X Won!");
        } else if (hasWon("O")) {
            new AlertBox("Yay! Player O has Won!", "O Won!");
        } else if (isFull()) {
            new AlertBox("It's a draw!", "Draw!");
        }
    }

    private boolean isFull() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (this.tiles[i][j].getText().equalsIgnoreCase("")) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean hasWon(String player) {
        for (int i = 0; i < 3; i++) {
            if (tiles[i][0].getText().equalsIgnoreCase(player)
                    && tiles[i][1].getText().equalsIgnoreCase(player)
                    && tiles[i][2].getText().equalsIgnoreCase(player)) {
                return true;
            }
        }

        for (int i = 0; i < 3; i++) {
            if (tiles[0][i].getText().equalsIgnoreCase(player)
                    && tiles[1][i].getText().equalsIgnoreCase(player)
                    && tiles[2][i].getText().equalsIgnoreCase(player)) {
                return true;
            }
        }

        return tiles[0][0].getText().equalsIgnoreCase(player)
                && tiles[1][1].getText().equalsIgnoreCase(player)
                && tiles[2][2].getText().equalsIgnoreCase(player);
    }

    private void checkTurn(Button btn) {
        if (isTurnX) {
            drawO(btn);

            isTurnX = false;
        } else {
            drawX(btn);

            isTurnX = true;
        }
    }

    private void drawX(Button btn) {
        btn.setText("X");
    }

    private void drawO(Button btn) {
        btn.setText("O");
    }
}

Another Note: AlertBox class is the other stage which would show up when a player wins or when it's a draw.

Thank you for the help!

JDK: jdk1.8.0_121

System: Windows 10 32-bit.

IDE: IntelliJ IDEA 2016.3.3

Upvotes: 1

Views: 610

Answers (1)

Itai
Itai

Reputation: 6911

Note that you are re-generating your tiles with every call to getTiles, so you have 9 copies of each button. This means that while each button can set it's own text, they all "see" only the last set of nine buttons (created by the last call to getTiles).

Make sure you only create your tiles once - either by generating them in the constructor of Tiles, or by checking if they were already initialized before calling generateTiles in getTiles.

Upvotes: 3

Related Questions