@ agnasg

agnasg


Creo que lo logré

13-10-2011 4:42 PM

Hay veces que nos encontramos con piezas de código inexplicables. Difíciles de entender. Complicados de comprender. Requieren de una segunda o tercera reflexión para dilucidar su génesis, su orígen, qué tipo de estado mental tenía su creador para “crear” algo así.

Hace muchos años alguien me mostró el siguiente segmento de programa. No hace realmente nada pero forma parte de la familia de códigos descritos en el párrafo anterior. (Este post es en realidad para mayores de 18 años. Sí Ud. es menor de 18 años años por favor abandone  esta página de inmediato)
[cce lang=”c” width=”480″]
for (int i = 1; i < 4; i++) {
switch (i) {
case 1:
ejecutar_cosas_relacionadas_a_1 (); break;
case 2:
ejecutar_cosas_relacionadas_a_2 (); break;
case 3:
ejecutar_cosas_relacionadas_a_3 (); break;
case 4:
ejecutar_cosas_relacionadas_a_4 (); break;
}
}
[/cce]
Este código tiene un bug y un problema de implementación.El bug no lo voy a explicar porque es obvio, pero el problema de implementación es demasiado descarado para dejarlo pasar. ¿Qué es lo que esto está haciendo? ¿Para qué es el “switch“? ¿Para qué es el “for”? ¿Para qué es todo este código? El código equivalente es el siguiente:
[cce lang=”c” width=”480″]
ejecutar_cosas_relacionadas_a_1 ();
ejecutar_cosas_relacionadas_a_2 ();
ejecutar_cosas_relacionadas_a_3 ();
ejecutar_cosas_relacionadas_a_4 ();
[/cce]
Yo nunca logré explicarme esto. Mi conclusión fue como la de aquél Cura en La Mala Hora: “Fue un momento de ofuscación”. Pero este tipo de accidentes de la prisa sobre la exactitud, no son tan inusuales como podríamos creer. Por ello debemos estar alerta. Debemos estar alerta todo el tiempo. Como he dicho antes, he estado trabajando un juego en javascript. En ciertas partes parece pacman. De hecho, el movimiento del personaje central es igual que el de pacman. En realidad yo nunca había implementado este tipo de movimiento en un espacio-tiempo de tiles (mosaicos o piedras cuadradas o baldosas). Este espacio muy simple simple obedece reglas matemáticas tan complicadas como las del espacio 2.5 o el espacio 3D. Es un espacio de juego, y como tal, es complicado. No podemos tomarlo a la ligera, no podemos aproximarnos a él descuidadamente. Debemos estar alertas.

En este particular espacio 2d, hay 4 movimientos posibles: arriba, abajo, derecha o izquierda. A continuación el código que controla el movimiento hacia la izquierda del jugador. Esta no es necesariamente la primera versión (ni es la definitiva).
[cce lang=”c” width=”480″]
function move_right()
{
room.last_move_direction = 6; // indica cuál dirección llevamos

if (moving)
return;

x = thile[thile_number].x;
y = thile[thile_number].y;
var min_width = bg_width – x – thile_width;
[/cce]
Guardamos en alguna parte que el último movimiento que hizo el jugador es derecha (identificado por 6), guardamos en x,y la posición del jugador (almacenado en thile). Luego inicializamos el mínimo width de del movimiento que en este caso sería lo que queda de tablero menos la posición actual.
[cce lang=”c” width=”480″]
for (i = 1; i < room.tiles_in_level.length ;i++) {
if (i == thile_number)
continue;
xo = parseInt (str_replace(“px”, “”, $(‘#thile’+room.tiles_in_level.charAt(i)).css(“marginLeft”)));
if (!xo)
continue;
if (x > xo) // it can´t be to the right of k.
continue;

yo = parseInt (str_replace(“px”, “”, $(‘#thile’+room.tiles_in_level.charAt(i)).css(“marginTop”)));

if (y > yo && y < yo + thile_height ||
(y < yo && y > yo + thile_height) ||
(yo < y && yo > y + thile_height) ||
y == yo ||
y + thile_height > yo && y + thile_height < yo + thile_height) {

var this_width = Number (xo) – Number (x) – Number (thile_width);
if (this_width < min_width) {
min_width = this_width;
room.cd_id = room.tiles_in_level.charAt(i);
}
}
}
[/cce]
Esto es bien pesado. Aquí estamos recorriendo todos los tiles cuál es el más cercano que está en nuestro camino. Lo voy a decir de nuevo: estamos revisando TODOS los tiles del tablero.
Calculamos la velocidad necesaria para recorrer la distancia obtenida en un segundo.
[cce lang=”c” width=”480″]
moving = true;
var speed_base = 1000.0;
var speed = (min_width / bg_width) * speed_base;

thile[thile_number].x = Number (thile[thile_number].x) + Number(min_width);
[/cce]

Finalmente esta última parte envía el tile la distancia determinada utilizando el procedimiento estándard de un jquery.
[cce lang=”c” width=”480″]

x = thile[thile_number].x;

$(thile[thile_number].tag).animate({
marginLeft: x + “px”
}, speed, function() {
// Animation complete.
moving = false;
start_level ();

});
}[/cce]
Yo no trataría de entender este código, de la misma forma como no trataré de explicarlo (más alla de lo dicho hasta ahora). Lo que quiero es compararlo con la versión final:
[cce lang=”c” width=”480″]
j = parseInt (Math.floor(y – bg_offset_y) / 64);
i = parseInt (x / 64);
do {
i++;
room.set (j, i);
} while (i < ncol && room.available_pos (i, j)); i--; [/cce] Es decir, todo el código de comparación de las posiciones de todos los tiles del tablero lo hemos cambiado por un loop donde validamos si el espacio está o no disponible, basado en un mapa. Las dos primeras instrucciones cambian las coordenadas de la pantalla a las coordenadas del mapa. Yo no sé que proceso mental me llevó a escribir la versión donde comparaba el posicionamiento individual de todos los tiles, pero debe haber sido un proceso mental bien complicado, al igual que su resultado (al cual dediqué días). Sirva como excusa que a medida que escribía código estaba diseñando el juego, y todavía no había decidido cómo sería el mapa. La lección aprendida es la misma: no dudes en eliminar código, cambiarlo, cuestionarlo, desecharlo. Si en un día no has desechado o cambiado código, algo malo está pasando.

Ahora sí se de qué se trata la vida

12-10-2011 9:06 AM

Mantenerse estático tiene sus consecuencias

11-10-2011 9:49 AM

Interesante artículo para descubrir la diferencia entre un programador accidental y un programador apasionado (los nombres los estoy colocando yo, en la presentación son un tipo y una niña adelantada) (por cierto, vi recientemente La red Social, donde se narra las anécdotas alrededor de la génesis de Facebook. La película está basada en un libro llamado Billonarios por accidente: yo no creo que sea un  accidente la forma como estos señores llegaron a millonarios, estaban destinados a ello porque sí). El artículo (llamado Deep C, o C profundo) muestra que un programador accidental apenas conoce cómo funcionan las cosas. Un programador apasionado por el contrario conoce en detallé las diferencias de las distintas construcciones, cómo se comportan en C y en C++, e inclusive sus diferencias a través de las distintas versiones ANSI C, C++98, C99.

Por ejemplo, cuál es la salida del siguiente programa? ¿Compila? ¿Produce algún warning? ¿Alguna diferencia entre ANSI C, C++98?
[cce lang=”c” width=”480″]
int a;
static int b;
int main ()
{
static int c;
int d = 42;
int e;
printf (“%d %d %d %d %d\n”, a,b,c,d,e);
}
[/cce]

Mejor lo probamos todo desde el comienzo

03-10-2011 8:43 AM

Quedé perplejo al leer este  artículo sobre las técnicas de programación de juego de Notch (el programador detrás de Minecraft). Se trata de algo que él hace, que puede resultar obvio, o quizás no, pero que como quiera que sea es una buena idea: probar todo desde el comienzo cada vez que hace una modificación.

The most important thing I learned was his testing technique. Notch tests continuously, and he tests completely thoroughly, playing through the entire game every time he makes a change.

Esto resulta tedioso y consume tiempo, pero la verdad es que más tiempo consume tratar de encontrar un bug que surgió en alguna parte del juego, en forma inadvertida cuando modificamos algo en otra parte del juego. En este momento estoy desarrollando el 2do. nivel de un juego javascript. Hice múltiples cambios en las rutinas de collision detection, para hacer funcionar un efecto que aparece solamente en este nivel. Pero ahora uno de los efectos del 1er nivel no funciona. No tengo idea cuánto tiempo voy a necesitar para encontrar la falla, inclusive utilizando git, un sistema de manejo de versiones que me permite ubicar la versión con la falla (con el comando git bisect). La verdad es que mejor probamos todo desde el comienzo. Así nos evitamos la incertidumbre y la frustración que un bug genera.