ark ark - 6 months ago 12
Java Question

unsure what if-statements i need to have for when a player is forced to go to a won box

so when a player is forced to go to a box that has been won, i want to give them the ability to go to any box not yet won, but i was wonder if there was an easier way than just doing nine different if-statements every time i need that to be checked. right now this is what happens: http://imgur.com/kD1UkVH and this is the code for the rules:

if(source.equals(a1) || source.equals(b1) || source.equals(c1) ||
source.equals(d1) || source.equals(e1) || source.equals(f1) ||
source.equals(g1) || source.equals(h1) || source.equals(i1))
{
b1.setEnabled(false);
b2.setEnabled(false);
b3.setEnabled(false);
b4.setEnabled(false);
b5.setEnabled(false);
b6.setEnabled(false);
b7.setEnabled(false);
b8.setEnabled(false);
b9.setEnabled(false);
c1.setEnabled(false);
c2.setEnabled(false);
c3.setEnabled(false);
c4.setEnabled(false);
c5.setEnabled(false);
c6.setEnabled(false);
c7.setEnabled(false);
c8.setEnabled(false);
c9.setEnabled(false);
d1.setEnabled(false);
d2.setEnabled(false);
d3.setEnabled(false);
d4.setEnabled(false);
d5.setEnabled(false);
d6.setEnabled(false);
d7.setEnabled(false);
d8.setEnabled(false);
d9.setEnabled(false);
e1.setEnabled(false);
e2.setEnabled(false);
e3.setEnabled(false);
e4.setEnabled(false);
e5.setEnabled(false);
e6.setEnabled(false);
e7.setEnabled(false);
e8.setEnabled(false);
e9.setEnabled(false);
f1.setEnabled(false);
f2.setEnabled(false);
f3.setEnabled(false);
f4.setEnabled(false);
f5.setEnabled(false);
f6.setEnabled(false);
f7.setEnabled(false);
f8.setEnabled(false);
f9.setEnabled(false);
g1.setEnabled(false);
g2.setEnabled(false);
g3.setEnabled(false);
g4.setEnabled(false);
g5.setEnabled(false);
g6.setEnabled(false);
g7.setEnabled(false);
g8.setEnabled(false);
g9.setEnabled(false);
h1.setEnabled(false);
h2.setEnabled(false);
h3.setEnabled(false);
h4.setEnabled(false);
h5.setEnabled(false);
h6.setEnabled(false);
h7.setEnabled(false);
h8.setEnabled(false);
h9.setEnabled(false);
i1.setEnabled(false);
i2.setEnabled(false);
i3.setEnabled(false);
i4.setEnabled(false);
i5.setEnabled(false);
i6.setEnabled(false);
i7.setEnabled(false);
i8.setEnabled(false);
i9.setEnabled(false);
}


nine times, one for each quadrant, and this is what happens when a button is pushed:

if("disable".equals(actionEvent.getActionCommand()))

{
if(PlayerOneTurn % 2 != 0)
{
if(source.equals(a1))
{
if(input.equals("X") || (input.equals("x")))
{
a1.setText("x");
a1.setActionCommand(" ");
}
else
{
a1.setText("o");
a1.setActionCommand(" ");
}
}
if(source.equals(a2))
{
if(input.equals("X") || (input.equals("x")))
{
a2.setText("x");
a2.setActionCommand(" ");
}
else
{
a2.setText("o");
a2.setActionCommand(" ");
}
}
if(source.equals(a3))
{
if(input.equals("X") || (input.equals("x")))
{
a3.setText("x");
a3.setActionCommand(" ");
}
else
{
a3.setText("o");
a3.setActionCommand(" ");
}
}
if((a1.getText().equals("x") && a2.getText().equals("x") && a3.getText().equals("x"))
|| (a1.getText().equals("o") && a2.getText().equals("o") && a3.getText().equals("o")))
{
AFalse(true);
}`


the AFalse() method is this:

public void AFalse(boolean PlayerOne){
if(PlayerOne == true)
{
a1.setActionCommand(" ");
a2.setActionCommand(" ");
a3.setActionCommand(" ");
a4.setActionCommand(" ");
a5.setActionCommand(" ");
a6.setActionCommand(" ");
a7.setActionCommand(" ");
a8.setActionCommand(" ");
a9.setActionCommand(" ");
winnerA = true;
}
else
{
a1.setActionCommand(" ");
a2.setActionCommand(" ");
a3.setActionCommand(" ");
a4.setActionCommand(" ");
a5.setActionCommand(" ");
a6.setActionCommand(" ");
a7.setActionCommand(" ");
a8.setActionCommand(" ");
a9.setActionCommand(" ");
winnerA = false;
}
}

Answer

I would suggest you to create some classes like the classes below.

This class represents single field. Here you are able to store moves, reset them easier and iterate over them.

/**
 * This class represents a single tic tac toe field.
 */
class Field {

    private static final short FIELD_DIMENSION = 3;

    public static final char EMPTY_FIELD = ' ';
    public static final char PLAYER_ONE = 'x';
    public static final char PLAYER_TOW = 'o';

    private final char[][] cells;

    public Field() {
        this.cells = new char[FIELD_DIMENSION][FIELD_DIMENSION];

        this.init();
    }

    /**
     * Tests a field by given coordinates if a player is present.
     *
     * @param row Given row index of target field
     * @param column Given column index of target field
     * @return True if the move is possible
     */
    public boolean isCellEmpty(final short row, final short column) {

        return Objects.equals(this.cells[row][column], EMPTY_FIELD);
    }

    /**
     * Returns the cell value of a field.
     *
     * @param row Given row index of target field
     * @param column Given column index of target field
     * @return Cell value
     */
    public char getField(final short row, final short column) {
        return this.cells[row][column];
    }

    private void init() {
        // Iterate over all rows
        for (short rowIndex = 0; rowIndex < FIELD_DIMENSION; ++rowIndex) {
            // Iterate over the columns and set the default (or start) value of field
            for (short columnIndex = 0; columnIndex < FIELD_DIMENSION; ++columnIndex) {
                this.cells[rowIndex][columnIndex] = EMPTY_FIELD;
            }
        }
    }
}

I think you want more than one field so you should use a "super field" class which contains all the sub fields you need.

/**
 * This class represents the super field which contains all single field of the traditional tic tac toe.
 */
class SuperField {

    private static final short SUPER_FIELD_DIMENSION = 3;

    private final Field[][] fields;

    public SuperField() {
        this.fields = new Field[SUPER_FIELD_DIMENSION][SUPER_FIELD_DIMENSION];

        this.init();
    }

    public Field getField(final short field) {
        return this.fields[field / 3][field % 3];
    }

    public void reset() {
        this.init();
    }

    private void init() {
        // Iterate over all rows
        for (short rowIndex = 0; rowIndex < SUPER_FIELD_DIMENSION; ++rowIndex) {
            // Iterate over the columns and set the default field
            for (short columnIndex = 0; columnIndex < SUPER_FIELD_DIMENSION; ++columnIndex) {
                this.fields[rowIndex][columnIndex] = new Field();
            }
        }
    }
}

One the view side the buttons should have an id like "0-0-0", "0-0-1",...,"8-2-2". This id could be parsed in the indeces you need to access the single cells.

public short[] getIds(String buttonId) {
    String[] idParts = anyId.split("-");
    return {Short.parseShort(idParts[0]), Short.parseShort(idParts[1]) ,Short.parseShort(idParts[2])};
}

short fieldIndex = getIds("1-1-1")[0];
short rowIndex = getIds("1-1-1")[1];
short columnIndex = getIds("1-1-1")[2];

The logic of your game should be in a separate class. This makes it easier to find mistakes.

EDIT: This is now additional

The game logic class should be a singleton. An instance of it could be used in all View classes. So you could pass the buttons in it and work with them.

public class GameLogic {

    private static final GameLogic INSTANCE = new GameLogic();

    private SuperField superField;

    private GameLogic() {
        this.superField = new SuperField();
    }

    public static GameLogic getGameLogic() {
        return GameLogic.INSTANCE;
    }

    public SuperField getGameField(){
        return this.superField;
    }
}

The usage looks like this:

GameLogic gameLogic = GameLogic.getGameLogic();
SuperField superField = gameLogic.getGameField();

If you write a method which takes a Button object as parameter the parameter should be final. This will pass the origin Button object into the method and you could change the button in it.

The GameLogic class could now be extended with methods like "isMovePossible" or "isGameFinished", ...