«Replicube» comentado: 18 puzles resueltos dibujando sólidos de vóxeles con código Lua
ゲーミングボーイTraducido del 日本語
«Replicube» es un juego de puzles por niveles en el que hay que reproducir un objeto 3D dado. Los vóxeles no se colocan a mano, sino escribiendo un programa. En este vídeo, quien comenta la partida resuelve los 11 problemas del primer conjunto, el tutorial, y los 7 del conjunto adicional que llega a continuación. No pretende completar todos los problemas, sino detenerse al terminar los dos conjuntos. A lo largo de toda la partida, da prioridad a escribir código legible antes que a apurar la puntuación.
Cómo funciona el juego: se recibe una coordenada y se devuelve un color
Según el tutorial, el juego consiste en colocar vóxeles de 16 colores en un espacio de vóxeles al estilo de Minecraft. «Vóxel» es un término acuñado que significa «píxel con volumen». La colocación se hace con código: si se escribe un código que siempre devuelve blanco, todo el espacio se llena de blanco; si devuelve morado, de morado; si devuelve azul, de azul. Es decir, se trata de escribir un código que recibe las coordenadas de cada vóxel y devuelve el valor del color que se coloca allí. Quien comenta dice que «ya me está empezando a doler la cabeza», pero comenta que últimamente solo ha jugado a juegos de combate, así que de vez en cuando no viene mal un juego tan pacífico como este.
En el juego llega un correo del administrador de un lugar llamado Cubenet, y la dinámica consiste en resolver el conjunto de problemas básicos adjunto. En la parte superior de la pantalla se muestra la forma correcta y, en la inferior, la forma que dibuja el código actual. Al parecer, la puntuación también tiene en cuenta la cantidad de código y la velocidad de ejecución, pero se deja la optimización como «el postre para después» y se decide priorizar primero la legibilidad. Como motivo, se afirma que «el código, en principio, se escribe para personas».
1-1 a 1-3: cambiar colores y condicionales
En el primer problema, leyendo los comentarios del código ya existente, se ve que lo único distinto es el color que devuelve. Basta con cambiar PURPLE por RED para superarlo.
En el segundo hay que pintar con dos colores según la posición. Como se proporcionan las variables x, y y z, que representan las coordenadas, se resuelve con una sentencia if que devuelve DARKBLUE si y es mayor o igual que 1 y ORANGE en caso contrario.
El tema del tercer problema son los operadores de comparación. Se señala que en este lenguaje la desigualdad se escribe «~=» y se prevé que pronto habrá que usarla. El problema en sí sigue casi el mismo patrón que el segundo y se resuelve con una condición del tipo «y mayor o igual que tal valor». Como alternativa sin operadores de comparación se menciona una expresión como return GREEN - (y + 4) // 3 * 2, pero se descarta porque este tipo de código suele confundir a quien lo lee.
1-4 y 1-5: cómo hacer cosas transparentes
A partir del cuarto problema se empiezan a usar, además de la coordenada y, las coordenadas x y z. El código inicial ya incluye una combinación de colores en arcoíris: como reutiliza directamente el valor de la coordenada y como código de color, cada vez que se sube un nivel se pasa al color siguiente. Sin embargo, en este problema solo hace falta volver transparente una parte del objeto. Según los comentarios, se consigue transparencia si no se devuelve nada o si se devuelve 0 o EMPTY. Se cambia la condición del código inicial «cuando x y z son mayores o iguales que 2» por «cuando son mayores o iguales que 0» y se supera.
El quinto tiene casi la misma estructura y se resuelve escribiendo la condición «cuando x, y y z son todas mayores o iguales que 1».
1-6: formas simétricas con el valor absoluto
En el sexto problema aparece la función abs. Los comentarios explican que es una función que devuelve el valor absoluto y que resulta muy útil para dibujar objetos con simetría axial. La forma objetivo de esta vez es simétrica respecto a todos los ejes.
Primero se fija en el eje z y define el grosor haciendo que no se devuelva nada si el valor absoluto de z es mayor o igual que 2. También podría escribirse «mayor o igual que 2 o menor o igual que -2», pero considera que usar el concepto de valor absoluto da un código más fácil de entender. La forma de almohadilla restante también se construye con valores absolutos, creando barras verticales y horizontales que se unen con el operador lógico or. Como las estructuras simétricas probablemente aparecerán a menudo, prevé que habrá muchas ocasiones de usar el valor absoluto.
1-7: leer el mensaje de error y corregirlo
El séptimo problema da error desde el principio. Lo primero es leer el mensaje de error. A partir del texto «LUA_ERRSYNTAX» deduce que el lenguaje es Lua. Se espera un end que cierre el if de la línea 10, pero en la línea 13 hay un if. Es decir, falta escribir un end.
Corregir esto no basta para terminar, porque queda una zona en la que una caja que debería ser roja aparece en GREY; tras cambiarla también, se supera. Aquí comenta que en realidad son pocas las ocasiones de escribir código desde cero y que, en la práctica, a menudo importa más saber leer el código de otros.
1-8: el reto libre «sushi?», resuelto con la distancia de tablero de ajedrez y una tabla
Sin embargo, el octavo problema obliga a escribir desde cero. Se llama «sushi?», y quien comenta percibe en ello «una sensibilidad tremenda». Como parece que la forma se puede crear libremente, se plantea pintar en rojo, blanco y verde según la distancia al centro.
La distancia que se usa aquí es la llamada distancia de tablero de ajedrez: cuántos pasos necesita una pieza que puede moverse en 8 direcciones para llegar a un punto. Se presenta como un concepto que también resulta útil de vez en cuando en roguelikes como «Shiren the Wanderer». Como esta distancia es fácil de calcular si se dispone de una función max, abre la pestaña Documentation para investigar. La documentación tiene 4 capítulos: el 1 es una introducción, el 2 detalla el dibujado y el 3 parece contener la información buscada. Ahí confirma que se pueden definir variables, que se pueden usar operadores como + y -, y que existe la función max.
Para convertir a rojo, blanco y verde, si no hubiera ninguna función práctica, la idea era ramificar sin más con sentencias if. Como «implementación curiosa», menciona también la opción de colocar 7, 1 y 11 en hexadecimal como 0xb17 y desplazarlo a la derecha según la distancia.
Aquí, a modo de inciso, cuenta que los plugins de OBS, el programa que usa habitualmente para grabar sus vídeos, también se pueden escribir en Lua. Una vez escribió un plugin sencillo para generar automáticamente las marcas de tiempo de YouTube, y quizá esa sea la única vez en su vida que había tocado Lua.
En la implementación, primero calcula la distancia de tablero de ajedrez desde el origen tomando el máximo entre el valor absoluto de x y el de y. En este caso el resultado es 0, 1 o 2, así que hace falta un mecanismo que convierta 0→7 (rojo), 1→1 (blanco) y 2→11 (verde). Como había visto de pasada la palabra «table» en la documentación, deduce que se puede usar una estructura parecida a un array. Escribe un literal de array con corchetes, como en muchos lenguajes, pero en Lua no funciona. Entonces, pensando por el nombre «Table» que serían llaves, lo reescribe y casi funciona. Sin embargo, los colores salen desplazados una posición, y así descubre que en Lua los índices empiezan en 1. Lo corrige en consecuencia y el sushi queda terminado.
Los elementos utilizados eran algo enrevesados, pero al mirar el código parece sorprendentemente conciso, según dice. Eso sí, añade por su cuenta que normalmente el único que piensa eso es quien lo ha escrito.
1-9 y 1-10: «dónde no pintar» e if anidados
El noveno problema se parece a un tutorial de manejo que invita a mover la cámara con el ratón para examinar bien la forma. Es una caja abierta por arriba: basta con devolver BROWN cuando x o z valen ±3, o cuando y vale -3. Al excluir el caso de y igual a +3, la tapa superior queda abierta. Aquí también se usa el valor absoluto. Los comentarios parecían querer que se expresara «dónde no pintar» en lugar de «dónde pintar», pero considera que da igual una cosa que otra. Como se había esforzado demasiado con el sushi anterior, le resultó algo decepcionante.
El décimo problema tiene, según quien comenta, una combinación de colores como la de Google Chrome, pero no se aprecia ninguna regularidad especial en el orden de los colores. Los comentarios también parecen esperar if anidados, así que escribe los condicionales sin complicaciones. Cuando y es mayor o igual que 1, devuelve GREEN si x es mayor o igual que 1 y ORANGE en caso contrario. Cuando y no es mayor o igual que 1, devuelve BLUE si z es mayor o igual que 1 y RED en caso contrario. Va escribiendo mientras gira la cámara para ver mejor los ejes.
1-11: una doble errata y el mecanismo de la X
El undécimo problema, el último del tutorial, también tiene fallos desde el principio. Según el mensaje de error, en la línea 31 se usa una variable desconocida llamada «colo». Deduce que es una errata de «color» y la corrige. Pero con eso solo se dibuja una parte de la escalera. Leyendo más el código, encuentra un punto escrito con la grafía del inglés británico, «colour»; al corregirlo, queda terminado.
En cuanto a por qué este código puede dibujar una X, interpreta que se debe a que usa expresiones como x==y y x==-y. Es decir, al usar funciones lineales como igualdades se pueden dibujar formas así. Aquí comenta: «Bueno, seguro que es un conocimiento que no tiene nada que ver conmigo».
2-1 a 2-3: dibujar líneas diagonales con igualdades
Al terminar el tutorial, llega de la comunidad un correo de bienvenida junto con un conjunto adicional de 7 problemas.
En el primer problema del conjunto adicional aparece enseguida un problema de funciones lineales, justo después de haber dicho que «no tenía nada que ver». Es la recta y = -x, que en Lua puede escribirse x == -y. La comparación con == devuelve true o false, pero en Replicube eso se dibuja como blanco y vacío, así que basta con devolver directamente el resultado de la comparación para superarlo.
El segundo también es una recta diagonal, pero hay que acotarla además con la coordenada z. Primero dibuja un plano con x == y y, uniendo la condición x == z con el operador lógico and, completa la forma. Para que el valor devuelto sea GREEN o vacío en lugar de true/false, añade and GREEN al final, aprovechando la evaluación en cortocircuito.
El tercero es la versión simétrica del segundo, así que se resuelve simplemente cambiando la expresión por su versión con valores absolutos. Calcular abs(x) dos veces es algo innecesario, pero como no es una operación lenta, decide no preocuparse.
2-4 a 2-6: patrones con el resto y la suma
En el cuarto problema se introduce el operador %, que devuelve el resto de una división. El código inicial reparte en orden entre 0, 1 y 2 usando el resto de dividir y entre 3. Al observar la forma correcta, parece que el valor de x se convierte en 0 o 1, así que deduce que basta con usar el resto de dividir entre 2. Como quedaba desplazado una casilla, lo ajusta restando 1 a x y lo supera.
El quinto es un tablero de damas. Primero, para convertirlo en un plano, intenta escribir un proceso que devuelva vacío si no es 0, pero al principio escribe la condición con z y luego la corrige a y. Las flechas RGB corresponden a XYZ, pero de vez en cuando se le olvida y se lía, según cuenta. En cuanto al patrón, había «echado un vistazo a escondidas» al código inicial del segundo problema, return x+y, que con la suma genera un patrón de franjas diagonales. Si se reduce con %, se obtiene un tablero de damas. Al principio sale rosa, así que lo corrige para que devuelva negro y lo supera. Muestra que, si se quieren evitar los condicionales, también puede hacerse con una expresión que convierta los restos 0 y 1 en el blanco y el negro, 1 y 3, pero considera que la conversión a colores resulta muy difícil de entender.
El sexto es un patrón de arcoíris en diagonal. Aprovechando que la suma permite dibujar franjas diagonales, primero suma x e y. Con eso se obtiene un aspecto bastante parecido. Pero, mientras que en su código el origen devuelve 0, en la solución correcta es LIGHTGREEN, así que suma LIGHTGREEN para que coincida. Por último, ajusta el grosor devolviendo vacío cuando z no es 0 y lo termina.
2-7: una mesa de cuatro patas y organizar el código con funciones
El problema final es una típica mesa de cuatro patas, toda de color BROWN. Como solo hay que pensar en la forma, siguiendo el proceso de montaje real trata por separado el tablero y las patas. Como ha descubierto que se pueden escribir comentarios con --, también los añade. El tablero es simplemente un plano que devuelve BROWN cuando y vale 1. Para las patas, primero limita el área de dibujo a y menor o igual que 0 para fijar la altura. Como su posición es simétrica, usa el valor absoluto como siempre y solo dibuja cuando los valores absolutos de x y z son ambos 2. Une estas condiciones con and y completa la forma devolviendo BROWN cuando se cumplen todas.
Sin embargo, le da un poco de pena terminar ya y decide retocarlo un poco más. Según la documentación, debería ser posible definir funciones propias, y quería probarlo. El código actual escribe return BROWN dos veces aunque todo el material es BROWN, así que si le dijeran «¿al final me lo puedes cambiar a WHITE?», tendría que hacer el trabajo dos veces. Por eso crea funciones que solo se ocupan de la forma y lo reescribe para que el color se decida en un único sitio.
En concreto, define la función isTopPlate, que devuelve si esa coordenada pertenece al tablero, y la función isLeg, que devuelve si pertenece a una pata, y escribe «si es tablero o pata, devuelve BROWN». En Lua probablemente lo habitual en los nombres de funciones es snake_case, pero esta vez, como la pantalla es extremadamente estrecha, usa camelCase por si acaso. Las llamadas a funciones tienen cierto coste, pero considera que no es un problema porque el dibujado de un objeto estático no exige tanto rendimiento. Por otro lado, señala que, como al parecer en este juego también se pueden crear animaciones, en esos casos quizá convenga prestarle algo más de atención.
Valoración de quien comenta
Tras terminar los dos conjuntos de problemas, valora «Replicube» de la siguiente manera. Al principio daba la impresión de ser poco accesible, pero tanto la interfaz como la introducción están muy cuidadas y, al jugarlo, resulta muy divertido. Es un excelente juego de puzles con diversos enfoques y soluciones, y también se puede disfrutar como punto de partida para interesarse por la programación. Concluye que tiene además conexiones con áreas de las matemáticas y que es una obra que se puede disfrutar desde muchos ángulos.
No se encontraron artículos.
Artículo publicado · Actualizado
