@ agnasg

agnasg


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 .)

Cómo Actualizar (crear un nuevo Release) la aplicación (actualizado 14-07-24)

Al entrar en play console y seleccionar la aplicación (se debe mostrar el nombre y logo de la aplicación en la esquina superior derecha), aparece al lado izquierdo un sidemenu, se debe seleccionar Production y luego presionar “Crear un nuevo Release” ( Create New Release), subir la aplicación en formato Google Bundle, por ejemplo app.release.aab (no en un formato apk), colocar la descripción del nuevo release y seguir las instrucciones (Next, Save, Send to review).

09-07-2024: Para generar el bundle en Android Studio se debe hacer Build => Generate Signed Bundle / APK.

El archivo para hacer el upload al play console está en tilemovers\app\release. Sustituya tilemovers por el nombre de su aplicación.

También puede estar en tilemovers\app\outputs\bundle\release todavía no he podido descubrir la diferencia.

09-07-2024: Nuevo error: Your Android App Bundle is signed with the wrong key. Ensure that your app bundle is signed with the correct signing key and try again

Si yo no he hecho nada sino actualizar el úmero de versión esto no debería pasar. Vi en Google que hay que hacer Build ->Rebuild Project y se debe resolver. A mi me funcionó pero dejé a un gentío en google diciendo que eso no funciona. Pobrecitos.

09-07-2024: Nuevo error: You uploaded an APK or Android App Bundle which has an activity, activity alias, service or broadcast receiver with intent filter, but without ‘android:exported’ property set

Para resolver esto, hay que ir a AndroidManifest.xml, buscar <activity android:name=”SDLActivity”> y en esa subseccion agregar al final

android:exported="true"

Por ejemplo, en esa subsección, aparece al final (en mi caso): android:screenOrientation="portrait" 

Hay que agregarlo en la línea siguiente. Eso debe resolver el problema (mucha suerte)

24-07-24: A pesar de todos los cambios y actualizaciones tratando de satisfacer el apetito de la play console, sigue apareciendo este mensaje:

We will soon take action because your app does not adhere to Google Play Developer Program policies. 
[Pronto tomaremos medidas porque tu aplicación no cumple las políticas del programa de desarrolladores de Google Play.]

Esto se puede deber a que tienes una versión en alguna parte (producción o testing) que no has actualizado a la última version del API (hoy es 34). La respuesta es que no hay forma de remover este mensaje hasta que el alto concilio de Trento, el Sacro Imperio Google y demás instancias apropiadas lo decreten. Tanto en los foros de Google como en Stackoverflow se indica que no hay forma de hacer "delete" a las versiones viejas, solo se indica que puedes deshabilitar el "track" (a veces tampoco se puede deshabilitar el "track", yo no puedo hacerlo).

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. Corre en windows y android!).

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!