@ agnasg

agnasg


De todo un poco neurodivergente

18-03-2023 5:42 AM

Change my mind

La vida de un programador es una misión Apollo XIII permanente. Resolviendo problemas desconocidos, inesperados, absurdos, todo el tiempo.

Ejemplo: estoy trabajando con un sistema reservaciones de vuelos, el orígen está en mayúscula, el destino debe estar en minúscula, pero algunas veces está solamente la primera letra en mayúscula. Explícale al jefe del proyecto que el orígen y el destino deben ser introducidos a mano en una tabla de equivalencias. Y este problema es el número 1729 en una interminable retahila de problemas.

Cuando te expulsan de un subreddit

Me expulsaron de /r/antivirus cuando me burlaba del troll local. No se si sentirme orgulloso o realizado. En todo caso, siento tristeza por el troll. Debe sentirse vacío y sin propósito. Debe estar suplicándole a los moderadores que me dejen entrar de nuevo. Ups, giro inesperado en la historia: él es un moderador. O no.

Números

Estaba leyendo algo aburrido este artículo sobre números aburridos y números interesantes, y me llenó de curiosidad lo que dice en relación al número 1729: es un número interesante porque “es el menor número expresable como suma de dos cubos de dos formas diferentes”. ¿Qué significa eso? El artículo trae la referencia a un libro del autor de la afirmación, Srinivasa Ramanujan, pero leer un libro 200+ páginas para buscar la cita con la esperanza de encontrar la explicación me resultó aburrido así que me puse a programar un buscador de números interesantes para confirmar eso. Efectivamente existen dos números 729 y 1000 (ambos cubos) suman 1729 y 1 + 1728 (123 == 1728) también suma 1729 (doh) . Pero con mi programa hice un descubrimiento interesante: 4104 es la suma de 729 + 3375 y 4096 + 8. Hay resúmenes de números que son aburridos y números interesantes (está mencionado en el artículo) pero a mi más interesante me resulta descubrirlo por mi cuenta.

¿Cuál es mi número favorito? 2.7182818284! En segundo lugar está 142857 (hay un post sobre él aquí en alguna parte).

Edit: sí leí el libro de Ramanujan (rápidamente), 1729 = 13 + 123 = 93 + 103. Ver también 1729, en Wikipedia. Este es un ejemplo más de por qué hacer las cosas fáciles si las podemos hacer difíciles.

El baúl de zombies está cerrado hasta nuevo aviso

He estado acariciando la idea de hacer una versión gráfica del juego Zork I/Dungeon, que permita hacer el recorrido pero con la ayuda de un mapa. Para los que no han jugado este tipo de juegos, es completamente texto, el juego te indica dónde estás y qué hay en el sitio, todo está descrito en palabras, sin gráficos. Luego de que has avanzado algo en el laberinto de lugares, por supuesto que te vas a perder, así que necesitas un mapa. Hay una larga serie de mapas disponibles, que la gente ha estado haciendo a lo largo de los años: Zarf Updates: A treasury of Zork maps (discusión en HN) tiene una buena colección de estos mapas. Mi idea es para facilitar las cosas agregarle un mapa al juego para no tener que hacer uno o consultar uno en línea. Yo no se si voy a implementar esto, mi vida de freelancer y khpx me mantienen muy ocupado, pero aquí dejo los enlaces relevantes, por si nunca concreto nada (lo que lo convertíría a un candidato frustrado al baúl de los zombies):

Normal no existe

Soñe que fui a un psicólogo quien intentó hacer un diagnóstico de mi condición. “¿Cuál condición?” le pregunté. “Hay personas que creen ser Julio César o Alejandro Magno. Tú crees ser una persona normal”. Me recordó cuando le dije a una amiga “Yo soy medio autista”. “¿Medio?” me contestó. Como quiera que sea, me pareció una buena idea para un cuento. “El hombre que creía ser normal”. El giro inesperado es uno de esos personajes que hablan grandilocuentemente: “el concepto de normal no aplica a las personas”. Con lo cual estoy de acuerdo, somos tan diferentes entre nosotros que es ridículo usar el término normal para describir a una persona, como si fuéramos una curva producida con una fórmula matemática. Aunque entiendo que no soy la persona apropiada para hablar de esto. Correcto, por eso mejor escribo un cuento. Se cansa uno.

Matrices & parábolas

25-02-2023 7:08 AM

chatGPT

Para ser honesto, chatGPT me tiene bien sorprendido, pero en realidad, si lo piensas bien, no me debería sorprender, no nos debería sorprender. La tecnología está evolucionando, abriendo nuevos caminos, logrando nuevos objetivos, todos los días. Y acercándonos al futuro: chatGPT ya está realmente cerca de la computadora abordo de la nave Enterprise en Star Trek y de Mother, en la nave Nostromo en la película Aliens. Una computadora amorosa que con paciencia infinita responde todas nuestras preguntas.

Pero siempre aparecen niños malcriados que le hacen la vida imposible a su mamá:

Chistes aparte, chatGPT puede responder cualquier cosa, siempre y cuando la respuesta exista en internet. Pero no nos ilusionemos: chatGPT no piensa, no deduce nada: es una matriz con una fórmula, que recibe una entrada y produce una salida, eso es todo. Es básicamente el mismo concepto de las redes neurales de hace 20 años, pero con matrices más sofisticadas y nuevas fórmulas. Nada más.

Se me presentó un problema bien difícil de resolver en estas semanas y no utilicé chatGPT para resolverlo. Mientras mi cerebro siga funcionando espero no necesitarla, Mother.

De matrices y parábolas

Y hablando de matrices, mis antíguas contendientes. Desde álgebra II se han encargado de hacerme la vida imposible. ¿Qué tengo yo que ver con matrices en estos días, más allá de las matrices de chatGPT ? Pues estoy programando un dungeon en khpx, y una de las fases es una batalla espacial, que en khpx es algo parecido a una batalla en Galaga o Galaxian (jugarlo en archive.org), juegos desconocidos hoy en día, provenientes de los albores de la computación cuando los juegos se jugaban en salas de juegos en cónsolas.

Es algo así como esto:

Los movimientos de las naves en este tipo de batallas lucen bien divertidos pero no necesariamente son algo fácil de implementar. Hay movimientos circulares que son fáciles de simular con la formula:

ship->lp.x = orig.x + ship->altitud.x * sin(ship->ang.x);
ship->lp.y = orig.y + ship->altitud.y * cos(ship->ang.x);

Pero otros movimientos son más complejos. En general, como aprendemos en 4to grado los cometas se mueven alrededor del sol en un movimiento parabólico, llamado así porque la curva que describe el cometa es una parábola:

Lo que me llevó regresar a mis notas sobre cómo simular un movimiento parabólico, a ecuaciones con senos y cosenos y finalmente a matrices, a Cramer, etc. Haciendo corto el cuento largo, al final el código quedó parecido a algo como lo siguiente:

void getParabolaFormula(D3DXVECTOR2 p1, D3DXVECTOR2 p2, D3DXVECTOR2 p3, D3DXVECTOR3& result)
{
	D3DXMATRIX m0(
		1.0f, 1.0f, 1.0f, 0.0f,
		4.0f, 2.0f, 1.0f, 0.0f,
		1.0f, -1.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 0.0f);

	// fill the data using the parabola formula
	// y = a*x^2+b*x+c

	D3DXMATRIX m(
		p1.x*p1.x, p1.x, 1.0f, 0.0f,
		p2.x*p2.x, p2.x, 1.0f, 0.0f,
		p3.x*p3.x, p3.x, 1.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 0.0f);

	D3DXVECTOR3 r(p1.y, p2.y, p3.y);

	// calculate the determinant
	// D3DXMatrixDeterminant and  D3DXMatrixInverse doesn't work out the box
	// d = a(ei-fh)-b(di-fg)+c(dh.eg)
	float d = m._11 * (m._22 * m._33 - m._23 * m._32) -
		m._12 * (m._21 * m._33 - m._23 * m._31) +
		m._13 * (m._21 * m._32 - m._22 * m._31);
	g_write_debug("determinant %6.2f ", d);

	D3DXMATRIX Da(m), Db(m), Dc(m);

	Da._11 = r.x; Da._21 = r.y; Da._31 = r.z;
	Db._12 = r.x; Db._22 = r.y; Db._31 = r.z;
	Dc._13 = r.x; Dc._23 = r.y; Dc._33 = r.z;

	float da, db, dc;

	da = Da._11 * (Da._22 * Da._33 - Da._23 * Da._32) -
		Da._12 * (Da._21 * Da._33 - Da._23 * Da._31) +
		Da._13 * (Da._21 * Da._32 - Da._22 * Da._31);


	db = Db._11 * (Db._22 * Db._33 - Db._23 * Db._32) -
		Db._12 * (Db._21 * Db._33 - Db._23 * Db._31) +
		Db._13 * (Db._21 * Db._32 - Db._22 * Db._31);

	dc = Dc._11 * (Dc._22 * Dc._33 - Dc._23 * Dc._32) -
		Dc._12 * (Dc._21 * Dc._33 - Dc._23 * Dc._31) +
		Dc._13 * (Dc._21 * Dc._32 - Dc._22 * Dc._31);

	result.x = da / d; result.y = db / d; result.z = dc / d;


}

Esta función recibe 3 puntos que se encuentran en una parábola y devuelve la fórmula de la parábola. Se supone que esto debe devolver a, b, y c para resolver la siguiente fórmula:

y = a x2 + b x + c

Pero no funcionó. Pasé varios días tratando de encontrar el problema pero no lo logré. He pensado que el problema no está en estas matrices, sino en el código que utiliza la función. Como ya tengo años en khpx y no puedo seguir dedicándole tiempo a cosas que no tienen nada que ver con programación de juegos (falso, todo tiene que ver con programación de juegos) empecé a buscar otra solución.

En realidad yo no necesito tres puntos o mejor dicho, yo no tengo tres puntos, yo tengo dos puntos, el punto donde está la nave enemiga y el punto donde está khpx. Y el punto donde está khpx, es el vértice de la parábola (el sol, en nuestro ejemplo del cometa y el sol):

Esta vez la ecuación es mucho pero mucho pero mucho más sencilla y no necesitamos odiosas matrices. Mi cuaderno terminó lleno de fórmulas con ejemplos:

Pero el resultado fue bien siemple, tan simple como la siguiente función:

void getParabolaFormula2(D3DXVECTOR2 p, D3DXVECTOR2 vertex, D3DXVECTOR3& result)
{
	float h = vertex.x;
	float k = vertex.y;
	float a = (p.y - k) / POW2(p.x - h);
	result.x = a;
	result.y = -2 * a * h;
	result.z = a * h * h + k;
}

Esta función recibe los dos puntos y devuelve a, b c como en el caso anterior para resolver la ecuación de la curva. Lo cual me permitió implementar lo siguiente:

Como un primer borrador no está tan mal. Le faltan los misiles de khpx, la coraza protectora (o campo de lasers), las secuencias de destrucción de las naves, y un largo etc. Espero terminarlo en dos semanas un mes dos meses (mentira).

Estaba pensando que este dungeon debe estar en el primer demo de khpx, para comenzar desde el comienzo a ponerle algo de acción al juego. Se cansa uno.

Cómo resolver el problema “Duplicate without user-selected canonical” sin perder el tiempo

07-02-2023 5:40 AM

Cuando la Google Search Console encuentra este error “Duplicate without user-selected canonical” si no lo conocemos nuestra primera reacción es ir a google a buscarlo. Ahora tenemos dos problemas.

Hay ciertas búsquedas en google que devuelven cientos de artículos, supuestamente respondiendo la pregunta pero en realidad son una novela de Corin Tellado, donde casi te describen lo que es el internet antes de responder la pregunta “¿Cómo resuelvo este error?” Entonces perdemos el tiempo leyendo y leyendo sin encontrar la solución.

Así que sin más preambulo, aquí está la solución:

Debes colocar en la cabecera de la página cerca de donde colocas los metas, por ejemplo meta name=”description” lo siguiente:

<link rel="canonical" href="URL" />

donde URL es por ejemplo “https://www.macronosis.com/products/” el URL que se debe usar para acceder a esta página.
Así que quedaría así:

<link rel="canonical" href="https://www.macronosis.com/products/" />

La razón por la que las páginas que tratan de explicar cómo se resuelven este tipo de problemas están llenas de banalidades tratando de extender el texto es porque ellos están dando espacio para colocar anuncios y presentar enlaces patrocinados. Lamentablemente esto se ha convertido en un negocio porque la gente buscando información y solución abunda en google. El hecho de que google coloca estas páginas arriba en las búsquedas cuando debería eliminarlas como spam también representa un problema, y una situación desconcertante, obviamente google también está ganando dinero.

Esta es la misma razón por la que yo no veo tutoriales en youtube, la información que busco se encuentra perdida en algún sitio en un video de 10 minutos, lleno de un palabrerío sin sentido, y al final una pobre explicación que la mayoría de las veces me deja frustrado.

La máquina de estados finitos que no existió

23-01-2023 5:36 PM

Este artefacto es un patrón en game programming, con el que se controla, entre otras cosas, el flujo de ejecución del juego en términos de las fases o etapas (llamados estados) que lo componen, y sus transiciones entre una y otra. La definición académica es:

“Una máquina de estados finitos es una estructura que permite definir comportamientos complejos. Facilita definir comportamientos complejos y encapsularlos en mini interacciones únicas que llamaremos estados. Cada estado debe describir una acción muy simple.”

Por ejemplo, en khpx (hasta ahora) he necesitado 3 niveles de control de flujo: el estado del juego (presentando la introducción, jugando, en el menú principal, dentro de un dungeon, etc). En los distintos estados de un dungeon (introducción, jugando, fase 1, fase2, game over, etc), y dentro de uno de estos estados, presentando un sprite, moviendo alrededor el sprite, haciendo fade in (lo cual puede requerir 2-3 etapas, etc).

La máquina de estados finitos (MEF) controla qué cosas puede y no puede hacer el jugador en cada etapa o fase del juego, y cuándo se deben activar cada tipo de eventos por fase del juego. Controla las distintas señales de los distintos componentes del juego que permiten cambiar de fase, activar un evento, cancelar otro, etc..

La máquina de estados finitos (MEF) es, en definitiva, un subsistema del juego, que permite programar en forma simple todo esto. Esa es la teoría. En juegos anteriores (antes de khpx) he implementado esto totalmente integrado al resto, en C o C++. A veces es buena idea que esta MEF tenga una interfase con un lenguaje scripting como python o LUA, lo cual va a facilitar el prototyping y configuración de estados, señales y eventos. En definitiva es un importante y crucial componente dentro del game loop.

Pero khpx no usa nada de eso.

Cuando comencé a programar el primer dungeon (“Mindworm”), lo hice completamente en C++, pensando que una vez completado podía reimplementarlo con el MEF y metascript (mi propio lenguaje interpretado tipo javascript pero no tan tarado, tan poderoso como python pero no tan, bueno eso, y más fácil que usar que Lua, porque si hay algo que no genera el resultado que espero, lo programo para que lo haga). Es decir, mi lenguaje de prototyping es, sí eso es correcto, es C++. Quedó tan bien, tan elegante y eficiente que ni en un millón de años lo voy a tocar para convertirlo en un indescifrable MEF y python y lua y no se qué cuernos. Así que en khpx, no hay máquinas de estados finitos, o, visto de otra forma, cada sección del juego que lo requiere tiene su propia MEF.

No es una arbitrariedad de mi parte, es la filosofía de diseño de khpx, tiene y debe ser absolutamente simple. Por otro lado, está programado en C++ sin clases + la standard template library. Las implementaciones de los MEF usualmente consisten en una clase con médodos que permiten registrar los estados, las transiciones, los eventos, las señales etc.. La máquina de estados finita del dungeon Mindworm es un vector de estados, y cada estado es basicamente una estructura con apuntadores a tres funciones: inicialización, actualización y rendering. La primera se ejecuta una vez, al comenzar un estado, las dos siguientes en cada game loop. El cambio de estado se ejecuta simplemente incrementando el índice en el vector de estados. Eso es todo. Debuguear aquello es tan simple como revisar las funciones, no hay nada más que revisar. De hecho, luego de 40 horas de trabajo, 575 líneas de código, y 32 estados diferentes, acumulé 0 bugs debido MEF (pseudo MEF). Claro que hubo bugs, pero fueron sprites desapareciendo y cosas por el estilo (so->sy debe ser incrementado con so->vy, no con so->sy. Por algo prefiero los nombres largos. Menos confusiones).

La MEF, el lenguaje de scripting, el engine de sprites, el editor de recursos del juego y el manejo del menú principal son cinco cosas que ya he implementado al menos 3 veces desde 0. Todo lo he hecho de diversas formas, siempre diferentes, casi sin reutilizar nada. Espero que con khpx sea la definitiva de y ahora en adelante pueda usar estos componentes en el futuro.