Diseñar un proyecto basado en contratos inteligentes requiere mucho más que saber programar: implica evaluar si la idea es viable, si el almacenamiento será eficiente y si la información sensible estará protegida. En esta lección se plantea el proyecto que servirá de hilo conductor durante todo el curso: un juego de Tic Tac Toe (tres en línea) desarrollado con Solidity, donde se analizan los criterios para decidir si un requerimiento merece vivir en la Blockchain y se construye la estructura base del contrato.
¿Qué criterios definen si un proyecto es apto para contratos inteligentes?
Antes de escribir una sola línea de código, es fundamental hacerse preguntas sobre la viabilidad del proyecto. No toda idea se beneficia de la descentralización, y estos puntos ayudan a filtrar propuestas [0:36].
¿La solución es realmente simple?
Si el requerimiento se traduce en muchísimas líneas de código, referencias cruzadas y una solución poco prolija, probablemente no sea la mejor idea implementarlo como contrato inteligente. Cuando se trabajó con el ejemplo del contador en clases anteriores, el requerimiento se trasladó al contrato y quedó una solución supersimple. Esa es la clave: si al bajar el requerimiento a un smart contract la solución no resulta clara, conviene buscar otra tecnología [1:07].
¿El almacenamiento será costoso?
Todo lo que se almacena en un contrato inteligente tiene un costo asociado en gas. Si se guarda información innecesaria, el costo de despliegue y de cada interacción del usuario aumenta significativamente. Si a un usuario le resulta caro interactuar con el proyecto, simplemente no lo usará [1:52]. La regla es almacenar únicamente lo que sea crítico y que realmente justifique estar gestionado en un ambiente descentralizado. Guardar, por ejemplo, el sabor favorito de helado de un usuario dentro de un smart contract no tiene sentido cuando existen alternativas de almacenamiento sin ese costo [2:28].
¿Se expone información sensible?
La Blockchain es transparente por naturaleza. Aunque se utilicen variables privadas, esa información puede terminar siendo accesible mediante comandos y scripts del entorno de desarrollo [2:53]. Si un proyecto necesita privacidad total, como un juego de piedra, papel o tijeras donde ver la jugada del oponente arruina la mecánica, el contrato inteligente no es la mejor opción.
¿Cómo se estructura el contrato del juego Tic Tac Toe?
Con los criterios claros, el juego propuesto permite múltiples partidas simultáneas donde distintos jugadores se enfrentan. Solo las direcciones asociadas a una partida pueden realizar movimientos en ella [3:47].
La estructura base del contrato parte de lo que se vio en clases anteriores: licencia, rango de versiones y nombre descriptivo del contrato (TicTacToe). Se reservan apartados para variables, constructor, funciones y modificadores [4:19].
¿Por qué usar un struct para representar la partida?
Si se manejaran las partidas con variables simples, habría que repetirlas constantemente. Un struct agrupa todos los datos de una partida en una sola estructura reutilizable [4:47]. La partida contiene:
- Jugador uno y jugador dos: representados como direcciones (address).
- Ganador: una dirección que almacena quién ganó la partida.
- Jugadas: una matriz de enteros sin signo (
uint) de 4x4 donde se registra cada movimiento [5:30].
La decisión de usar una matriz 4x4 en lugar de 3x3 permite utilizar directamente los índices 1, 2 y 3 tanto en horizontal como en vertical, evitando la conversión donde el índice cero representa la primera fila [6:14]. El valor 1 representa al jugador uno y el valor 2 al jugador dos.
Para almacenar las partidas se utiliza un array dinámico, ya que no se conoce de antemano cuántas partidas existirán. Cada nueva partida agrega un elemento a este array [7:00].
¿Qué funciones necesita el contrato para funcionar?
Se definen dos funciones públicas fundamentales [7:50]:
- Crear partida: recibe las direcciones de los dos jugadores y retorna un
uint con el identificador de la partida recién creada.
- Jugar: recibe el identificador de la partida, la casilla horizontal y la casilla vertical. No necesita recibir la dirección del jugador porque se obtiene directamente del
msg.sender de la transacción [8:36].
Dentro de la función jugar, la lógica sigue un orden preciso [9:17]:
- Validaciones: verificar que quien llama sea un jugador de la partida, que la casilla no tenga un movimiento previo, que las coordenadas estén dentro de la grilla y que la partida siga activa.
- Guardar la jugada: registrar el movimiento en la matriz.
- Chequear resultado: determinar si hay un ganador o si la grilla está completa.
Estas validaciones pueden implementarse con modificadores o con funciones privadas, y el chequeo del ganador también se resuelve con una función privada [10:16].
Planificar el código como un boceto antes de escribirlo ayuda a detectar lo que falta y a mantener el orden. ¿Te animás a completar el código del juego antes de ver la solución? Dejá en los comentarios cómo te fue con el desafío.