Juego 3 en raya o Tic Tac Toe en Java

Hola a todos, hoy os voy a explicar como podemos crear el juego del 3 en raya en Java.

El juego del 3 en raya o Tic Tac Toe es un juego mas que conocidos por todos, pues vamos a ver como podemos hacerlo en Java con programación orientada a objetos.

Tendremos como atributos una matriz de char donde contendrán X, O o – (vacio), otro atributo para saber cual es el turno actual y contantes para de los valores posibles.


public class TicTacToe {

    //Reprentaciones de los jugadores y del simbolo vacio
    private final char J1 = 'X';
    private final char J2 = 'O';
    private final char VACIO = '-';

    //turno actual
    //true = J1, false = J2
    private boolean turno;

    //tablero donde vamos a jugar
    private char tablero[][];

}

Tenemos que inicializar el tablero de primeras, poniendo todo a vacio:


/**
 * Inicializa el tablero con el simbolo VACIO
 */
private void inicializarTablero() {

    for (int i = 0; i < tablero.length; i++) {
        for (int j = 0; j < tablero.length; j++) {
            tablero[i][j] = VACIO;
        }
    }

}

El constructor seria así:


public TicTacToe() {
    this.turno = true;
    this.tablero = new char[3][3];
    this.inicializarTablero();
}

Necesitamos mostrar el tablero en algún momento:


/**
 * Muestra la matriz
 *
 */
public void mostrarTablero() {

    for (int i = 0; i < this.tablero.length; i++) {
        for (int j = 0; j < this.tablero[0].length; j++) {
            System.out.print(this.tablero[i][j] + " ");
        }
        System.out.println("");
    }

}

Para el tema del turno, debemos saber cual es el actual y como cambiarlo:


/**
 * Mostramos el turno actual
 */
public void mostrarTurnoActual() {

    if (turno) {
        System.out.println("Le toca al jugador 1");
    } else {
        System.out.println("Le toca al jugador 2");
    }

}

/**
 * Cambia el turno
 */
public void cambiaTurno() {
    this.turno = !this.turno;
}

Para insertar un elemento en la matriz, usaremos este método:


/**
 * Insertamos en una posicion de una matriz un simbolo en concreto
 *
 * @param fila
 * @param columna
 */
public void insertarEn(int fila, int columna) {
    if (turno) {
        this.tablero[fila][columna] = J1;
    } else {
        this.tablero[fila][columna] = J2;
    }
}

Para usar el método anterior, recomiendo antes validar la posición que se va a insertar:


/**
 * Validamos la posicion que nos insertan
 *
 * @param fila
 * @param columna
 * @return
 */
public boolean validarPosicion(int fila, int columna) {

    if (fila >= 0 && fila < tablero.length && columna >= 0 && columna < tablero.length) {
        return true;
    }
    return false;

}

/**
 * Indicamos si en una posicion hay una marca
 *
 * @param fila
 * @param columna
 * @return
 */
public boolean hayValorPosicion(int fila, int columna) {
    if (this.tablero[fila][columna] != VACIO) {
        return true;
    }

    return false;
}

Hacemos otro método para saber si el tablero esta lleno o no:


/**
 * Indica si el tablero esta llena cuando el simbolo por defecto aparezca,
 * no esta llena
 *
 * @return talero vacio o no
 */
public boolean tableroLleno() {
    for (int i = 0; i < tablero.length; i++) {
        for (int j = 0; j < tablero[0].length; j++) {
            if (tablero[i][j] == VACIO) {
                return false;
            }
        }
    }
    return true;
}

Para saber si alguno de los jugadores han ganado, debemos comprobar tanto lineas, columnas y diagonales, entonces, lo hacemos cada uno en diferentes métodos.

La idea en general, es coger el primer elemento y ver si el resto de la linea, columna o diagonal coincide con ese elemento. En el caso de que no, devolvemos el elemento vacio (-).

Para comprobar las lineas:


/**
 * Indica si hay un ganador en una linea
 *
 * @return Simbolo del ganador, VACIO sino hay ganador
 */
private char coincidenciaLinea() {

    char simbolo;
    boolean coincidencia;

    for (int i = 0; i < tablero.length; i++) {

        //Reiniciamos la coincidencia
        coincidencia = true;
        //Cogemos el simbolo de la fila
        simbolo = tablero[i][0];
        if (simbolo != VACIO) {
            for (int j = 1; j < tablero[0].length; j++) {
                //sino coincide ya no habra ganadro en esta fila
                if (simbolo != tablero[i][j]) {
                    coincidencia = false;
                }
            }

            //Si no se mete en el if, devuelvo el simbolo ganador
            if (coincidencia) {
                return simbolo;
            }

        }

    }

    //Si no hay ganador, devuelvo el simbolo por defecto
    return VACIO;

}

Para comprobar las columnas:


/**
 * Indica si hay un ganador en una columna
 *
 * @return Simbolo del ganador, VACIO sino hay ganador
 */
private char coincidenciaColumna() {

    char simbolo;
    boolean coincidencia;

    for (int j = 0; j < tablero.length; j++) {

        //Reiniciamos la coincidencia
        coincidencia = true;
        //Cogemos el simbolo de la columna
        simbolo = tablero[0][j];
        if (simbolo != VACIO) {
            for (int i = 1; i < tablero[0].length; i++) {
                //sino coincide ya no habra ganadro en esta fila
                if (simbolo != tablero[i][j]) {
                    coincidencia = false;
                }
            }

            //Si no se mete en el if, devuelvo el simbolo ganador
            if (coincidencia) {
                return simbolo;
            }

        }

    }

    //Si no hay ganador, devuelvo el simbolo por defecto
    return VACIO;

}

Para comprobar las diagonales:


/**
 * Comprueba las diagonales
 *
 * @return Simbolo del ganador, VACIO sino hay ganador
 */
private char coincidenciaDiagonal() {

    char simbolo;
    boolean coincidencia = true;

    //Diagonal principal
    simbolo = tablero[0][0];
    if (simbolo != VACIO) {
        for (int i = 1; i < tablero.length; i++) {
            //sino coincide ya no habra ganadro en esta fila
            if (simbolo != tablero[i][i]) {
                coincidencia = false;
            }
        }

        //Si no se mete en el if, devuelvo el simbolo ganador
        if (coincidencia) {
            return simbolo;
        }

    }

    coincidencia = true;

    //Diagonal inversa
    simbolo = tablero[0][2];
    if (simbolo != VACIO) {
        for (int i = 1, j = 1; i < tablero.length; i++, j--) {
            //sino coincide ya no habra ganadro en esta fila
            if (simbolo != tablero[i][j]) {
                coincidencia = false;
            }
        }

        //Si no se mete en el if, devuelvo el simbolo ganador
        if (coincidencia) {
            return simbolo;
        }
    }

    //Si no hay ganador, devuelvo el simbolo por defecto
    return VACIO;

}

Ya teniendo estos métodos, podemos saber cuando se acaba la partida:


/**
 * Indica si es el fin de la partida, acaba cuando hay un ganador o el tablero esta lleno
 * @return fin de partida
 */
public boolean finPartida() {

    if (tableroLleno()
            || coincidenciaLinea() != VACIO
            || coincidenciaColumna() != VACIO
            || coincidenciaDiagonal() != VACIO) {
        return true;
    }

    return false;
}

Cuando se acabe el juego, tendremos que mostrar el ganador:



/**
 * Muestra el ganador de la partida
 */
public void mostrarGanador() {

    char simbolo = coincidenciaLinea();

    if (simbolo != VACIO) {

        ganador(simbolo, 1);

        return;

    }

    simbolo = coincidenciaColumna();

    if (simbolo != VACIO) {

        ganador(simbolo, 2);

        return;

    }

    simbolo = coincidenciaDiagonal();

    if (simbolo != VACIO) {

        ganador(simbolo, 3);

        return;

    }

    System.out.println("Hay empate");

}

/**
 * Funcion auxiliar de la anterior funcion
 *
 * @param simbolo
 * @param tipo
 */
private void ganador(char simbolo, int tipo) {

    switch (tipo) {
        case 1:
            if (simbolo == J1) {
                System.out.println("Ha ganado el Jugador 1 por linea");
            } else {
                System.out.println("Ha ganado el Jugador 2 por linea");
            }

            break;
        case 2:
            if (simbolo == J1) {
                System.out.println("Ha ganado el Jugador 1 por columna");
            } else {
                System.out.println("Ha ganado el Jugador 2 por columna");
            }
            break;
        case 3:
            if (simbolo == J1) {
                System.out.println("Ha ganado el Jugador 1 por diagonal");
            } else {
                System.out.println("Ha ganado el Jugador 2 por diagonal");
            }
            break;
    }

}

Os dejo la clase completa:

public class TicTacToe {

    //Reprentaciones de los jugadores y del simbolo vacio
    private final char J1 = 'X';
    private final char J2 = 'O';
    private final char VACIO = '-';

    //turno actual
    //true = J1, false = J2
    private boolean turno;

    //tablero donde vamos a jugar
    private char tablero[][];

    public TicTacToe() {
        this.turno = true;
        this.tablero = new char[3][3];
        this.inicializarTablero();
    }

    /**
     * Inicializa el tablero con el simbolo VACIO
     */
    private void inicializarTablero() {

        for (int i = 0; i < tablero.length; i++) {
            for (int j = 0; j < tablero.length; j++) {
                tablero[i][j] = VACIO;
            }
        }

    }

    /**
     * Indica si es el fin de la partida, acaba cuando hay un ganador o el
     * tablero esta lleno
     *
     * @return fin de partida
     */
    public boolean finPartida() {

        if (tableroLleno()
                || coincidenciaLinea() != VACIO
                || coincidenciaColumna() != VACIO
                || coincidenciaDiagonal() != VACIO) {
            return true;
        }

        return false;
    }

    /**
     * Indica si el tablero esta llena cuando el simbolo por defecto aparezca,
     * no esta llena
     *
     * @return talero vacio o no
     */
    public boolean tableroLleno() {
        for (int i = 0; i < tablero.length; i++) {
            for (int j = 0; j < tablero[0].length; j++) {
                if (tablero[i][j] == VACIO) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Indica si hay un ganador en una linea
     *
     * @return Simbolo del ganador, VACIO sino hay ganador
     */
    private char coincidenciaLinea() {

        char simbolo;
        boolean coincidencia;

        for (int i = 0; i < tablero.length; i++) {

            //Reiniciamos la coincidencia
            coincidencia = true;
            //Cogemos el simbolo de la fila
            simbolo = tablero[i][0];
            if (simbolo != VACIO) {
                for (int j = 1; j < tablero[0].length; j++) {
                    //sino coincide ya no habra ganadro en esta fila
                    if (simbolo != tablero[i][j]) {
                        coincidencia = false;
                    }
                }

                //Si no se mete en el if, devuelvo el simbolo ganador
                if (coincidencia) {
                    return simbolo;
                }

            }

        }

        //Si no hay ganador, devuelvo el simbolo por defecto
        return VACIO;

    }

    /**
     * Indica si hay un ganador en una columna
     *
     * @return Simbolo del ganador, VACIO sino hay ganador
     */
    private char coincidenciaColumna() {

        char simbolo;
        boolean coincidencia;

        for (int j = 0; j < tablero.length; j++) {

            //Reiniciamos la coincidencia
            coincidencia = true;
            //Cogemos el simbolo de la columna
            simbolo = tablero[0][j];
            if (simbolo != VACIO) {
                for (int i = 1; i < tablero[0].length; i++) {
                    //sino coincide ya no habra ganadro en esta fila
                    if (simbolo != tablero[i][j]) {
                        coincidencia = false;
                    }
                }

                //Si no se mete en el if, devuelvo el simbolo ganador
                if (coincidencia) {
                    return simbolo;
                }

            }

        }

        //Si no hay ganador, devuelvo el simbolo por defecto
        return VACIO;

    }

    /**
     * Comprueba las diagonales
     *
     * @return Simbolo del ganador, VACIO sino hay ganador
     */
    private char coincidenciaDiagonal() {

        char simbolo;
        boolean coincidencia = true;

        //Diagonal principal
        simbolo = tablero[0][0];
        if (simbolo != VACIO) {
            for (int i = 1; i < tablero.length; i++) {
                //sino coincide ya no habra ganadro en esta fila
                if (simbolo != tablero[i][i]) {
                    coincidencia = false;
                }
            }

            //Si no se mete en el if, devuelvo el simbolo ganador
            if (coincidencia) {
                return simbolo;
            }

        }

        coincidencia = true;

        //Diagonal inversa
        simbolo = tablero[0][2];
        if (simbolo != VACIO) {
            for (int i = 1, j = 1; i < tablero.length; i++, j--) {
                //sino coincide ya no habra ganadro en esta fila
                if (simbolo != tablero[i][j]) {
                    coincidencia = false;
                }
            }

            //Si no se mete en el if, devuelvo el simbolo ganador
            if (coincidencia) {
                return simbolo;
            }
        }

        //Si no hay ganador, devuelvo el simbolo por defecto
        return VACIO;

    }

    /**
     * Muestra el ganador de la partida
     */
    public void mostrarGanador() {

        char simbolo = coincidenciaLinea();

        if (simbolo != VACIO) {

            ganador(simbolo, 1);

            return;

        }

        simbolo = coincidenciaColumna();

        if (simbolo != VACIO) {

            ganador(simbolo, 2);

            return;

        }

        simbolo = coincidenciaDiagonal();

        if (simbolo != VACIO) {

            ganador(simbolo, 3);

            return;

        }

        System.out.println("Hay empate");

    }

    /**
     * Funcion auxiliar de la anterior funcion
     *
     * @param simbolo
     * @param tipo
     */
    private void ganador(char simbolo, int tipo) {

        switch (tipo) {
            case 1:
                if (simbolo == J1) {
                    System.out.println("Ha ganado el Jugador 1 por linea");
                } else {
                    System.out.println("Ha ganado el Jugador 2 por linea");
                }

                break;
            case 2:
                if (simbolo == J1) {
                    System.out.println("Ha ganado el Jugador 1 por columna");
                } else {
                    System.out.println("Ha ganado el Jugador 2 por columna");
                }
                break;
            case 3:
                if (simbolo == J1) {
                    System.out.println("Ha ganado el Jugador 1 por diagonal");
                } else {
                    System.out.println("Ha ganado el Jugador 2 por diagonal");
                }
                break;
        }

    }

    /**
     * Insertamos en una posicion de una matriz un simbolo en concreto
     *
     * @param fila
     * @param columna
     */
    public void insertarEn(int fila, int columna) {
        if (turno) {
            this.tablero[fila][columna] = J1;
        } else {
            this.tablero[fila][columna] = J2;
        }
    }

    /**
     * Muestra la matriz
     *
     */
    public void mostrarTablero() {

        for (int i = 0; i < this.tablero.length; i++) {
            for (int j = 0; j < this.tablero[0].length; j++) {
                System.out.print(this.tablero[i][j] + " ");
            }
            System.out.println("");
        }

    }

    /**
     * Mostramos el turno actual
     */
    public void mostrarTurnoActual() {

        if (turno) {
            System.out.println("Le toca al jugador 1");
        } else {
            System.out.println("Le toca al jugador 2");
        }

    }

    /**
     * Cambia el turno
     */
    public void cambiaTurno() {
        this.turno = !this.turno;
    }

    /**
     * Validamos la posicion que nos insertan
     *
     * @param fila
     * @param columna
     * @return
     */
    public boolean validarPosicion(int fila, int columna) {

        if (fila >= 0 && fila < tablero.length && columna >= 0 && columna < tablero.length) {
            return true;
        }
        return false;

    }

    /**
     * Indicamos si en una posicion hay una marca
     *
     * @param fila
     * @param columna
     * @return
     */
    public boolean hayValorPosicion(int fila, int columna) {
        if (this.tablero[fila][columna] != VACIO) {
            return true;
        }

        return false;
    }

}



También os dejo un pequeño ejemplo:


import java.util.Scanner;

public class Ejercicio_POO_DDR_23 {

    static Scanner teclado = new Scanner(System.in);

    public static void main(String[] args) {

        TicTacToe ttt = new TicTacToe();

        int fila, columna;
        boolean posValida, correcto;

        //No salimos hasta que uno gane o no haya mas posibilidades
        while (!ttt.finPartida()) {

            do {

                //mostramos el jugador al que le toca
                ttt.mostrarTurnoActual();

                //muestro el tablero
                ttt.mostrarTablero();

                correcto = false;
                fila = pedirInteger("Dame la fila");
                columna = pedirInteger("Dame la columna");

                //Validamos la posicion
                posValida = ttt.validarPosicion(fila, columna);

                //Si es valido, comprobamos que no haya ninguna marca
                if (posValida) {

                    //Si no hay marca, significa que es correcto
                    if (!ttt.hayValorPosicion(fila, columna)) {
                        correcto = true;
                    } else {
                        System.out.println("Ya hay una marca en esa posicion");
                    }
                } else {
                    System.out.println("La posicion no es valida");
                }

                //Mientras no sea correcto, no salgo
            } while (!correcto);

            //depende del turno, inserta un simbolo u otro
            ttt.insertarEn(fila, columna);

            ttt.cambiaTurno();
        }

        //Muestra el tablero
        ttt.mostrarTablero();

        //Mostramos el ganador
        ttt.mostrarGanador();

    }

    /**
     * Pedimos un numero y lo devolvemos
     *
     * @param mensaje
     * @return
     */
    public static int pedirInteger(String mensaje) {

        System.out.println(mensaje);
        int numero = teclado.nextInt();

        return numero;

    }

}

También te dejo un par de videos:

Espero que os sea de ayuda. Si tenéis dudas, preguntad. Estamos para ayudarte.

Compartir

1 comentario

  1. Ramon

    Buenas, no se me coloca la matriz en consola, se imprime de 3 en 3 hacia abajo, sabes que puede suceder o que puedo hacer para que esto no ocurra?

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *