Cómo hacer en Java un programa para el juego del Gato (Tic-tac-toe)
El juego del Gato o en inglés Tic-tac-toe, es un juego muy popular que seguramente hemos jugado en múltiples ocasiones. Pero ¿cómo hacer un programa en Java que permita que dos usuarios lo jueguen? En esta entrada hablaré sobre la creación de este programa.
El Gato
Para este juego se utiliza un tablero que es en esencia una matriz cuadrada de 3 x 3. Participan dos jugadores, uno de ellos tira con las ‘X’ y otro con las ‘O’. Cada jugador tiene su turno y puede seleccionar tirar en cualquier casilla del tablero que no esté ocupada.

Figura 1. El juego del gato
El juego termina cuando pasan alguna de dos cosas:
- Un jugador ya ganó, lo cual sucede cuando logra que en todas las casillas de una fila, columna o diagonal estén ocupadas con su “marca”, ya sea ‘X’ u ‘O’.(Ver figura 1)
- Ya no existe ninguna casilla disponible en el tablero, pero tampoco ningún jugador ha ganado. (Ver figura2)

Figura 2. Ejemplo de un empate.
El algoritmo
Para crear un programa en Java que permita a dos usuarios humanos jugar el gato, planteo el siguiente algoritmo:
Inicio terminar = false; jugadorActual = ‘X’ Hacer { registrarJugada(jugadorActual); imprimirTablero(); Si ( verificarGanador(jugadorActual) ) // ya hay un ganador, imprimir mensaje de felicitación terminar = true; Sino { Si (! verificarEspacio() ) // ya no hay casillas disponibles, imprimir mensaje porque el juego se ha empatado terminar = true; Sino Si ( jugadorActual == ‘X’ ) jugadorActual = ‘0’; Sino jugadorActual = ‘X’; } } mientras (!terminar); Fin
Como puedes observar, el primer jugador tirará con las ‘X’, y mediante el método registrarJugada registraré su tiro en el tablero. Después de que se haya registrado una jugada, siempre se mandará a imprimir el estado actual del tablero. A continuación llamo al método verificarGanador pasándole como argumento el jugadorActual, que en realidad sólo se trata de un carácter (‘X’ u ‘O’). Este método debe buscar si ya existen la línea de 3 con el símbolo del jugadorActual. Si esto sucede, la bandera terminar se pone igual a true, el ciclo se rompe y el juego termina. Pero si aún no hay ganador, hay que revisar que aún existan casillas disponibles, pues si esto no sucede, el juego también debe terminar, pues se declara el empate. En el caso de que no exista un ganador ni empate, jugadorActual se intercambia para valer ahora ‘O’ y permitir el turno del siguiente jugador. Todo esto se debe realizar mientras terminar sea falso.
En las secciones siguientes, describiré los métodos que son llamados por el programa principal.
El método registrarJugada.
A continuación, te muestro el método registrarJugada:
public static void registrarJugada(char caracter) throws IOException{ boolean salir = false; String entrada; BufferedReader bufer = new BufferedReader(new InputStreamReader(System.in)); int posicion; do{ imprimirPosiciones(); System.out.println("Escriba el número de casilla en donde desea tirar: "); entrada = bufer.readLine(); posicion = Integer.parseInt(entrada); if ( casillaNoOcupada(posicion)){ switch (posicion){ case 1: gato[0][0] = caracter; break; case 2: gato[0][1] = caracter; break; case 3: gato[0][2] = caracter; break; case 4: gato[1][0] = caracter; break; case 5: gato[1][1] = caracter; break; case 6: gato[1][2] = caracter; break; case 7: gato[2][0] = caracter; break; case 8: gato[2][1] = caracter; break; case 9: gato[2][2] = caracter; break; } salir = true; } else System.out.println("Casilla no válida, escriba una posición valida"); } while (!salir); }
Este método:
- Recibe un argumento tipo char, que será la jugada a registrar, y que puede valer ‘X’ u ‘O’.
- Imprime los números de cada casilla del tablero. Pueden existir dos formas de resolver el problema para que los jugadores indiquen la casilla en la que desean tirar:
- Se le pide el número de renglón y el número de columna de la casilla en la que se desea tirar. (Desde el punto de vista del programador, es la forma más sencilla, pero quizá no la más simple de entender por los jugadores)
- Se le asigna un número entero del 1 al 9 a cada casilla en el tablero y se le pide al jugador que escriba el número de casilla, de esta forma ingresará un solo número. Esta solución es sólo un poco más compleja para el programador, pero mucho más sencilla para el jugador, y es la que utilizaré para este programa, y se realiza con el llamado al método imprimirPosiciones().
- Una vez que el jugador ingresa el número de casilla en la que desea tirar, antes de registrarla en el tablero, se llama al método casillaNoOcupada(), al que se le envía la posición deseada, para que revise que esa casilla no esté ocupada con un tiro previo de otro jugador o quizá del mismo.
- Si la casilla no está ocupada, se procede a registrar la jugada con el carácter enviado al tablero, utilizando una estructura switch para que el número de casilla direccione a los índices de renglón y columna correspondiente.
- Todo esto sucede dentro de un ciclo do-while, del cual se sale cuando el jugador indica un número de casilla válida.
En la figura 3 se muestra la corrida de esta sección del programa.

Figura 3. Pantalla de corrida del método registrarJugada().
El método hayGanador().
Éste método es el encargado de determinar si ya existe un ganador. A continuación, puedes observar la codificación de éste método:
public static boolean hayGanador(char caracter){ if ( ganaPorRenglon(caracter) ) return true; if ( ganaPorColumna(caracter)) return true; if ( ganaPorDiagonales(caracter)) return true; return false; }
Recordemos que un jugador puede ganar en una de tres formas: por renglón por columna o por cualquiera de las dos diagonales que tiene el tablero. Es por eso que a su vez llama a tres métodos:
- ganaPorRenglon()
- ganaPorColumna()
- ganaPorDiagonales()
Estos métodos reciben el caracter que buscarán que se encuentre en todas las casillas de un renglón, una columna o una de las dos diagonales. A continuación muestro el código de ganaPorRenglón:
public static boolean ganaPorRenglon(char caracter){ for (char[] gato1 : gato) { if (gato1[0] == caracter && gato1[1] == caracter && gato1[2] == caracter) { return true; } } return false; }
Como puedes observar, es bastante sencillo. Revisa que en cada casilla de un renglón se encuentre el caracter que puede valer ‘X’ u ‘O’. Si eso pasa, regresa verdadero y falso en caso contrario.
El método hayEspacio
Este método sirve para indicarle al programa principal si aún existe una casilla disponible para tirar. El código se muestra a continuación:
public static boolean hayEspacio(){ for (char[] gato1 : gato) { for (int j = 0; j<gato.length; j++) { if (gato1[j] == '-') { return true; } } } return false; }
Básicamente este método regresa verdadero si encuentra una casilla disponible, y falso en caso contrario. El tablero se ha inicializado previamente con el caracter ‘-‘, el cual servirá para indicar una casilla vacía. En cuanto encuentra una casilla con este caracter, detiene el recorrido por las casillas del tablero y regresa verdadero. Pero si recorre cada casilla y no encuentra el caracter ‘-‘, eso indica que ya no hay una casilla disponible para tirar, y como tampoco hay ganador, entonces el juego termina en empate.
Programa completo.
A continuación te muestro el programa completo:
/* * Programa que permite que dos usuarios humanos * jueguen al gato (tic-tac-toe) * Blog: dcodinGames.com */ package proyectogato; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * * @author Gaby Nieva */ public class ProyectoGato { public static char[][] gato = new char[3][3]; public static void imprimirPosiciones(){ int pos = 1; System.out.println("Selecciona una posición en el tablero: "); System.out.println("Las casillas marcadas con X u O ya están ocupadas"); for (int i= 0; i<gato.length; i++){ for (int j=0; j<gato.length; j++){ if (gato[i][j] == 'X' || gato[i][j] == 'O') System.out.print( " " + gato[i][j]); else System.out.print( " " + pos); pos++; } System.out.println(); } } public static boolean casillaNoOcupada(int posicion){ switch (posicion){ case 1: return gato[0][0]!=' '; case 2: return gato[0][1]!=' '; case 3: return gato[0][2]!=' '; case 4: return gato[1][0]!=' '; case 5: return gato[1][1]!=' '; case 6: return gato[1][2]!=' '; case 7: return gato[2][0]!=' '; case 8: return gato[2][1]!=' '; case 9: return gato[2][2]!=' '; default: return false; } } public static void registrarJugada(char caracter) throws IOException{ boolean salir = false; String entrada; BufferedReader bufer = new BufferedReader(new InputStreamReader(System.in)); int posicion; do{ imprimirPosiciones(); System.out.println("Escriba el número de casilla en donde desea tirar: "); entrada = bufer.readLine(); posicion = Integer.parseInt(entrada); if ( casillaNoOcupada(posicion)){ switch (posicion){ case 1: gato[0][0] = caracter; break; case 2: gato[0][1] = caracter; break; case 3: gato[0][2] = caracter; break; case 4: gato[1][0] = caracter; break; case 5: gato[1][1] = caracter; break; case 6: gato[1][2] = caracter; break; case 7: gato[2][0] = caracter; break; case 8: gato[2][1] = caracter; break; case 9: gato[2][2] = caracter; break; } salir = true; } else System.out.println("Casilla no válida, escriba una posición valida"); } while (!salir); } public static void imprimirGato(){ System.out.println("El gato hasta el momento: "); for (char[] gato1 : gato) { for (int j = 0; j<gato.length; j++) { System.out.print(" " + gato1[j]); } System.out.println(); } } public static boolean ganaPorRenglon(char caracter){ for (char[] gato1 : gato) { if (gato1[0] == caracter && gato1[1] == caracter && gato1[2] == caracter) { return true; } } return false; } public static boolean ganaPorColumna(char caracter){ for (int i=0; i<gato.length; i++){ if (gato[0][i] == caracter && gato[1][i]==caracter && gato[2][i] == caracter) return true; } return false; } public static boolean ganaPorDiagonales(char caracter){ // Busca ganador en la columna de izquierda a derecha if (gato[0][0] == caracter && gato[1][1]==caracter && gato[2][2] == caracter) return true; if (gato[0][2] == caracter && gato[1][1]==caracter && gato[2][0] == caracter) return true; return false; } public static boolean hayGanador(char caracter){ if ( ganaPorRenglon(caracter) ) return true; if ( ganaPorColumna(caracter)) return true; if ( ganaPorDiagonales(caracter)) return true; return false; } public static boolean hayEspacio(){ for (char[] gato1 : gato) { for (int j = 0; j<gato.length; j++) { if (gato1[j] == '-') { return true; } } } return false; } public static void inicializarTablero(){ for(int i=0;i<gato.length; i++) for(int j=0;j<gato.length; j++) gato[i][j] = '-'; } /** * @param args the command line arguments */ public static void main(String[] args) throws IOException { // TODO code application logic here char jugadorActual = 'X'; boolean terminar = false; inicializarTablero(); do{ registrarJugada(jugadorActual); imprimirGato(); if ( hayGanador(jugadorActual)){ System.out.println("Felicidades jugador " + jugadorActual); System.out.println("Has ganado el juego"); terminar = true; } else { if (!hayEspacio()){ // Ya no hay casillas disponibles, el juego se empató System.out.println("El juego termina en empate"); terminar = true; } else if (jugadorActual == 'X') jugadorActual = 'O'; else jugadorActual = 'X'; } } while ( !terminar); } }
Comparte el conocimiento.
Si éste artículo te ha parecido útil, te invito a compartirlo en tus redes sociales.
¡Hasta la próxima!