@ agnasg

¿Cómo se hizo tilemovers?

15-01-2021 3:04 PM

¿Por qué hacerlo fácil si lo podemos hacer difícil?
— típico programador sabelotodo

Sinópsis

tilemovers es un juego de sliding tiles (¡baldosas deslizantes!) donde el objetivo es mover un tile de la posición inicial a la final. Este tipo de juegos es como los “3 Chiflados”: el 50% de la gente los ama, y el 50% de la gente los odia. Para colmo, le he agregado 3 niveles: “Normal”, “Nightmare” y “Apocalypse”. Es decir, fácil, difícil y superdifícil. Pero para el que conoce este tipo de juegos debería ser divertido.

La diferencia con los otros 1000’s juegos similares es que los tiles vienen en 4 tamaños: 1×1, 1×2, 1×3 y el endiablado 2×2. La página oficial para descargar el juego es https://agnasg.itch.io/tilemovers, también hay un video (no apto para cardiacos) https://www.youtube.com/watch?v=wbcf3ICA2es.

El devlog en español está aquí https://www.micronosis.com/category/tilemovers/ y en inglés en tigsource https://forums.tigsource.com/index.php?topic=71419.0. Lo pueden jugar en windows (https://agnasg.itch.io/tilemovers) o en Android (https://play.google.com/store/apps/details…).

Módulos

tilemoves se puede descomponer en 4 módulos:

  • El sistema gráfico implementado en SDL2
  • La lógica del juego que implementa los movimientos de los tiles, sus tipos y los comandos que los gobiernan.
  • El módulo de bases de datos que usa una bd SQLite para almacenar y cargar la información del juego.
  • Un editor que permite crear los puzzles y los almacena en la base de datos.

En este post vamos a hablar de este último módulo, el editor de puzzles. Cuando entendí la necesidad de tener una herramienta para agregar, quitar, cambiar el tipo de tile, y guardar los cambios, me propuse los siguientes objetivos:

  • Facil de usar, es decir agregar y borrar los tiles debe ser rápido y fácil.
  • Fácil de cambiar el tipo de tile (estático, rápido, angel y humano)
  • Integrado al juego para repetir el ciclo de probar, cambiar, probar rápidamente.
  • Debe ser fácil de implementar

Para cumplir con todos estos requerimientos es invevitable que el panel de control quede congestionado sobretodo porque mi objetivo es que funcionara integrado al juego. Estuve viendo algunas ideas de cómo hacer el gui, incluyendo paneles como este:

DeadImgui Example

Para lo cual tendría que utilizar una tercera librería (ya el juego está usando SDL2 y SQLite) y eso no es bueno, al menos para este proyecto. Estoy pensando en planificar el estudio para analizar el posible uso de DearImgui para los gui de los siguientes juegos incluyendo el próximo juego (memory + arcade + platform, todavía sin nombre) y para khpx, pero no para tilemovers porque eso iba a agregarle más tiempo al ya abultado proyecto.

Por eso simplifiqué todo y me quedé con un panel incrustado con todos los botones:

La primera línea son operaciones con los tiles: intercambiar posición, cambiar su estatus, agregar nuevo tile, eliminar tile. Luego botones para guardar cambios, guardar como un nuevo puzzle, opciones para generar un puzzle en forma aleatoria (cuando estaba sin mucha inspiración). También opción para cambiar de nivel cuando su nivel de dificultad no era apropiado para su nivel actual. Finalmente botones para asignar el tipo de tile: faster (se mueve en todas direcciones), static (no se mueve), angel (se mueve verticalmente, human (se mueve horizontalmente).

Las operaciones de agregar y borrar tile se ven mejor en video:

Conclusión

No llevé contabilidad de cuánto tiempo le dediqué al editor específicamente, pero a grosso modo fue cercano a un mes (lo cual podríamos traducir en 24-30 horas, que es lo que yo le dedicaba mensualmente a este proyecto). En comparación al total de tiempo empleado podría decir que el editor ocupó un 20% del tiempo del proyecto. Creo que es el tiempo mejor empleado en todo el proyecto.

Cómo migrar una db implementada en Windows y SQLite a Android sin decir grocerías

06-01-2021 8:23 AM

Object-oriented no es el único patrón de diseño válido. A muchos programadores se les ha enseñado a pensar puramente en términos de objetos. Y, para ser justos, los objetos son a menudo una buena manera de descomponer un problema. Pero los objetos no son la única manera, y no siempre son la mejor manera de descomponer un problema. A veces el viejo y buen código de procedimientos es más fácil de escribir, más fácil de mantener y entender, y más rápido que el código orientado a objetos.

Por qué SQLite está programado en C

En el post anterior, describo mis peripecias migrando un juego desarrollado en C++ y SDL2 a Android.

El juego es tilemovers y ya está publicado en itch.io. Puedes echarle una mirada.

Versión Android en el Google Store. Por favor descarga la versión Android y enviame tus comentarios.

En este post describo cómo fue el proceso de migrar el código relacionado a SQLite y la base de datos. Por qué mi juego tilemovers requiere una base de datos, es tema de otro post, baste decir, por ahora, que la idea detrás de programar videojuegos es la diversión intrinseca en hacerlo, es decir, para mí tiene/debe ser divertido hacerlo, y no hay nada más divertido que programar juegos que usen complicadas bases de datos. Si alquien se divierte jugando el juego, es un extra maravilloso, pero no requerido.

Como explico en el post anterior, si es la primera vez que programas una aplicación para Android (y para mobile en general) hay muchas preguntas obvias cuya respuesta desconoces (aunque es algo que un programador de mobile sabe desde el prescolar y Plaza Sésamo).

¿Dónde se guardan los archivos, y en particular, la base de datos de SQLite?

Es un asset como cualquier otro y se declara en la carpeta assets dentro de la estructura de archivos de Android manejada por Android Studio (en adelante AS). Es decir,

\app\src\main\assets

pero no puedes escribir ahí, es decir, esos archivos son read-only. Tienes que moverla al área de datos de la aplicación (se dice fácil pero es una epopeya: leer más adelante)

¿Y hay que pedir permiso para escribir en la base de datos?

Como dice la documentación oficial, la aplicación no requiere autorización para leer o escribir en los directorios asignados para almacenamiento interno (de nuevo, no estamos hablando de la carpeta assets, donde solo se puede leer)

¿Por qué no se puede abrir un asset usando ifstream o apuntadores a archivos?

De acuerdo a esta respuesta en stackoverflow, porque no. Pero en el ámbito de esta respuesta, si se puede utilizar sqlite.c (es decir no hay que migrar SQLite a la versión java/kotlin) pero la base de datos tiene que estar en el área de datos de la aplicación. (¿por qué estoy repitiendo esto tantas veces? Porque un programador avezado y con experiencia en otras plataformas va a encontrar bien complicado de entender que una aplicación tiene al mismo tiempo 2 áreas de datos asignadas, una read only, y la otra read-write-has-lo-que-quieras).

Luego de mi investigación (que puede no estar completa, y puede contener errores). Las alternativas a la mano para trabajar con bases de datos o archivos de datos en una aplicación Android son las siguientes:

  1. Necesitas trabajar con AAssetManager, AssetManager_open, AAsset_read para acceder a la base de datos tal como se explica aquí. Esta es una solución implementada en C usando NDK. Se llama SQLite-NDK. No hice esto, aunque dejo abierta la posibilidad para otros juegos, ver abajo.
  2. La bases de datos almacenada en assets no se puede usar directamente, hay que instalarla en:
    “data/data/your-package-name/databases” (ver punto 5 abajo)
  3. Una idea es tener los datos en un archivo json o xml, crear la base de datos y luego cargar los datos (como se explica aquí).
  4. Otra sugerencia es usar el Content Provider
  5. Otra idea es utilizar una clase DataBaseHelper derivada de SQLiteOpenHelper para copiar la base de datos de assets al área de datos de la aplicación. Aquí se explica cómo es el proceso. Esta explicación al parecer es más clara. Este fue el procedimiento implementado.
  6. Para obtener la ubicación donde se va a ubicar la base de datos se debe utilizar:
    File dbFile = context.getDatabasePath(name_of_database_file);
    (ver aquí, y ver documentación en developer.android.com, hay que usar esta función o la aplicación puede fallar en algunos teléfonos inteligentes nuevos, por ejemplo, Huawei)

Hay varios retos para hacer que todo esto funcione. Primero hay que averiguar cómo llamar código escrito en java desde la aplicación desarrollada en C/SDL2 (sí, en cierto momento surge la pregunta de por qué no migrar completamente a kotlin (el lenguaje nativo de aplicaciones android) y la respuesta es que la idea desde el comienzo es migrar una aplicación desarrollada en C++, en Windows usando SDL2 a Android).

La solución es simple tal como se explica aquí. El siguiente paso es cómo integrar una clase java a una aplicación desarrollada usando NDK, dentro de Android Studio (lo inverso es fácil y aparece aquí, es decir, como usar NDK desde una aplicación desarrollada en kotlin).

Lo interesante es que viendo ese artículo recordé que Android Studio tiene menus, muchos menus, y entre ellos hay un “Add Java class”, el cual funciona pero no necesariamente sobre el nombre del proyecto (como mi intuición me indica) sino sobre la carpeta del paquete que en el caso de mi aplicación basada en sdl se llama org.libsdl.app

Al agregar la clase, Android Studio pregunta a cuál paquete lo vamos a agregar, indicamos org.libsdl.app. Copiamos el contenido de nuestra clase al archivo creado. Este archivo es almacenado en app\src\main\java\org\libsdl\app

Nuestro archivo java es basicamente una clase ( DataBaseHelper) que verifica que la base de datos existe. Si no existe la copia desde assets al área de datos. La soluión completa está aquí.

La declaración de la clase luce así:

public class DataBaseHelper extends SQLiteOpenHelper

Ahora, como dije antes todo parace simple siempre y cuando puedas hacer llamadas a java desde tu aplicación desarrollada en C.

Para ello, básicamente tienes que usar esta llamada

jclass dbhelper = env->FindClass(“org/libsdl/app/DataBaseHelper”);

donde estás declarando un apuntador (creo que en el universo java tiene otro nombre) a la clase (que debes identificar colocando el path completo)

El primer problema es cómo consigues inicializar la variable “env”. Si comienzas a buscar en stackoverflow quedas en un lazo infinito porque para inicializarla, necesitas hacer;

JNIEnv *env;
g_JavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)

Y ahora tu problema es como inicializar la variable g_JavaVM, el apuntador a la máquina virtual. El cuento es largo y lo voy a omitir, al final la forma de hacerlo es inicializar la llamada en el momento de carga de JNI, lo que se hace a través de esta función:

JavaVM* g_JavaVM = NULL;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {

   g_JavaVM = vm;
   return JNI_VERSION_1_6;
}

Yo estuve varias horas tratando de utilizar Android_JNI_GetEnv(void); sin suerte, función que se encuentra declarada en SDL_android.h (en el código de la versión Android de SDL2) pero no funciona. La única forma que descubrí fue con la llamada JNI_OnLoad ().

Luego que tienes el apuntador a la clase debes crear una instancia de la clase (un objeto) lo que haces a través de lo siguiente:

jclass javaGlobalClass = reinterpret_cast<jclass>(env->NewGlobalRef(dbhelper));

jmethodID helperConstructor = env->GetMethodID(javaGlobalClass, “<init>”, “(Landroid/content/Context;)V”);

Con esto puedes hacer la llamada para la ejecución del constructor de la clase:

jmethodID helperConstructor = env->GetMethodID(javaGlobalClass, “<init>”, “(Landroid/content/Context;)V”);

Y con el contructor haces la llamada a createDataBase:

jmethodID createDataBaseMethod = env->GetMethodID(dbhelper, "createDataBase", "()V");
env->CallVoidMethod(obj, createDataBaseMethod);

Y con la base de datos, ya puedes acceder a ella utilizando las llamadas de SQLite de forma normal, tal como lo haces en cualquier programa C/C++ implementado con SDL2, que en nuestro caso es tilemovers.

Cómo migrar un juego desarrollado con SDL2 en Windows a Android

04-01-2021 1:22 PM

“El hombre, en su miserable condición, levanta con la mente complicadas arquitecturas y cree que aplicándolas con rigor conseguirá poner orden al tumultuoso y caótico latido de su sangre” Álvaro Mutis

Nunca digas no

Durante los últimos 18 años, que he estado trabajando como freelancer, me han preguntado al menos una vez al mes si trabajo mobile. Es decir, si hago aplicaciones para el play store. Mi respuesta indubitable siempre ha sido “No“. A veces “Yo no trabajo eso” con énfasis en “eso“. Y cuando estoy realmente de mal humor “Que no chico, que no“.

Hoy, estoy descargando Android Studio (AS), el jdk actualizado, y otras librerías para hacer mi primera aplicación “comercial” en el play store. He trabajado algunos proyectos formando parte de un equipo y mi trabajo ha consistido en resolver “detalles“. En estos casos hemos usado Qt. Esta es la primera vez que hago un proyecto 100% utilizando Android Studio, yo, solo.

Lo que pasa es que yo aprendo lentamente: las primeras 3 reglas que debemos aprender en la vida de los negocios son:

  1. No permitas que nadie dirija tu vida (“Claim your life“)
  2. Aprende a ser despiadado (“Learn to be an asshole“)
  3. Nunca digas no. (“two are enough!!!”)

Tardé 41 años para aprender la primera regla, 37 segundos para aprender la segunda y 18 años para aprender la tercera. Nunca es tarde para aprender, ¿cierto?

Si eres programador freelance, tienes sólidos conocimientos de negocios y quieres expandir tus horizontes (¿quién no?), mobile es una apuesta inevitable. Además, no se trata de hacer un mmo con temática espacial y que requiere 3 años de desarrollo (como khpx). Los juegos mobile son, casi por definición, fáciles de jugar y “fáciles” de programar (¿cierto?)

Los juegos en el play store tienen que ser de bajo nivel, nivel idiota por decirlo de alguna forma. Estilo flappy bird. Y por lo que se puede ver con khpx, a mi me gustan los juegos complicados, y tardar años en ellos. Así que voy a tratar de migrar mis juegos cortos (que tengo unos cuantos) y luego paso a juegos complicados.

¿Tardar años en ellos?” ¿Qué clase de loco soy? ¿Cuál es el problema? Si 3 años te parece demasiado, piensalo de nuevo. Por ejemplo, este señor dedicó 11 años de su vida a hacer un Line Rider Track!!!

A continuación mis anotaciones sobre el proceso de migrar tilemovers, un juego de tiles, que se mueven, y son tiles, (y se mueven) y la idea es mover uno de ellos desde la posición inicial, hasta la posición final. Listo. Esa es toda la documentación. The end.

Esta decisión de colocar khpx en suspenso y trabajar juegos pequeños no es al azar. Hubo mucha intelectualización (una palabra que acabo de inventar). Baste decir que a veces, un resultado temprano es necesario. Hay otras consideraciones pero este post está demasiado largo, así que no va a pasar la edición final. Aquí algunos comentarios sobre juegos pequeños:

Hacer juegos cortos e intensos:
piensa en haiku, no en épico.
Piensa en poesía, no en prosa.”

Más adelante estaré migrando otros: un juego de memoria, un juego tipo asteroides y un juego tipo counter strike pero muy simple y por supuesto monojugador.

Voy a trabajar en:

  • C++ (no hay sorpresas aquí)
  • SDL2 (podría ser Qt pero la parafernalia es demasiado aparatosa (y parafernálica), lo cual no digo que la de SDL2 no lo sea, pero es más por el tema de AS)

Retos

SDL2. El problema con SDL2 es SDL. Hay dos versiones y en todas partes (StackOverflow e inclusive algunos sitios de tutoriales sobre SDL*) se confunden las filosofias de los dos. El enfoque es ligeramente diferente y las llamadas son diferentes. SDL2 se parece a SDL pero es diferente. De hecho, el mencionado juego de memoria está implementado en SDL, y lo he estado viendo de reojo, o sea, abro el archivo, lo veo, salgo corriendo, me tomo nerviosamente un whisky, lo vuelvo a ver, me tomo otro whisky. Etcétera. Creo que lo voy a reescribir en SDL2 sin siquiera verlo otra vez. De hecho voy a borrar todo el directorio que lo contiene:

format c: \ memorybreaker

Listo lo borreddddddddddddd

Es infinitamente repetitivo. Después de 50 juegos realizados comenzar a migrar/adaptar un juego a una nueva plataforma es extremadamente aburrido y repetitivo. Los tiempos en que algo nuevo me entusiasmaba ya pasaron así que tengo que armarme de entusiasmo y optimismo.

Android. Ya he hecho algunas cosas, nada del otro mundo así que esto es nuevo, o casi nuevo. No conozco Android Studio así que veremos.

Ahora, el problema desde el punto de vista de negocios es que este proyecto (publicar una aplicación en el play store y ganar dinero con eso) se supone que debe ser algo que no debe durar más de 2 semanas de comienzo a fin: voy ya por la segunda y apenas la aplicación está lista para comenzar la migración. Además de la migración, falta el marketing, otras muchas cosas. (NOTA: desde que escribí eso hasta hoy ya han transcurrido 1 2 3 4 5 meses).

Es como el proceso de ingresar en Tiragarde Sound (World of Warcraft) un alt, debería ser rápido (10, 15 minutos?) Pero en realidad son 2-3 horas. Finalmente yo soy un programador como Arthur Bishop (el personaje interpretado por Charles Bronson en Asesino a precio fijo (ves, otra película que en español tiene un mejor nombre (en inglés es The mechanic (los que han leído otros post en este sitio sabrán de qué estoy hablando ))))

Arthur Bishop recibe contratos para asesinar a funcionarios rivales, millonarios etc. Pero él es un asesino metódico e infalible: él estudia a su objetivo durante semanas para descubrir los puntos débiles de seguridad en su rutina diaria. Una vez identificado el momento perfecto cuándo su victima es más vulnerable, procede a ejecutar a su objetivo.

Yo procedo de una forma similar. Tengo 3 días pensando un algoritmo para generar los niveles del juego de una forma automática. Pronto estará liquidado. Pero va a tomar su tiempo, necesito verificar todos sus ángulos.

Nota del editor: lo anterior fue escrito alrededor del 19 de julio, y los planes segun se puede entender, es que tilemovers mobile esté listo en 1-2 semanas. Hoy es 16 de octubre.

El proyecto se demoró un poco porque se me ocurrió hacer un algoritmo para resolver los puzzles. Resulta ser que este es un problema complicado. Pasé un mes haciendo un programa no recursivo que algunas veces resuelve el puzzle, otra veces comienza a generar soluciones con muchos pasos innecesarios. Entonces pasé 2 semanas haciendo un programa para eliminar los pasos inncesarios. Finalmente decidi probar los puzzles a mano. El programa quedará como un caso de estudio. Si alguna vez lo termino colocaré el resultado en github como una contribución a la humanidad.

Tratar de hacer un programa para resolver uno de estos puzzles es algo bien difícil de justificar. Es como Kurz (Apocalipsis ahora) entrenando para ser paracaidista a los 38 años. “¿Por qué hizo algo así?

Yo tengo una buena razón. Lo prometo. Es una muy buena razón. Estaba tratando de invertir la búsqueda de buenos puzzles. Al diseñar los puzzles se llega a un momento en que las ideas se agotan. Así que quería hacer un algoritmo capaz de resolverlos, y de esa forma encontrar puzzles con soluciones difíciles. Es decir, diseñar el puzzle basado en soluciones difíciles de encontrar. Este camino no funcionó.

El juego tiene 5 4 3 niveles de dificultal. El primer nivel Fácil Normal tiene 512 256 128 puzzles (todos probados a mano. En algún sitio tengo notas sobre algunos de ellos, los publicaré en un próximo post). El segundo nivel Difficult Imposible tiene 64 32 28 y el tercer nivel Kill me now Apocalipse tiene 12 puzzles. En futuras versiones a estar disponibles pronto agregaré más puzzles.

Versión Windows

Como está programado en SDL2 potencialmente se puede publicar además de Windows en Linux, Ios y Android. La versión windows se puede descargar aquí:

https://agnasg.itch.io/tilemovers

Es gratis. Si usted lo desea puede contribuir con $2 o más, lo cual me permitirá hacer más juegos similares (más rápidamente).

También puede contribuir en mi página de patreon que me ayudará a terminar mi mmo, khpx5 y otros juegos del baúl de los zombies.

Versión Android

Nota: esta sección discute mis vicisitudes migrando tilemovers a Android. Es una aventura con mucha acción, intriga, emboscadas, romance, suspenso y un desenlace feliz.

Versión Android en el Google Store.

Protagonistas:

Android Studio: el ambiente de desarrollo (algo así como Visual Studio)

Gradle: el build system, permite organizar y ejecutar la compilación y linkeo con todos los componentes involucrados.

ndk: Native Development Kit, un conjunto de herramientas que permiten usar C++ con Android.

Para la instalación de android, cosa que que hago por primera vez (como dije al comienzo yo nunca he trabajado con app mobile) seguí esta guía. https://lazyfoo.net/tutorials/SDL/52_hello_mobile/android_windows/index.php. Hay otras guías en google, pero esta me pareció la mejor.

Nota (04-01-2021): otra guía es esta que está ligeramente actualizada y tiene explicaciones pormenorizadas. Lamentablemente la encontré muy tarde. Como dice el autor, “estás entrando en el reino de desarrollo Android… respira profundo…“.

Como suele suceder en estos casos la guía está desactualizada y en el paso 5 las cosas comenzaron a aparecer diferentes. En el momento de la instalación, no aparece la pregunta sobre la ubicación del Android SDK (que debe ser inicializado a c:\androidsdk). Esa pregunta aparece luego de instalar y ejecutar por primera vez la aplicación.

La guía está tan desactualizada que indica instrucciones para Android SDK 16 (la versión que estoy instalando en octubre de 2020 dice Android 30. Hay errores que no me sucedieron a mi, y otros que sí: por ejemplo, el paso 15 Error:(688) Android NDK: Module main depends on undefined modules: SDL2.

Problemas encontrados: muchos, incluyendo algunos inexplicables. Uno de los tantos escoyos (algo que particularmente sucede con aplicaciones Java) es que hay múltiples versiones de multiples paquetes y tablas de compatibilidades entre paquetes y multiples errores debido (quizás) a esas incompatibilidades. Por ejemplo, aqui aparece las compatibilidades entre versiones entre Android Gradle Plugin y Android Gradle: trabajando este proyecto tuve que revisar innumerables veces stackoverflow, y encontrar tablas como esta con mucha frecuencia.

En resumen, el error que no se encuentra SDL2 se resuelve tal como se explica en la guía. El error “fatal error: ‘string’ file not found” se presta para confusión porque el archivo makefile “Android.mk” no tiene la línea:

#APP_STL := stlport_static

Simplemente se agrega y el error desaparece. (esta línea no hace falta, ver más abajo)

Otro error que apareció no está incluído en la guía:

Unsupported method: TaskExecutionResult.getExecutionReasons()

La consola indica que hay una incompatibilidad con Gradle sin dar mayores detalles. Un paso que me salté en la guía es el paso 12 porque a mi no me apareció el error:

Minimum supported Gradle version is 4.1. Current version is 2.14.1.

Pues bien, en la nueva versión, o debido a que yo tenía la versión 3.1.x el mensaje de error es diferente (después de varios tropiezos, resbalones, caidas, risas, gritos desconsolados a la media noche, alaridos y otras manifestaciones de frustración apareció sin explicación el mensaje ” Minimum supported Gradle version“. Si siempre estuvo ahí, no estoy seguro ahora) . Para resolverlo se abre el archivo build.gradle del proyecto y se modifica la línea

classpath ‘com.android.tools.build:gradle:3.1.4’

a

classpath ‘com.android.tools.build:gradle:4.0.2’

y en el archivo gradle-wrapper.properties la línea distributionUrl debe decir:

distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

(Aquí 6.1.1 se debe sustituir por la última versión).

La ventana va a desplegar un enlace para hacer Sync, lo que va a permitir descargar la actualización.

Salir del proyecto y volver a entrar, a veces funcionó para restaurar valores y hacer desaparecer errores. Como estoy trabajando como trabajamos todos los programadores, es decir, sin leer el manual, la mayor parte del tiempo estoy en un modo de descubrimiento. En stackoverflow y en otros sitios se habla de cosas como “Edit Configurations“, en Android Studio eso podría sea cualquier cosa, si no sabes exactamente dónde está (está dentro del menu de “Run”, ¿cómo es que no se me ocurrió?)

Android Studio es un caballo difícil de domar. ¿Cómo se definen las arquitecturas que se deben compilar? Por defecto, las compila todas, "armeabi-v7a", "armeabi", “x86”, “x86-64”, “arm64-v8a” (respuesta). Si voy a cambiar un asset, qué debo hacer? Si presiono “rebuild”, comienza a compilar todo de nuevo, eso no es lo que quiero, sino que el apk final incluya los nuevos assets. ¿Cómo se crea un nuevo proyecto basado en uno viejo? Me sorprende que “Import” de un proyecto viejo es lo mismo que “Abrir el proyecto“, aparte de abrirlo no hace nada más (esto puede generar problemas, ¿cierto?) (al final simplemente se copia la carpeta y se abre el proyecto desde la nueva carpeta).

Eventualmente conseguí las respuestas a estas y otras preguntas, pero hay que estar preparado para dedicarle tiempo (es decir, la curva de aprendizaje tiene subidas bien pronunciadas).

Mi impresión es que el proceso de instalación y ajustes de la aplicación tiene muchos detalles y cambian a medida que cambian las versiones de los plugins y paquetes. Lo mejor es descargar la aplicación y comenzar a trabajar paso por paso. Eventualmente todo funciona.

SDL es de temer

Hay que tener cuidado porque SDL no necesariamente es inofensivo. Puede causar dolor, y mucho. Por ejemplo perdí 1 hora tratando de descubrir por qué mi programa al ejecutar decía que no encontraba el SDL_main en la librería. Resulta que este define
#define SDL_MAIN_HANDLED
funciona en windows pero no en Android (es decir, en Android debe estar deshabilitado) Usualmente esto debería resolverse rápidamente con una visita a la documentación, pero tenía chrome cerrado porque mi máquina tiene solo 8gb y Visual Studio, Android Studio y el emulador mobile se lo devoran todo. Luego para resolver este problema temporalmente dupliqué mi memoria agregando a mi artillería mi laptop, ahora chrome tenía 4gb para él solito.

¿Otras emboscadas?

La variable abiFilters que es la que indica cuales arquitecturas van a ser soportadas por el ejecutable de Android (las posibilidades son ‘armeabi-v7a’, ‘arm64-v8a’, ‘x86’, ‘x86_64’) solamente debe modificarse en el archivo build.gradle de la aplicación ( el que está en la carpeta app) también se puede ajustar en Application.mk a través de APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 pero o es ignorado o el efecto es desconocido (al menos para mi).

Como un truco de productividad hay que tener a la mano el archivo build.gradle(:app) y cambiar la variable ‘abiFilters’ a la arquitectura del dispositivo que estás usando. Por ejemplo, pescando el problema con la base de datos sqlite corrupta, tuve que provar altenativamente entre el emulador Pixel 2 API 30 (abiFilters ‘x86’) y mi dispositivo celular (un antiguo BLU R1 HD) (abiFilters ‘armeabi-v7a’)

Otra nota curiosa: el emulador de Pixel 2 API 30 no es tan lento como dice se puede trabajar en él. Tuve que pasar varios minutos (más de los que desearía admitir) porque no encontraba cómo llegar a Settings->Applications para borrar ek juego e instalarlo de nuevo (también se puede hacer en AS -> Tools -> AVD Manager). Resulta que en google pixel el menú con todos los iconos se abre deslizando desde abajo en el borde inferior hacia arriba (en todos los dispositivos androides que he conocido es de arriba hacia abajo).

Todavía otra nota mucho más curiosa: implementar el movimiento deslizando los tiles require una combinación SDL_FINGERMOTION y SDL_FINGERUP. Ya yo había implementado todas las combinaciones y validaciones para el movimiento de los tiles, pero todavía tuve que crear nuevas funciones especialmente para SDL_FINGERMOTION. La forma correcta es olvidar la cantidad de movimiento reportado, al menos las 2-3 horas que estuve tradando de convertir esos deltas en la resolución correcta, en windows y en todas las combinaciones de dispositivos en android, determiné que es inútil. Igualmente inútil es tratar de convertir el valor float 0…1.0 que arroja SDL_FINGERUP a la resolución correcta. Mi implementación final es determinar la dirección del movimiento con SDL_FINGERMOTION y hacer los cálculos cuando se dispara SDL_FINGERUP .

En los últimos meses de 2020 estuve trabajando simultáneamente Python, Java, PHP, C++ y C#. Este último me tiene sorprendido, le huía como al Covid 19, pero ahora me gusta, me entusiasma, y siento una felicidad tan grande cuando cruzo el puente y entrego una flamante aplicación funcionando, que me sorprende que he hecho tan pocos desarrollos en C#.

Java. Las energías en el universo se compensan, y como Java sabe que yo lo odio, porque tenemos historia, él me paga con odio. Por ejemplo, el código para abrir una página web en android. Al parecer hay una forma de hacerlo desde ndk, pero como había tantas preguntas huí por la derecha como el león Melquíades. Además la versión java era tan simple, mjummmmm…

public static void openWebPage(String url) {
    Uri webpage = Uri.parse(url);
    Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

Ok, al tratar de compilar “eso” comenzó a decir “cannot find symbol”. Uri.parse fue fácil simplemente hay que agregar

import android.net.Uri;

y lo mismo con Intent intent = new Intent(Intent.ACTION_VIEW, webpage); hay que agregar:

import android.content.Intent;

El resto fue otra historia. ¿por qué sistemáticamente toda la documentación ignora/soslaya/oculta cuál es el archivo que hay que importar? Inclusive las respuestas en stackoverflow no incluyen los “import” respectivos, por razones que desconozco. Yo creo que todos los 200 programadores java en el mundo saben algo que yo no sé. O en alguna documentación hay una clave de como deducir el “import” basado en el nombre de la función.

Lo ignoro.

Que la documentación es imprecisa, incompleta o sospechosamente ambigua es algo que se ve por todas partes. Tengo múltiples ejemplos, pero veamos el de agregar el icono a la aplicación. Hayq eu olvidarse de stackoverflow porque esto ha cambiado mucho durante los años, y hay respuestas que corresponden a 2013, 2016 y 2018, ninguna sirve para la versión actual (Android Studio 4.0).

El manual dice que hay que usar un tool llamado Image Asset Studio. No dice que este tool se invoca directamente dentro de Android Studio, debemos suponer eso (aunque la forma que es descrito parece un tool aparte/externo). Pero más abajo nos dan las instrucciones (incompletas) de cómo invocarlo desde AS. Dice que en la ventana del proyecto se debe seleccionar la vista Android, tal como se muestra en la siguiente imagen:

Android Studio debería tener mapas que dicen “Ud. está aqui”

Luego dice como paso 2 ” Right-click the res folder  ” es decir hacer click derecho sobre la “carpeta res” pero ahi solamente está Gradle Scripts y “External Build Files”. ¿Donde está la carpeta res? Oh, sí, aparece otra vez app pero eso contiene los fuentes del sistema eso no debe ser, ¿verdad?. Pues sí es, dentro de app está la carpeta res. Pero eso lo omite el manual porque sí.

INSTALL_PARSE_FAILED_NO_CERTIFICATES

Este error aparece al tratar de instalar la version release, y, al parecer, no se ha completado correctamente el proceso de signature del apk. Vamos a ver que dice stackoverflow. Esto me dejó con un verdadero overflow.

  • Rebuild la aplicación a mi no me funcionó. Y eso que esperé los 10 minutos que tarda en mi equipo para generar el paquete para las 4 arquitecturas, release, etc.
  • Puesto que dice “APK signature verification failed.” debe ser algo con la firma. Cuando hago build dice Generate Signed Bundle… App bundle(s) generated successfully… así que el proceso Falló exitosamente. No entiendo.
  • Es algo con la firma pero “v2SigningEnabled true” en build.gradle no funciona.
  • Tampoco parece que tenga que ver con las versión de la firma v1 o v2 (full APK Signature), porque AS ya no tiene opción para indicar cuál versión se va a usar (no la tiene cuando generas un Bundle, pero cuando generas un APK, si aparece)
  • Si en vez de usar “Generate Signed Bundle” seleccionamos “Generate Signed APK” el error es el mismo.
  • Finalmente el problema se resolvió validando que en  “Project Structure” > “Modules” > “Signing Configs” esté seleccionado una configuración de firma correcta. Por defecto “Signing Configs” no está seleccionado. Esto debe ser un bug.
  • Jugué un extra tiempo aquí tratando de encontrar “Project Structure” . Es una opción en el primer menú, File, está entre “Settings” y “Other Settings“. Ya había estado ahí hace un mes y todavía se me pierde.
  • La primera vez que intenté esto me generó un error “Keystore file not set for signing config dev“, así que probé con la otra configuración “$signingConfigs.debug” y funcionó sin errores. El problema es que estaba trabajando con build release así que no quería nada “debug” involucrado.
  • Edité el resto porque ya este post ha crecido desmesuradamente. Aquí tenía al menos otras 3 notas adicionales con otras escaramuzas. Sigamos.

SQLite no tiene la culpa

La compilación de tilemovers avanzó sin problemas hasta que apareció este error al momento de ejecutar el linker:

../toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/include/c++/v1/ios:547: error: undefined reference to ‘std::__ndk1::ios_base::clear(unsigned int)’

Android Studio no indica por ninguna parte cuál código en mi aplicación está generando el problema, simplemente lanza ese error sin anestesia. Supuse que podía ser SQLite ya que yo estaba usando el código fuente directamente y no la librería que hay en la página de descargas:

sqlite-android-3330000.aar

Así que seguí este procedimiento para agregar directamente la librería. Pero eso no quitó el error.

Luego el error se hizo viral, y cada cambio generaba errores similares pero con diferentes funciones:

undefined reference to ‘std::__ndk1::__next_prime

undefined reference to ‘std::__ndk1::locale::locale

Aquí encontré una sugerencia, que tengo una mezcla entre compilación con el parámetro c++_static y el parámetro gnustl_static, es decir unas partes las estoy compilando con c++_static y otras (por ejemplo una librería) con gnustl_static, pero yo no estoy usando librerías externas más allá de SQLite.

Es muy gracioso cuando se presentan situaciones como estas (porque entre otras cosas no debería suceder). En mis pruebas, traté de utilizar SQLite librería (el archivo .aaa que se encuentra en la página de descargas) y removí el código fuente sqlite.c. Importé el archivo .aaa, quedó agregado como un nuevo módulo en el proyecto. Hice mis pruebas y no hubo cambios, el error continuó. Entonces traté de remover el módulo: ups, eso no es tan fácil como hacer RIGHT-CLICK Remove. No señor, eso en sí es una epopeya.

Aparentemente es suficiente removerlo del archivo y en la sección dependencies quitarlo de ahí (de build.gradle ). Pero eso no lo remueve del árbol del proyecto. Alrededor de stackoverflow se indica que basta con:

Removerlo en Build -> Edit Libraries And Dependencies (sigue en el árbol)

Removerlo del archivo settings.gradle (no está ahí)

Right click en el módulo y seleccionar “Select Open Module Settings ” (eso no existe, tampoco “Module Settings”, debe ser en una versión vieja)

No, “Module Settings” está en Right click sobre Project (no, no y no)

En settings.gradle buscar la línea':modulenameyouchooseduringimport' y borrarlo de ahi (no).

Al final como no parecía haber forma de borrarlo, comencé hacer cosas sin sentido. Por ejemplo, salir y entrar de Android Studio: el módulo aparece, y luego de unos 20-30 segundos desaparece (Android Studio tarda en mi máquina 2 minutos para cargar completamente). Parece que al borrarlo de build.gradle no se refleja el cambio en el árbol del proyecto a menos que salgas de la aplicación y entres de nuevo (eso es un bug, ¿correcto?)

En diversos sitios dice (por ejemplo) que ese error (undefined reference to ‘std::__ndk1::locale::locale ) se corrige agregando en el archivo build.gradle lo siguiente:

externalNativeBuild {
    cmake {
        cppFlags "-DANDROID_STL=c++_shared"
    }
}

Lamentablemente nada de eso funcionó probando todas las combinaciones.

Así que comencé a eliminar archivos en el juego con la esperanza de conseguir información adicional, y efectivamente así fue. Al ir eliminando módulos los errores cambiaban, pero siempre eran del tipo undefined reference to ‘std::__ndk1:: xx (algún método). Hasta que apareció

undefined reference to ‘std::__ndk1:: to_string ()

Eso encendió el bombillo sobre mi cabeza. ¿ndk/gradle/android Studio no está aceptando C++11, solamente C99? Porque std::to_string () es una típica llamada agregada en C++11. Pero ya había revisado eso casi desde el comienzo, en el Android.mk estaba el APP_STL := c++_shared que supuestamente (aquí lo confirman) habilita el soporte hasta C++17.

Cualquier combinación de esto falló:

APP_STL := c++_shared
APP_STL := c++_static
# Enable c++11 extentions in source code
APP_CPPFLAGS += -std=c++11
LOCAL_CFLAGS := -std=gnu++11
LOCAL_FLAGS := -std=c++11

Lo cual me llevó a un dead-end, un cul-de-sac, un callejón sin salida. Investigando cómo viajar en el tiempo usando gradle, descubrí que en la carpeta

\tilemovers\app.cxx\ndkBuild\debug\armeabi-v7a

Está el archivo donde se muestran todos los flags que clang está usando para cada uno de los archivos del proyecto. La línea que corresponde a mi archivo de prueba no tenía el flag:

–std:c++11

Curiosamente otros archivos sí tienen este flag, por ejemplo el archivo de SDL2

SDL2\src\hidapi\android\hid.cpp

Así que simplemente fui a esa carpeta y revisé el archivo Android.mk

La instrucción que se debe colocar es esta:

LOCAL_CPPFLAGS := -std=c++11

(eso no aparece en ninguna parte hasta donde me alcanzó la paciencia para revisar la documentación. Más adelante veremos que esto tampoco es necesario)

Al agregarlo la línea de comandos de mi archivo con std::to_string () apareció con el flag correcto (-std=c++11) pero el error siguió apareciendo.

Mi siguiente prueba fue determinar si estaba usando ese flag, así que elimine y agregué algo típico de c++11. Lambda expressions. Algo como esto (tomado de aquí):

auto unhex = [](char c) -> int {
return c >= ‘0’ && c <= ‘9’ ? c – ‘0’ : c >= ‘A’ && c <= ‘Z’ ? c – ‘A’ + 10 : c >= ‘a’ && c <= ‘f’ ? c – ‘a’ + 10 : -1;
};

Funcionó. Así que si estaba compilando tomando en cuenta las extensiones de c++11. ¿Y entonces? ¿Otro cul-de-sac?

Recordé que en algún sitio alguien decía que en alguna parte se estaba deshabilitando o asignando en forma incorrecta el uso de ndk. Comencé a buscar y revisar todos los archivos dentro de app y ahí estaba:

Application.mk

dentro de app\jni.

# Uncomment this if you're using STL in your project
# APP_STL := c++_shared

Funcionó (el error undefined std::to_string() desapareció)

Es decir, Android Studio ignora cualquier cosa que coloquemos en build.gradle, Android.mk y utiliza la opción en Application.mk

Lamentablemente las secciones dedicadas a C++ en la documentación no mencionan esto (por ejemplo aquí), pero sí en la sección dedicada a ndk. que por supuesto tiene sentido, dado que ndk es específico para C++. Como siempre, una vez resuelto el problema, resulta raro cómo es que no encontré la solución al comienzo.

Este error undefined std::to_string() representó unas 2 horas de escaramuzas e intrigas, pero fue divertido. Me recordó algo que siempre he criticado, la proliferación de archivos de configuración, algo muy común en sistemas java. En el caso de Android Studio hay que estar pendiente de 5 archivos de configuración. Afortunadamente, esto es algo que se hace una sola vez la primera vez que se instala/crea un proyecto, y más nunca en la vida. De aquí en adelante es un copy-paste de los proyectos.

¿Leer el manual? ¿Quién? ¿yo?

¿Por qué no leo la documentación? Porque es inútil. Por ejemplo, en el trabajo de programación diario, (edición-compilación-decepción-frustración-aceptación-repetición) en Android Studio vamos a ver con mucha frecuencia esto abajo en el UI:

Si usted va a trabajar con Android Studio prepárese a ver esa imágen dando vueltas durante minutos y más minutos. Mi record personal es 15 minutos. Algunas veces se queda atascado ahi y no te deja hacer nada más. No es que está procesando, está actualizando alguna librería de alguna parte de internet (eso no está en la documentación, eso lo leí en stackoverflow) Claro que eso se puede deshabilitar, simplemente hay que ir a View-Tool Windows-Gradle. No aparece una ventana popup, sino una ventana en el UI. En el borde superior hay un icono con dos rayas cruzadas por una línea: ese ícono significa trabajar offline. Si está presionado (en negrillas) significa que Gradle está trabajando offline, de lo contrario está online. ¿Conseguir todo eso en la documentación? Ni lo sueñes.

Historias similares ocurrieron con otras actividades Cómo haces para actualizar la aplicación en el dispositivo? si la ejecutas del Android Studio, no se actualiza sino que se ejecuta la versión instalada, no importa si AS sabe que acabas de hacer dramáticas modificaciones al código fuente, por defecto, ejecuta la versión en el dispositivo. what the fuck!. (Simply uninstall the application from your mobile device and then run your app, no eso no funciona= Al final, resulta ser que es un bug. Hay que hace clean y rebuild. Y AS es muy gracioso: Luego de tardar varios minutos haciendo varias cosas termina diciendo:
CONFIGURE SUCCESSFUL in 16s

AS tiene serios problemas de conducta

AS tiene sus peculiaridades. En internet dicen con frecuencia que es una inmensa montaña de !#@%. (AS is complete joke and bs. I am just tired of this nonsense ) Y a veces pienso que es posible. Por ejemplo, yo no he leido el manual, pero qué tan difícil puede ser hacer build de la aplicación. Inspeccionando el UI no hay ningún icono que diga “Build”, pero si hay un menu “Build”. Dentro del menú hay un “Make Project” que no me suena a “Build“, eso debe hacer otras cosas aparte de “Build” luego hay un “Make module app” eso si se acerca bastante a lo que quiero hacer, porque la otra opción, “Run generate sources Gradle Task” nuevamente se aleja demasiado de “Build“. Pero hay un problema: “Make module app” está deshabilitado. Luego de buscar todo el UI observamos que en la ventana del proyecto “app” no está seleccionado. Si hacemos click se torna azul (seleccionado) y ahora “Make module app” está habilitado. Nuestro siguiente problema es que no vemos qué está pasando y eso se debe a que la ventana de “Build” está oculta: tenemos que hacer click sobre el tab “Build” en el sector del UI de ventanas de outputs. Si tenemos suerte y el “Build” resulta exitoso, ahora podemos hacer “Run” (aleluya: si hay un ícono “Run”). Nuevamente tenemos que hacer click sobre el tab “Run” en el sector del UI de ventanas de outputs. ¿Eso no debería ser automático? Bien, ejecutamos el juego, el cambio que tratamos de hacer no funcionó así que tenemos que ir al editor, corregir y repetir el ciclo. ¿Saben qué? Nuevamente hay que hacer click en “app” en la ventana del proyecto porque nuevamente se deshabilitó, hacer click en la ventana de output de “Build”, y así horas y horas haciendo cosas inútiles como si no tuviéramos otras cosas que hacer. Hmm sí, AS tiene serios problemas de conducta.

Nota final: al publicar la aplicación en el Play Store (también conocido como Google Store), el Play Console me arrojó este error “Debes utilizar otro nombre de paquete porque “org.libsdl.app” ya existe en Google Play.”. En toda la explicación precedente hay que incluir que se debe colocar un nombre nuevo en Android Studio. Yo sabía eso hace 6 meses e intenté hacerlo, pero al parecer, a pesar de que el nombre “tilemovers” aparece en todas partes, desde el punto de vista muy personal de Android, la aplicación se llama “app”.

Ok respira profundo:

  1. Hay que modificar en res/values/string the string name=”app_name” y colocarle el nombre de la aplicación. Lo hice desde el comienzo, No es suficiente.
  2. Bueno etc. La solución (como explico aquí) es cambiar en build.gradle applicationId “org.libsdl.app” a applicationId “org.libsdl.tilemovers”

Nota sopotocientos: Si al publicar en Play Store o Google Play o como se llame te aparece este error: ” Tienes que usar un código de versión diferente para el APK o Android App Bundle porque ya cuentas con un archivo con el código de versión 1. “, tienes que corregir el valor en build.gradle(:app) versionCode 1 => versionCode 2 o el siguiente valor. También aparece en el manifiesto (AndroidManifest.xml) pero no tiene efecto (es decir, lo que cuenta es lo que aparece en build.gradle(:app) versionCode .)

Adios, me despido de ti

Mi cuaderno de notas está lleno de anotaciones sobre las peripecias con el manejador de bases de datos sqlite, pero ya eso es tema de otro post. Se cansa uno.

Me abro al cierre

30-12-2020 7:55 AM

La gente con poder lo tiene porque tú se lo diste.

¿Me conoces?

Hoy aprendí que el despiadado y siniestro ruso corrupto que aparece en Jack Reacher es Werner Herzog (!)

How lonely is the life of the solo dev (Qué solitaria es la vida del desarrollador de juegos que trabaja solo)

Bien lo dijo García Márquez: el inglés es compacto, le da fuerza al mensaje. Esa frase (“How lonely is the life of the solo dev “) pierde su pureza al traducirla al español. Como quiera que sea (“anyway“) , eso. Para ser solo dev hay que tener una psique especial, ser alguna especie de lobo estepario de la tecnología, olfateando el ambiente huyendo de los rebaños. Porque el rebaño lo aleja de su objetivo. Terminar “su proyecto que no forma parte de su trabajo 8am-5pm sino que es un proyecto personal” (“finish the side project“). Y en mi caso, doble, porque en este momento estoy trabajando el side project de mi side project. Uff. Me espera mucha soledad. Hola soledad!!

“Escribe código. No mucho. Principalmente funciones”

Este artículo hace analogía entre escribir código y comer. Interesante. Ambos satisfacen una necesidad y un deseo. ¿Por qué escribo código? Necesito hacerlo. Es como Picasso pintando. Necesita hacerlo.

Pero no es eso lo que me llama la atención del artículo, sino la explicación de esa recomendación. Es una paráfrasis de lo que Michael Pollan dijo sobre la comida: “Come comida. No mucha. Principalmente vegetales”, y la explicación de esto es: “… el valor de la comida es más una devoción religiosa y de moda a la mitología de las soluciones simples que una conclusión convincente y fiable de la investigación científica incontrovertible” maravillosamente equivocado pero acertadamente falso. Parece verdad pero es en realidad una colección de palabras colocadas una detrás de otra. Me pregunto si eso es el fenómeno que sucede en twitter. Para los que necesitamos comer y necesitamos programar, todo eso es un madejo de gamelote: necesito programar, ergo necesito comer. Punto. Para programar necesito hacer miles de millones de conexiones sinápticas en mis neuronas. Ese proceso consume electricidad. La electricidad la produce mi cuerpo. Mi cuerpo necesita comida, principalmente proteina.

Pero estoy de acuerdo en comer vegetales. Nuestro colon nos lo agradece todos los días. Y en relación a las funciones, una función más no hace daño. Toda función puede ser reescrita como una colección de llamadas a otras funciones (originalmente había escrito aquí “hay que escribir todas las funciones, ni una más ni una menos”. Dejo las dos opciones abiertas)

Enlace.

Decisiones… Todo cuenta

Puesto que realmente yo no trabajo en C++ sino en C + STL, hace sentido buscar alternativas a STL para trabajar en C más esa alternativa. Existen algunas pero pienso que quizás esta es la mejor, C TEMPLATE LIBRARY. El problema es que revisando el código de khpx yo utilizo algunas cosas adicionales de C++ que son cómodas. Estoy anotando aquí en un papelito hacer un post sobre qué cosas de C++ además de STL utilizo en khpx . Espero no perderlo.

Sin embargo, me parece una locura claudicar STL por una oscura y desconocida librería-header de github. Los comentarios en hacker news son favorables, pero. Eso, hay un “pero” ahí escondido en alguna parte. Decisiones… todo cuenta… cada día. Mejor no.

2020

Este año ha sido tan malo pero tan malo que cuando termine mañana todo el mundo va a decir “Por fin”. Este año ha sido tan malo pero tan malo que he estado pensando en cambiar el nombre de los niveves en tilemovers de “Normal, Difícil, Imposible” a “Normal, Difícil, 2020”. (tilemovers es un juego de tiles, el side project de mi side project).

Este año ha sido tan malo pero tan malo que cuando alguien cuente algo malo que le sucedió en su vida, la gente le va a preguntar: “ya va, eso fue en el 2020 o un año regular?”. De igual forma, este año ha sido tan malo pero tan malo que las películas de terror van a tener una aclaratoria al comienzo que diga “Los siguientes acontecimientos no ocurrieron en el 2020”.

Este año ha sido tan malo pero tan malo que todo el mundo va a sonreir y ser feliz el resto de sus vidas, no por cualquier razón sino porque sobrevivieron al 2020.

Vivir deliberadamente

30-11-2020 4:26 AM

Sí, este es un post de enlaces. Los posts sobre Android y SDL siguen en la cola. Ambos son de más de 2000 palabras así que la espera va a valer la pena.

Que el festival de enlaces comience!!!

Es igual pero diferente

Esta respuesta en stackoverflow sobre para qué sirve super() en python, nos deja pensando si realmente stackoverflow es capaz de responder adecuadamente una pregunta, o deliberadamente intenta que tengas que leer medio libro de La Guerra y la Paz antes de entender la condición humana ¿? No, no estoy interesado en este momento en entender la condición humana, solo quiero saber para qué sirve super() en python. Las respuestas son del estilo “un destornillador permite destornillar un tornillo”, lo cual te obliga a buscar qué significa “destornillar ” para descubrir que “destornillar es la acción de usar un destornillador”. Al final, resulta que super() es la forma de python lidiar con class inheritance, es decir, cómo facilitarte la vida cuando te quieres complicar la vida. No necesariamente vas a tener éxito, pero mira el lado positivo, al menos descubriste que no solamente las classes en C++ son inútiles y están implementadas torpemente, también python es igual. Pero diferente. python es como un carro donde el volante está en los pies, y el acelerador y el freno están en las manos. Se maneja igual, pero es diferente. Tú sabes, la innovación es más importante que la utilidad.

De juegos multijugador

Volví a abandonar World of Warcraft. Terminé basicamente la expansión (Battle for Azeroth) y en algún momento jugaré Shadowlands, que salió el 23 de noviembre. Lamentablemente, el juego tomó un curso deprimente, y al parecer la nueva expansión va a continuar igual. Ahora no se trata de maximizar el equipo para derrotar a los jefes máximos. Ahora la idea es alimentar el poder de tu equipo infinitamente, hacer misiones, etc. Ya no es divertido, quizás para mí luego de 12 años, ya se terminó.

12 años es nada. Hay personas que tienen 20 años jugando Muds, y todavía siguen haciéndolo. Yo abandoné el Mud que jugaba, BatMud hace 5 años y desde entonces he estado buscando una nueva casa. Jugué Carrions Fields, Zombiemud, Lands of Redemption, Darkmud, Achaea, Alter Aeon, Genesis y ahora estoy probando nuevamente Sindome. Ya lo jugué sin éxito en 2016, y luego de leer este artículo, me animé a darle una nueva oportunidad. Es diferente a cualquier cosa que haya jugado antes, por ejemplo, la mayoría de los comandos son diferentes, por ejemplo, si ves un panfleto, “leer panfleto” es suficiente en cualquier mud, pero no, en Sindome hay que hacer “leer [pagina] de panfleto” donde [pagina] es primera, última, o un número. Pero los panfletos solamentienen una página, así que nada de eso funciona. Un mini puzzle que me tomó un buen tiempo resolver.

Qué es un Roguelike

Interesante artículo sobre la respuesta a esta pregunta (por supuesto, según el autor, es su opinión). Para mi es tan simple como cualquier juego que tenga los elementos del Rogue original. Si tiene graficos como Dungeons of Dredmor no es un roguelike, aunque apliquen reglas similares y el movimiento es 2d. Yo diría que es un juego con reglas similares a los roguelike. meh.

What stands in the way becomes the way — Marco Aurelio

Hay quienes se resisten a ver la realidad. Lo hacen voluntariamente, conscientemente, sin darse cuenta, intencionalmente, deliberadamente, con alegría, con tristeza, con obsecuencia, calladamente, a gritos o en una combinación de todo lo anterior. También hay quienes hacen papel de camello.

Resulta que los camellos no son iguales a los caballos. Los caballos dan por sentado su destino de servir al hombre, y de la misma forma que los perros, son sumisos e incapaces de levantar una pata o un colmillo contra sus amos. Los camellos por el contrario, aceptan su rol a regañadientes, y solo con la condición de ser tratados con gentileza y con una buena porción de comida. Pero, a veces, pierden la paciencia. Se enojan con su amo y comienzan a lanzar mordiscos y patadas. El camellero, que está muy consciente de esta posibilidad, está preparado: se quita la camisa y se la lanza al camello. Éste paga su rabia con la camisa, mordiendola y pateándola repetidamente. Al cabo de un rato, se cansa, se apacigua, y vuelve al trabajo mirando de reojo al camellero.

Pues bien, hay quienes se comportan como los camellos, desatando su ira hacia un objeto cualquiera, sin percatarse que lo que hacen es perder el tiempo, mientras el culpable se esconde, y los mira desde lejos, esperando a que regrese a su sumisión.

Wordplay

Yo estuve trabajando durante 5 años en Business Intelligence (BI) (hace más de una década), lo cual me permitió adquirir destrezas en el área de minería de datos, y sistema analíticos. BI forma parte de las herramientas de optimización de negocios.

Estaba viendo por casualidad la presentación de esta compañia (en una publicidad antes de ver un video en youtube) donde muestran como hacer data mining con python (tengo algunas historias sobre python, pero eso será contenido de otro post). Lo que me llamó la atención es el cambio de nombres, y nuevos nombres de las diferentes tecnologías: en esa presentación se habla de data wrangling (que llamábamos ETL, extracción, transformación y carga de datos), data science (BI lo podríamos catalogar entonces como un subconjunto de data science), cluster analisis, etc. Así, data science incorpora los nuevos desarrollos en machine learning y big data. Interesante.

De prestar atención

Este artículo trata (no necesariamente lo logra) que le prestemos atención a nuestra capacidad de prestar atención. Si no prestamos atención todo está perdido, lo cual parece intuitivamente correcto. Hay una claudicación en ese artículo: si no puedes prestarle atención a algo es mejor rendirse. No todo el mundo estuvo de acuerdo con este planteamiento, sobretodo los que tienen serios problemas para prestarle atención a algo: si te rindes, jamás vas a poder avanzar un proyecto.

El autor utiliza como técnica la clasificación de Eisenhower: urgente e importante y todas las combinaciones posibles de las dos. A mi, personalmente, eso me recuerda a hacer listas y colocarle prioridades. Lo cual no funciona para mi. Al final, todo es urgente e importante, esas clasificaciones son artificiales y dejan de funcionar, eventualmente. Las artividades tienen un ordenamiento lógico: más allá de eso, lo único que hay que hacer es trabajar. Llámalo como quieras: prestarle atención o no, procrastinación, pereza: ¡trabaja!

Vivir deliberadamente

He estado pensando en cambiar mi motto: “vivir sin virtudes aparentes“, y cambiarlo por deliberadamente: con pasión: no se me ocurre otra forma de vivir. En la película “Automata”, uno de los robots le responde al asesino: “¿soy sólo un robot? Eso es como decir que tú eres solo un mono”. Los idiotas de 9gag dicen que eso es un mal argumento, “somos solamente unos monos”, lo cual me hace pensar que por algo son idiotas: no entendieron el razonamiento de un robot (o el que escribió el guión).

No somos simplemente monos: somos capaces de vivir con pasión, cosa que ningún mono sabe hacer. ¡Los monos comen bananas, 9gag!

Cuando las aguas dejan de moverse

08-09-2020 6:57 AM

Este es un post explicativo. Es del tipo “¿qué está pasando?” (“what’s going on?”). ¿Por qué no hubo post en agosto? ¿Cómo salir de la pandemia vivo y coleando? ¿Es este el fin del mundo? ¿Debemos odiar a amazon por ser tan exitoso?

  1. Si hay un post de agosto, todavía no lo he publicado porque describe el procedimiento que seguí para publicar un juego en Play Store. Todavía no está listo. Espero publicarlo este mes. Para ello saqué un juego al azar de mi baúl de zombies (tilemovers) y lo reimplementé en SDL2. Está listo también hay una versión para pc lo voy a publicar itch.io.
  2. Coronavirus. El sentimiento generalizado es que no vale la pena cuidarse tanto porque ha habido personas que después de 6 meses de estricto aislamiento y bañarse en alcohol todos los días, se descuidan un día y se contagian. Si todos nos vamos a contagiar, ¿para qué tanta paranoia? La realidad es que todos los días existe una posibilidad de 1/1.000.000 que te caiga un piano en la cabeza. Repentinamente, ahora la posibilidad es 1/34. Yo no sé usted pero yo al menos comenzaría a mirar para arriba solo para estar seguro.
  3. Este no es el fin del mundo. El cambio climático es el fin del mundo tal como lo conocemos. La pandemia va a terminar. Nos tocó vivirla. Otras generaciones se salvaron, pero les tocaron otros retos. Cada generación tiene sus retos. La nuestra quizás se dedica más a llorar sobre platos rotos, lo cual siempre ha sido una pérdida de tiempo.
  4. Yo no tengo nada contra Amazon ni contra Bezos, y él no está obligado a retribuir sus ganancias a la comunidad. Depende de la comunidad que continúe haciendo negocios con él, lo cual parece que va a continuar por mucho tiempo.

Una explicación inútil

12-07-2020 8:18 AM

“There is a different between live intensely and live with passion” (“Hay una diferencia entre vivir intensamente y vivir con pasión”)

Itzhak Perlman



Ok, yo no sé qué significa eso, pero ¿qué tal ambas cosas? Cuando oí esa afirmación (Itzhak Perlman es una violinista consagrado, así que él sabe de intensidad y de pasión) lo primero que pensé fue “no sabía que ambas cosas eran mutuamente excluyentes”. Yo siempre he vivido intensamente como un funámbulo y con pasión como un violinista. Así que agárrame esa ballena por el chorrito, Itzhak!

De “Dark” y Hitler

Advertencia: no hay realmente spoilers en lo que viene, pero los comentarios pueden sugerir algunos elementos que forman parte de la trama. Quizás sea buena idea que termine de ver la serie y regrese. Suerte con eso.

La serie de Netflix “Dark” ha causado revuelo. Primero, porque no se entiende. Segundo, porque cualquier especulación sobre lo que está sucediendo genera polémica. Por ejemplo esto:

“hicieron lo correcto”

El problema, sin ahondar mucho si la serie tiene la intención oculta de sugerir una respuesta a ese dilema, o a esa decisión, es que, si algo hemos aprendido con todas las experiencias en “Volver al futuro” 1, 2 y 3, “El vengador del futuro” (“Total Recall”) el capítulo “Al borde de la eternidad” de “Viaje a las Estrellas” y otras películas similares con viajes en el tiempo, es que esos personajes de “Dark” hicieron lo correcto al tener sexo con sus tías y no asesinar a Hitler, porque el resultado hubiera sido el mismo, o peor.

Me explico.

La cuestión fundamental es si la segunda guerra mundial era evitable. Mi teoría es que no. Muerto Hitler, cualquier otro canciller alemán, u otro líder cualquiera hubiera tomado similares decisiones, dadas las malas condiciones en que Alemania terminó después de la 1ra guerra. Además, haber perdido territorios era algo inaceptable en aquéllos tiempos, y otra cantidad de razones que ya todos conocemos. Así que Alemania hubiera ido a la guerra de todas maneras. La pregunta es qué hubiera pasado si Alemania no ataca en la forma que lo hizo a Rusia, sino que comienza a ganar tiempo, a fortalecer los territorios conquistados, a prepararse para el ataque, desistir de la solución final con los judios, etc.. Rusia quizás no hubiera atacado, y tendríamos hoy en día una Europa alemana. Alemania perdió la segunda guerra mundial por la tonta decisión de Hitler de invadir Rusia en las peores condiciones cuando todavía no había consolidado su poder sobre Inglaterra y continuaba enfrascada en resolver el tema de los judíos. Con un líder diferente Alemania quizás hubiera tenido éxito en sus loqueras, e inclusive, quizás una política exterior más razonable. Claro, buena parte de Europa sería territorio alemán y hoy en día sería una potencia mundial (tal como lo es de todas formas). Así que asesinar a Hitler probablemente no hubiera resuelto nada, o quizás hubiera sido peor.

C++ sin clases

06-06-2020 5:06 AM



Ventaja oculta de no usar c++ clases: cuando agregas una nueva función no tienes que registrarla en la clase, la escribes, la usas y ya.

Prometo que este va a ser mi último post “yo uso C++ pero no uso clases“. Quiero concentrarme en el tema de la complejidad en si misma y olvidar otros temas. No he querido buscar en wikipedia como se llama el síndrome de los que piensan “por qué hacerlo sencillo si lo podemos hacer complicado“, estoy seguro que este síndrome tiene un nombre. Bueno, yo sufro de ese síndrome. Por ejemplo, cuando abandoné mi juego psyblast (para los que nos visitan por primera vez yo tengo una enorme colección de juegos inconclusos, es parte de mi hobbie, comenzar juegos y no terminarlos, es divertido sugiero intentarlo) decidí que mi próximo juego sería algo simple, muy simple, un juego en el espacio, donde el jugador tiene una consola donde pueda ver diversos medidores, y el juego sea simplemente tratar de viajar lo más lejos optimizando el uso de combustible, oxigeno, hidrógeno, etc.

5 años después esto se convirtió en khpx, un juego de estrategia con elementos del género 4x (eXploración, eXpansión, eXplotación, eXterminio) (pero no es un juego 4x) donde el jugador puede practicar la minería, la cosecha, desarrollo de tecnología, y en general desarrollo y dominio de recursos. Y sigue y sigue complicándose hasta el infinito. Así que todos los días mi motto es “cómo hago para simplificar las cosas” o “qué puedo simplificar hoy”.

Como mi compulsión me impide simplificar más allá de cierto nivel el juego, apunto mis baterías a C++. ¿Qué más puedo simplificar? No mucho pero contener mi impulso natural a usar clases ya es un comienzo.

Entonces cayó en mis manos este software, recientemente liberado por EA, el código fuente de CnC(Command & Conquest). C++. Clases. Muchas. Ví archivos muy grandes, vi un caos en la nomenclatura de los archivos, los nombres de clases, variables, métodos, todo. Y sentí un alivio porque así es khpx: un caos de nomenclatura donde hay funciones_con_barra_de_abajo, funcionesConNotacionCamel, variables_con_la_funcionalidad_al_comienzo y demás cosas similares.

CnC tiene un DISPLAY.CPP (de 190k) y un DISPLAY.H de 13k (en khpx el archivo más grande tiene 40k y es mucho) Ví cosas como DisplayClass::TacticalClass::Command_Object (..), y mezclado con esto, muchas funciones static funcion_no_en_una_clase_y_es_libre (…). Lo cual me lleva a pensar cuál es el punto. Sin embargo, el código se deja leer, el nivel de espaguetismo es minimo (khpx es un espagueti de pasticho hecho con pasta corta). Ahora, en el código de CnC seguir el camino de las clases esa es otra historia: entre algo->subalgo (..) mezclado con OtroAlgoSolo(…) entender quién llama a quién desciende a un nivel de complejidad natural, porque está implementando un juego naturalmente complejo, complicado por la complejidad de la capa de clases mezclada con funciones estáticas imposibles de meter en clases porque son libres como el viento. La idea de las clases es organizar la complejidad, a veces con éxito, a veces agregando una nueva capa de complejidad a la complejidad para que la complejidad subyacente sea invisible, aunque nosotros sabemos que está ahí, e inevitablemente nos devora como arenas movedizas.

Es decir, buen intento, pero las clases en sistemas complejos fracasan en hacer amigable la complejidad, simplemente la ocultan y la hacen más compleja a niveles inmanejables.

Este nivel de complejidad innecesario y a veces inútil (estas palabras no son sinónimos), es como el síndrome de willicoyotismo (algo que acabo de inventar) Willie el Coyote quiere atrapar al correcaminos (en la vida real un coyote alcanza en una carrera a un correcaminos en cuestión de 30 segundos) pero utiliza catapultas, cuerdas atadas a un risco, y se coloca un casco con una rueda arriba, y patas para arriba se lanza sobre la cuerda corriendo con la rueda que tiene en el casco como un funicular invertido. También arroja misiles, bombas, tumba puentes etc. Es lo mismo que utilizar los artefactos alrededor de las classes de OOP. ¿Ejemplos? Bueno este señor quería calcular la desviación estándar para las mediciones de un sensor. Lo que hizo fue tener una clase Sensor y una clase Medida, y cada vez que quería calcular la desviación estándar, convertía los valores tipo double a Medidas, los cargaba en un Sensor, llamado “CalculateStDev”, y tomaba la propiedad “CurrentStdDev” del Sensor. ¿Ok? ¿Cuál es la diferencia entre eso y meterse en un cañon como el hombre bala, activar el disparador calculando que la trayectoria dé exactamente en el sitio donde el correcaminos va corriendo por una carretera cualquiera a una velocidad de 24 km por hora ? Yo no veo la diferencia honestamente.

Como un ejemplo adicional, este señor muestra como OOP es inconveniente en algunos casos (realmente todos) y cómo una solución escrita con clases puede ser escrita fácilmente con una simple función. En realidad, cuando resolvemos un problema, es en el problema en que debemos estar pensando, no en cómo usamos nuestro lanzamisiles de última generación para resolver el problema. Lo cual es la principal razón por la que dejé de usar clases. Pierdo mucho tiempo pensando en cómo emplear sus patterns para resolver problemas, en vez de simplemente resolver el problema.

Esta pérdida de perspectiva termina desvirtuando “lo que tienes que hacer” de maneras incomprensibles. Es como ciertos gerentes de proyecto que manejan los proyectos usando el software, viendo las actividades en gráficos y los tickets cambiando de estatus. Quizás ves algunas cosas pero no el conjunto y sus interacciones, y tus acciones y rabietas terminan siendo absurdas. Los demás te miran desde el otro lado de la mesa con perplejidad: ¿qué le pasa?.

Crimen y Netflix

26-05-2020 6:35 PM

La Guerra de las Galaxias es una saga de 6 películas realizadas por el maestro de la ciencia ficción George Lucas. Luego de vender la franquicia a Disney, esta hizo una serie de pésimas películas (excepto quizás Rogue One) que trataron de exterminar y eliminar todos los elementos previamente construidos en las 6 películas anteriores. Este proceso se llevó al extremo de que la película conocida como The Last Jedi destruye elementos de la nueva serie, donde, sin razón alguna, el villano es muerto prematuramente.

Este proceso de exterminio de los elementos básicos de una franquicia es algo común en Hollywood y no es una práctica exclusiva de corporaciones como Disney. Algo similar podemos apreciar en Misión Imposible, donde en la primera película el personaje central de la serie, el Sr. Phelps es presentado como un villano corrupto, comportamiento absurdo en base al desarrollo que tiene este personaje en la serie original. En la serie, Jim Phelps participa en operaciones encubiertas diseñadas e implementadas por él, de un nivel excepcional, sin fallas ni errores. En la película es presentando como un villano de poca monta, torpe y descuidado que desciende fácilmente en la confrontación física, cosa que el personaje original jamás haría. Los actores de la serie Peter Graves y otros protestaron esta línea argumentativa pero los productores de la película estaban decididos a eliminar cualquier vestigio de los héroes originales y crear su propia mitología.

Lo que Disney le hizo a La guerra de las galaxias es un crimen.

Acabo de terminar Community, una de las mejores series que he visto en Netflix últimamente. Realmente me gustan las series (y las películas) innovadoras, rebeldes, que cambian paradigmas, que dan de qué pensar. Community utiliza todas las técnicas posibles para desconcertar el espectador, incluyendo capítulos donde todos los personajes comienzan una guerra de paintball sin razón, los personajes hablan fuera del personaje rompiendo la 4ta pared, vemos múltiples timelines con diferentes resultados si cambiamos un pequeño elemento, etc.. Todas las 6 temporadas son buenas, y en mi opinión todos los capítulos son excepcionales. Algunas personas en imdb dicen que la calidad desmejoró después de la temporada 3 y que la 5 y las 6 son malas. A mi me parece que la temporada 6 fue tan buena como las 1-3, de hecho el episodio t6/e8 tiene uno de los mejores finales de episodio de todos los episodios de todas las series que he visto (estoy exagerando), cuando después de que Chang tiene su aventura donde casi (realmente casi) llegó a ser una estrella tipo Steven Spelberg, regresa a la mesa cuadrada, se sienta y todos continúan en lo suyo, sin recriminarle nada, aceptándolo de vuelta, y él, los mira a todos y se da cuenta que sus temores de ser rechazado eran injustificados y se da cuenta que es uno de ellos, que es definitivamente miembro de la mesa cuadrada. Altamente recomendable.

Bullet Head es un nuevo ejemplo de la manía de Hollywood de colocarle malos nombres a las películas. Esta es una excelente película con brillantes actuaciones de Adrien Brody, John Malkovich y un sorprendente Antonio Banderas haciendo papel de malo. Inclusive Rory Culkin el hermano menor de Macaulay Culkin, hace una magnífica actuación a pesar de no tener tanta experiencia como sus compañeros en la película. ¿Entonces, si el casting y la película son tan buenos por qué en imdb el rating es 5.4 (yo esperaba al menos un 7.8) ? De acuerdo a este comentario, la razón de los abrumadores malos reviews es que la gente estaba esperando una película de acción. ¿Por qué, si ninguno de los actores es conocido como un actor de películas de acción (Brody hizo Depredadores, que puede ser una película de acción)? Mi opinión es que se debe al título (Bullet Head) que sugiere una película repleta de balas al estilo Tarantino. De hecho, en el faq de la página de imdb alguien pregunta por qué esta película se llama así, y no hay respuesta. Mi mejor ejemplo de películas con nombres inapropiados es Coming Home, la película de Jane Fonda, nombre que no dice nada (el nombre en español es “Regreso sin gloria“, que es simplemente perfecto). ¿Otro ejemplo?, “Out of Africa”, la hermosa película de Meryl Streep, que se podría traducir por “en Africa” o “Por los lados de Africa”. Un nombre que sugiere movimiento. El nombre en español es mucho más apropiado “Africa mía” que refleja profundamente el sentimiento de cariño y nostalgia que le generó a la protagonista su estadía en Africa.

Code smell

08-05-2020 6:39 AM

una galaxia
la capsula viajera
sin tiempo

Suena sospechoso pero en realidad se refiere a código que no corresponde a las mejores prácticas de programación. Por ejemplo código que luce correcto pero que seguramente va a generar problemas en algún momento. Llegó a mis manos un artículo de medium sobre el tema (que ahora no encuentro… ah, ya, aquí está) así que comencé a buscar en el código de khpx algo similar. Hay mucho data clump, porque es un juego y hay mucho control de estados, no he hecho nada al respecto porque lo estoy dejando para el final. Agrupar esto en objetos en este momento puede ser precipitado y optimización temprana. Muchos de estos problemas son relativos a classes y como yo no uso clases, pues simplemente no tengo esos problemas.

Un ejemplo de code smell es este:

void inocente_funcion (int importante_argumento, bool soy_code_smell)
{
   mucha_inicializacion();
   y_preparacion_de_datos();
   if (soy_code_smell) {
        hacer1 ();
   }
   hacer2 ();
}

Esto hace que la función modifique su comportamiento basado en argumentos booleanos. hacer1 () debería estar afuera.

El problema es que a veces hay situaciones en que la forma fácil, rápida y obvia de hacer algo entra en conflicto con esta regla de no programar con code smell’s. Por ejemplo en la inocente función de arriba se hace mucha inicialización y preparación de datos para luego ejecutar a veces a hacer1() y siempre a hacer2(). Pero supongamos que hacer1() es básicamente despliegue de los datos en pantalla. Así que la variable booleana soy_code_smell es en realidad un flag verbose, para desplegar información algunas veces. La solución es separar todo en funciones y hacer las llamadas apropiadas pero entonces caemos en código espagueti y repetición de código.

khpx tiene mucho código espagueti porque un juego es un espaqueti mezclado con ensalada rusa, y más aun cuando el juego en sí es un trabalengua. Claro, no llega al nivel de la primera versión de Zork, pero estudiando su código me pregunto cómo simplificar algo complicado.

¿Cual es la descripción corta actual de khpx?

khpx, a hard core low level full featured pixel art solo dev mmo space game (un juego espacial de bajo nivel con características avanzadas, graficos de pixels, desarrollado en solitario, masivamente multijugador).

O algo así.

¿Los bugs del juego se pueden explicar debido a estos code smell? Realmente no. Los bugs aparecen debido a la complejidad. Yo adopto un enfoque tipo Abed a los problemas: si lo pienso lo suficiente y me concentro cualquier problema se puede resolver. Los académicos de manera condescendiente moverán la cabeza diciendo que detrás de todo bug hay un code smell escondido, o falta de OOP, o pobre diseño de software, o problemas no resueltos, simples problemas manejados incorrectamente.

Ok. Veamos.

Los cultivos en los planetas está implementado. La función que se encarga de eliminar una parcela de la lista de parcelas cultivadas tiene 12 llamadas a funciones:

  1. convertir la parcela de cultivo a parcela vacia (cambio del ícono)
  2. borrar el rectángulo que resalta a la parcela en el campo de cultivo
  3. si tiene plagas, borrar la plaga
  4. remover el identificador de parcela
  5. remover el indicador de progreso de la parcela (nivel de cosecha)
  6. guardar el estado de los iconos a la base de datos
  7. borrar la parcela de la lista de parcelas del jugador
  8. cargar el script de parcelas
  9. actualizar usando la info del script, la lista de parcelas disponibles
  10. guardar la lista en la base de datos
  11. desplegar el resultado de la parcela en la pantalla
  12. desplegar un mensaje de “Listo”.

El manejo de cultivos requirió un mes de trabajo (8 horas diarias la pandemia de covid-19 ha reducido mi capacidad de enfocarme, lo cual no es raro).

Hay dos directrices importantes-urgentes-prioritarias que entran en conflicto: escribir código de calidad (sin code smells, OO, buen diseño) y terminar el juego. El trabajo de un programador es encontrar el equilibrio entre estas dos reglas en permanente pugna. Sin excusas o coartadas: es realmente deplorable echarle la culpa a una de estas reglas el no haber cumplido con la otra.

O, revisamos nuestra lista de “o”s:

  • O regreso a trabajar tiempo completo freelancer.
  • O me dedico a programar juegos en androide tipo mario bro para ser publicados semanalmente y hago de mi vida un infierno.
  • O me dedico a escribir ebooks y publicitarlos en twitter

Me imagino lo que Bobby Axelrod pensaría de todas estas opciones.