Contenido del curso
Desarrolla el contrato inteligente de un juego
- 4

Diseño de contrato Tic Tac Toe en Solidity
17:36 min - 5

Programación de un contrato inteligente para tic-tac-toe en Solidity
18:57 min - 6

Guardando movimientos y detectando ganador en Solidity
Viendo ahora - 7

Implementación de NFTs en Contratos de Tic-Tac-Toe con Solidity
18:58 min - 8

Emitir tokens ERC-20 al ganar partidas
07:24 min
Ampliado las capacidades del contrato
Revisando la seguridad del contrato
Continúa desarrollando contratos inteligentes
Guardando movimientos y detectando ganador en Solidity
Resumen
Cerrar la lógica de un juego de tic tac toe en Solidity implica algo más que escribir funciones: requiere decidir cuándo acceder al almacenamiento, cuándo trabajar en memoria y cómo organizar las validaciones para que el contrato sea claro y económico en gas. Acá vas a ver cómo guardar el movimiento del jugador, detectar al ganador y persistir el resultado en el contrato.
Por qué conviene usar funciones pure en Solidity
Cuando el compilador te muestra un warning en una función, casi siempre te está dando una pista de optimización. En el caso de partidaTerminada, la función no modifica el contrato ni lee del almacenamiento porque recibe la partida por memoria.
Eso permite reducir el scope a tipo pure. Para vos como usuario el cambio es invisible, pero para la red marca una diferencia: la función se vuelve más liviana y barata en gas porque no necesita referenciar el storage [01:30].
¿Cuándo uso view y cuándo pure en Solidity? Usá
viewsi tu función solo lee del almacenamiento. Usápuresi ni siquiera lee, porque trabaja con parámetros que ya están en memoria.
La regla práctica es revisar siempre las restricciones de cada función antes de cerrar el contrato.
Cómo guardar el movimiento de un jugador en el contrato
La función guardarMovimiento necesita acceder al almacenamiento porque vamos a escribir sobre la partida. Por eso recibe tres parámetros: el ID de la partida, las coordenadas horizontal y vertical de la casilla, y la información para distinguir si juega el jugador uno o el jugador dos [03:45].
Una decisión importante de diseño: la validación de si la casilla ya está ocupada no va dentro de guardarMovimiento, va en el bloque de validaciones previas. ¿La razón? Cuanto antes detectes que una validación falla, más pasos te ahorrás y más limpia queda la función de guardado.
La lógica de validación recorre el array de jugadas y revisa si la posición [H][V] tiene valor cero. Si tiene cero, está vacía y se puede jugar. Si tiene cualquier otro valor, lanza una excepción.
Cómo escribir la jugada en el almacenamiento
Una vez que la validación pasó, guardarMovimiento queda dedicada exclusivamente a escribir. Accedés directo a partidas[idPartida].jugadas[H][V] y le asignás 1 si es el jugador uno o 2 si es el jugador dos.
No se trae la partida a memoria porque sería para una sola lectura, lo cual no aporta. Sobre nombres de variables: usar H y V está bien si el contexto es claro, pero si dudás, escribí horizontal y vertical completos. Escatimar caracteres no vale la pena si rompe la claridad del contrato.
Cómo detectar al ganador en un tic tac toe sobre Solidity
Detectar al ganador requiere chequear las ocho líneas posibles del tic tac toe: dos diagonales, tres filas y tres columnas. En las tres casillas de cada línea el valor tiene que ser igual y distinto de cero.
La función obtenerGanador recibe la partida en memoria y devuelve un entero:
0si todavía no hay ganador y el juego sigue.1si ganó el jugador uno.2si ganó el jugador dos.
En vez de repetir el código de comparación ocho veces, conviene mecanizarlo en una función auxiliar chequearLinea, que recibe el array de jugadas más las seis coordenadas (X1, Y1, X2, Y2, X3, Y3) de las tres casillas a comparar [10:20].
Cómo funciona la comprobación por transitividad
Dentro de chequearLinea la lógica es simple: si la jugada en [X1][Y1] es igual a la jugada en [X2][Y2], y la jugada en [X2][Y2] es igual a la jugada en [X3][Y3], entonces por transitividad las tres son iguales.
Si hay coincidencia, retornás el valor almacenado en cualquiera de las tres casillas (porque las tres son iguales). Si no hay coincidencia, retornás cero.
¿Por qué compilar seguido en Solidity? Porque errores chicos como escribir
returnen vez dereturns, un paréntesis de más o uno faltante se detectan al instante. Compilar de forma frecuente evita que se acumulen varios bugs juntos.
Un detalle del compilador: cuando pasás un array como parámetro, también tenés que indicarle explícitamente que va por memory, igual que con los structs.
Cómo guardar al ganador dentro de la partida
La última pieza es persistir al ganador en el almacenamiento. La función guardarGanador recibe dos parámetros: un entero con el ganador (0, 1 o 2) y el ID de la partida [14:50].
Acá sí necesitás acceder al storage directamente, no a la copia en memoria, porque vas a modificar el dato del contrato. La lógica sigue tres pasos:
- Si el ganador es cero, no hacés nada (no hubo ganador todavía).
- Si el ganador es uno, asignás
partidas[idPartida].ganador = jugador1. - En cualquier otro caso, asignás al jugador dos, porque la lógica garantiza que solo existen esas dos posibilidades.
Por qué separar funciones públicas y privadas en el contrato
Una convención que ayuda a mantener el código prolijo: las funciones públicas van en la parte superior del contrato y las privadas más abajo. Las públicas suelen ser más sensibles porque cualquiera puede llamarlas, así que conviene revisarlas primero. Las privadas, asociadas a lógica interna, quedan al final.
Esta estructura no es exclusiva de Solidity, pero acá impacta directo en la legibilidad y en la auditoría del contrato. ¿Vos cómo organizás tus contratos? Dejanos en los comentarios cómo te fue al implementar la lógica de ganador.