Aprendiendo a programar: introducción a los arrays

February 20th, 2007

El siguiente tema en esta serie de artículos serán las tablas, también llamadas arrays ó matrices, estructura fundamental para el programador. Constantemente haremos uso de ellas; cuanto más soltura se tenga con ellas, menos disgustos nos ahorraremos.


¿Qué es un array?

Un array es un conjunto de datos del mismo tipo. Un conjunto de enteros, un conjunto de chars… Llevan asociados un mismo nombre y para diferenciarlos tienen un índice. LLamaremos elemento a cada dato que compone el conjunto de datos, es decir, el array.


Vectores

Un vector es un array de una dimensión, o lo que es lo mismo, un array de una fila y N columnas.

int miTabla[5];

Con eso tendríamos un array, “vacío”, de enteros, de 5 elementos, en lenguaje C. El índice del primer elemento sería el cero, accediendo a él a través de miTabla[0]. El elemento 2, siguiendo la misma pauta, sería accesible mediante miTabla[1] y el último elemento, por miTabla[4].

En pascal, podemos especificar nosotros el rango del índice… de modo que…

Var MiTabla : Array [1..5] of Integer

En este caso el índice iría de 1 a 5, correspondiendo el 1 al primer elemento y el 5 al último.

Cada elemento del array, se comportará como cualquier otra variable. Es decir, podremos hacer un miTabla[3]=5; y asignaremos al elemento 3 del array miTabla el valor 5; también podremos hacer un miTabla[3]=miTabla[3]+2; con lo que tendremos que en ese elemento de miTabla ahora hay un 7.

Por supuesto, cuando accedamos a los elementos del array, debemos asegurarnos que el elemento con el índice que indicamos, existe. Es decir, si en el ejemplo anterior, intentamos acceder a un elemento miTabla[7];, nos daría un error de overflow, pues el índice pasado no coincide con el rango indicado (de 1 a 5). Esto hay que tenerlo en cuenta de forma especial cuando en lugar de una constante, accedamos por una variable, el típico miTabla[i]; metido en un bucle. Si i en algún momento se pasa del rango, el programa cascará. (En tiempo de ejecucción, además, pues el compilador no daría error al compilar).

Otro aspecto que se ha de tener en cuenta, al igual que con las variables, es comprobar qué tiene el elemento en cada momento. Tras crear el array, en el primer ejemplo, en C, puse un vacío entre comillas. Y, es que, al declarar el array no lo estamos inicializando… y sus elementos pueden contener cualquier cosa, y no necesareamente ceros como se podría pensar. Aprovechando esto para ver cómo sería el recorrido completo de un vector y suponiendo que nuestro programa necesita inicializar todos los elementos del vector a cero…

for(i=0;i<5;i++) miTabla[ i ]=0;

En un algoritmo genérico, la 0 del i=0 sería el comienzo del índice del vector y el i<5, el último. Visto desde pascal, con el ejemplo anterior,

for i=1 to i<=5 do miTabla[ i ]:=0;

Si no recuerdo mal, esa sería la sintaxis para pascal. Pero más allá del código en concreto, hay que coger la filosofía. ;)


Matrices

Por matriz se entiende un array bidimensional. En este caso, tendremos un array de N filas por M columnas, es decir, de tamaño NxM, por ejemplo, int Matrix[5][5]; ó int Matrix[10][2]. Lo dicho anteriormente se aplica aquí, teniendo en cuenta la segunda dimensión.

Para recorrer la matriz, necesitaremos dos bucles anidados, con dos contadores, i y j, una para las filas (i) y otra para las columnas (j).

// Código en C
for(i=0;i<10;i++)
for(i=0;i<2;i++)
Matrix[ i ][ j ]=0;


Arrays multidimensionales

También podemos crear arrays de más de dos dimensiones, 3, 4.. N dimensiones. Pasando de 3, la cosa se complicaría mucho y no merecería la pena, optando por otra opción de implementación. En el caso de tres, tendríamos algo como int arrayM[5][6][7]; y recorriendolo con tres bucles for y tres variables, i, j y k.


Aprendiendo a programar: programación modular

February 18th, 2007

Seguimos con el cursete de programación. Según vaya avanzando, me meteré más en profundidad en algoritmos genéricos, ejemplos y demás… pues el anterior post del curso igual me parecía muy básico para extenderme demasiado…


Programación estructural, modular y orientada a objetos

Estos tres conceptos hacen referencia a una serie de principios o pautas que definen la filosofía a seguir en los programas.

La programación estructural es la que se compone de las 3 estructuras básicas ya vistas: secuencial (las instrucciones se ejecutan una detrás de otra, en orden descendente), alternativa (los if) e iterativa (los bucles), teniendo en cuenta que todo programa comienza en un sitio y termina en otro o, dicho de otro modo, tiene un principio y un final. (Todo principio, tiene un final, que decía Morpheus)

La programación modular consiste en dividir un programa en partes bien diferenciadas, llamadas módulos (o subprogramas), que pueden ser analizadas y programadas por separado. Existe un algoritmo o programa principal, que cede el control al resto de módulos y, una vez éstos se han ejecutado, vuelve a tomar el control, continuando la ejecución del programa por dónde los llamó.

Entre las reglas principales para programar de forma modular (práctica más que conveniente), están:

- Cada módulo tiene que tener un punto de entrada y otro de salida. (Es decir, el módulo una vez haya realizado su tarea, debe devolver el control al programa principal desde donde fue llamado).

- En el programa principal, se debe definir todos los módulos que se van a utilizar y definirlos en consecuencia.

- Los módulos deben tener independencia, en la medida de lo posible, respecto del programa principal. La idea es que los módulos realicen una tarea de forma genérica, para poder reutilizar código si fuese necesario en otro programa, sin tener que tocar el código del módulo.

- Los datos del programa principal que el módulo necesite, serán pasados desde allí. (Evitar las variables globales).

De la POO ó OOP, programación orientada objetos, hablaré más adelante, pues primero conviene tener soltura con las “programaciones” anteriores, para pillar bien ciertos conceptos.


Funciones y procedimientos

Para no cambiar de tema y después retomarlo, seguiré hablando de subprogramación o programación modular. Las funciones y los procedimientos forman parte de ello.

¿Qué es un procedimiento? Teniendo en cuenta lo anterior, se definiría como un módulo. Concretamente, como un módulo que no retorna ningún valor, ejecuta lo que tenga que ejecutar y devuelve el control al programa que lo llamó.

¿Qué es una función Un módulo que retorna un valor.

La llamada de ambas desde un programa, se hace de forma diferente, atendiendo a su definición. Cuando trabajemos con una función, al llamarla no podremos llamarla sin más, si no que deberemos hacerlo en alguna parte en el que el valor devuelto sea evaluable. Imagina que tenemos una función llamada EsNumero, de tipo booleano (devuelve un valor de tipo bool, true o false, según la cadena pasada esté compuesta toda de números o haya otros caracteres). Si la llamamos como

Sentencia 1
Sentencia 2
EsNumero(cadena)
Sentencia 3

A dónde iría el valor devuelto por la función? A ninguna parte. Y tampoco sería evaluable de ningún modo. La llamada correcta sería en un if, o igualando a una variable. (if EsNumero(cadena) then writeln(”es numero”); ó miVar=EsNumero(cadena), donde miVar es una variable de tipo boolean).

Observa la diferencia. ;) :) También al margen, observa que en el if no he puesto EsNumero(cadena)=true. En un if, cuando la evaluación de la condición da true, verdadero, es cuando se ejecuta las instrucciones que hay dentro de él. Al retornar nuestra función un valor booleano, si devuelve true, en el if habrá algo como if true then… puesto que true, es true…. entrará. Si retorna false, if false then.. como la evaluación de false no es verdadero, no entrará. Por supuesto puedes ponerlo de forma completa con EsNumero=true, pero estos detalles muestran la soltura de un programador.

Por el contrario, la llamada a un procedimiento se hace en “solitario”, como en el ejemplo primero de EsNumero (el que está en el recuadro).

Dentro de una función, deberemos tener una sentencia que indique el retorno de un valor. En pascal, tomaremos el nombre de la función y le asignaremos el valor. (EsNumero:=true). En C, se hará mediante return valor; Para no olvidar este retorno, una buena práctica es declarar una variable del tipo de la función, en este caso bool. Es decir, al principio de la función, haremos var retorno: bool; En el código de la función, iremos asignando a esa variable el valor que proceda en cada caso y al final de la misma, asignaremos esa variable a la función, EsNumero:=retorno. Así, la lógica de la función podrá fallar, pero siempre devolveremos un valor de forma clara. ;)


Paso de parámetros

Los parámetros son los datos que pasaremos a los subprogramas, cuando necesiten trabajar con datos ajenos a ellos. Hay dos formas de pasar estos parámetros, por valor o por referencia. Tanto las funciones como los procedimientos, aceptan ambas formas.

Paso por valor. Se pasa una copia de la variable. Los cambios que hagamos a esa variable, sólo tendrán efecto en el subprograma, pues al tratarse de una copia, en el programa principal tendremos el valor original.

Paso por refencia. En este caso se pasa la dirección de memoria en dónde está la variable, una referencia a su ubicación. (de ahí el nombre). Por tanto, cualquier cambio que hagamos en ella, quedará modificado en el resto del programa, pues se trata de la misma variable.

Y… ¿qué sistema es mejor? Depende de lo que queramos hacer. Es cuestión de valorar cada caso y decidir. (Cuando se trabaje con TAD´s, tipos abstractos de datos, los pasaremos siempre por referencia, pues por valor carecería de sentido por concepto).

Parámetros formales / parámetros actuales. Cuando hablamos de parámetros formales, nos referimos a los parámetros presentes en la declaración de un subprograma. Por parámetros actuales entendemos los parámetros pasados al subprograma en cada llamada. Los parámetros formales y actuales han de coincidir en número y en tipo. De forma opcional, también pueden coincidir en el nombre.


Vida y visibilidad de variables

De vuelta a las variables, comentar una cosilla en relación a la última frase del apartado anterior. Las variables globales, a evitar, son aquellas que son accesibles en cualquier lugar del programa principal y de los subprogramas. En cambio, las locales sólo son accesibles desde el subprograma donde fueron creadas. (Si se crean en el programa principal, sólo serán visibles en el programa principal, no así en los módulos). Por este motivo, si tenemos dos subprogramas y en cada uno de ellos declaramos una variable con el mismo nombre, el programa no daría error, pues se trataría de dos variables diferentes (no se ven entre sí y por tanto, no influyen entre ellas, y tampoco pasaría nada por tener una variable llamada i en el programa principal y en dos subprogramas). La vida de esas variables, terminará (se liberará el espacio en memoria destinado a esa variable), cuando se finalice el subprograma… por ello hay que tener en cuenta que las variables locales de cada subprograma no son accesibles desde el resto de subprogramas o programa principal… si nos hace falta un dato procesado en el subprograma, lo enviaremos por medio de una función, retornando un valor.


Aprendiendo a programar: conceptos iniciales

February 17th, 2007

Comenzando con la recopilación de apuntes o tutorial de introducción a la programación….

Introducción

¿Qué es un programa? Podríamos definirlo como una secuencia lógica de instrucciones que manipulan unos datos para obtener unos resultados. En esa secuencia podemos distinguir dos partes: una declarativa, donde se definen / especifican los datos a tratar y otra operacional, donde van las instrucciones que procesarán la información.

Cuando se comienza a aprender a programar, antes de comenzar en algún lenguaje concreto, suele verse la llamada metodología, que no es más que desarrollar los programas en papel, de forma genérica, en pseudocódigo. Resolviendo problemas de esta manera y posteriormente pasándolos a un lenguaje concreto es una buena manera de aprender a programar.

Aquí haré un poco mezcla de todo… pseudocódigo, pascal y c… éste ultimo para que veais que una vez sepais resolver problemas (programar), pasar de un lenguaje a otro no es difícil. En el aspecto léxico no me meteré mucho, pues se puede mirar en cualquier tutorial especializado en el lenguaje concreto.


Comenzando

Cuando nos enfrentemos al desarrollo de un programa, debemos tener presente de principio dos cosas: los datos que van a entrar y los que van a salir. Haciendo valoración de ambas, pensaremos en el algoritmo que solucionará nuestro problema. (Los pasos a efectuar para conseguir nuestro objetivo).

Para ello haremos uso de todo lo que veremos a continuación…

Tipos de datos

Hemos hablado de datos, pero no del tipo que son esos datos. Y, esto es importante, pues no trataremos de igual modo un número entero que un número decimal o que una cadena de caracteres. Por lo general, hablaremos de integer (entero), float (decimales de simple precisión), double (decimales de doble precisión), bool (true/false) y char (caracter). Actualmente los lenguajes soportan el tipo string o cadena de caracteres, pero no lo usaremos. (Creando las susodichas cadenas a partir de arrays de char).

También podremos crearnos nuestro propio tipo de dato, a través del type y registros de pascal o las estructuras de C.

Variables

Las variables son contenedores de información. Como su nombre indica, cambian de valor a lo largo de la ejecución del programa. (O mejor dicho, las asignamos nosotros valores diferentes según convenga). Lo explicado sobre los tipos de datos, quedará patente en este apartado. Si queremos meter un 5 en una variable, por ejemplo, esa variable deberá ser de tipo integer, haciendo

var num: integer; num:=5; (* en pascal *)
int num=5; // en C

Con ello, en la variable num tendremos un 5 almacenado para su posterior uso. Hay que poner atención al rango del tipo de datos, pues por ejemplo, si en un entero intentamos meter un número que no está en su rango, dará un error de overflow.

Constantes

En muchos programas, usaremos datos que se mantendrán invariables en todo el programa. Hablamos entonces de constantes. Realmente, también lo podríamos meter en una variable, pero no estaría bien visto, por así decirlo. Poniendo un ejemplo, imaginemos que por algún motivo necesitamos el número pi, 3.14, en muchos puntos del programa. Una solución sería poner en cada uno de sus puntos el número tal cual… pero… imagina que en un momento dado, quieres poner dos decimales más: tendrías que cambiarlo en todos los sitios.

Lo suyo sería definirnos una constante, antes de todo procedimiento, incluso de la función principal.

(* Pascal *)
Const
PI = 3.14

// C
# define PI 3.14

En cualquier punto del programa, podremos hacer referencia a la constante escribiendo su nombre. Las constantes no tienen un tipo específico y se suelen nombrar con todos sus caracteres en mayúscula.

Operadores

Con los datos podemos hacer una serie de operaciones, concretamente, en pascal:

Aritméticas: +, -, *, /, div (división entera), mod (resto)
Relacionales: =,<,>,<=,>=, <>
Lógicas: and, or, not
Asignación: :=

Sentencias de control

If. Sentencia condicional. El código que esté dentro se ejecutará sólo si la condición se cumple. De lo contrario, pasará de largo, ejecutando el resto de código que esté después.

Un if puede contener otros if dentro de él, llamados anidados. Además, puede seguirle una instrucción else, con la que indicaremos el código a ejecutar si la condición no se cumple.

if a>5 then writeln(”a es mayor que 5″)
else writeln(”a es menor o igual a 5″);

En pascal, si dentro de un if y, en general de cualquier bloque, va más de una instrucción, debe llevar un begin al principio y un end al final del bloque. En C, el contenido iría entre llaves. { }

Bucles. Un bucle hace posible que determinado código se ejecute una y otra vez, un número finito de veces. Ese número finito queda definido por una condición.

En pascal, tenemos los bucles for, while y repeat. Algo a tener en cuenta es la diferencia de concepto entre los tres. En el for, se ejecutarán las instrucciones tantas veces como se cumpla la condición, sin poder salir antes del bucle. En el while, se ejecutarán mientras se cumpla la condición (si desde el principio no se cumple, no se ejecutarán) y en el repeat, como el while, pero se ejecutarán al menos una vez, pues la condición va al final.

Y cuando se usa uno u otro? Sabiendo lo anterior y valorando. En una búsqueda, usaremos un while, pues saldremos del bucle en cuanto encontremos lo que buscamos, para recorrer una matriz de principio a fin, usaremos un for… a base de hacer ejercicios y de ir pensando, se va cogiendo soltura… ;) :) Manejando bucles de forma correcta, ireis por el buen camino, pues son el pan diario del programador.

Más sobre variables

Para terminar la primera parte, un pequeño matiz sobre variables. Según el uso que las demos, podemos hablar de contadores, interruptores, auxiliares… en esencia no dejaremos de hablar de variables, pero por cumplir una misión específica, se las llama de un modo especial.

Contador: es la variable que usamos, como su nombre indica, para llevar alguna cuenta. Por ejemplo, en los bucles. Se suelen llamar i, j, k… y su incremento o decremento suele ser de uno. Si es de más, también se le llama acumulador. (Por ejemplo, suma=suma+lo leido por teclado).

Interruptores: también llamadas switch. Nos indican el estado de algo concreto en un punto del programa. Se llaman así porque funcionan como el botón de encendido, tomando sólo dos valores, true o false. Por ejemplo, la variable booleana en el típico algoritmo de búsqueda con while.

Auxiliares: las anteriores son clasificadas como auxiliares, pero nos referiremos con este nombre a aquellas que nos sirven para almacenar un valor mientras hacemos otras operaciones. Por ejemplo, en el intercambio de valores en los algoritmos de ordenación, en los que para no perder un dato, debemos guardarlo en un sitio auxiliar, para reubicarlo en su sitio adecuado después.


Consejos para afrontar exámenes de programación

January 26th, 2007

A modo de recopilación de consejos para afrontar un examen de programación….

- Antes de comenzar a programar de forma concreta, es útil hacer un esbozo o esquema del planteamiento que vas a seguir, es decir, el cómo te lo vas a montar. (Las estructuras de datos que necesitarás, qué aspectos o datos has de tener en cuenta en cada momento…). Si comienzas a codificar sin este paso previo, es muy posible que te encuentres con problemas y te vuelvas loco modificando una y otra vez el programa para que funcione, haciéndote perder mucho tiempo.

- Usa funciones y procedimientos, aunque no te lo pidan de forma expresa. Si tienes una parte de código que se repite en el programa, crea un subprograma para tal fin y llámalo cuando te haga falta. Si, aunque sólo la hagas una vez, la tarea es algo concreto, también pásalo a un subprograma. Mejorarás el orden del código y lo tendrás más limpio para no liarte al repasar.

- Si alguna parte te da problemas… que no sabes por dónde abarcar, pásalo a un subprograma,dejando su contenido en blanco y suponiendo que hace lo que tiene que hacer. Si te sobra tiempo, puedes darle vueltas a eso que te falta sin haber perjudicado el resto del programa. En la vida real esta práctica no te servirá de mucho, pero igual en el examen, si tienes todo lo demás correcto, te puntuen positivamente.

- Recuerda no hacer uso de variables globales. Las que necesites en varios subprogramas, pásalas como parámetros. Muestra especial atención al tipo y al orden de parámetros, y si se han de pasar por valor o por referencia. Equivocarse en el paso de parámetros, suele bajar mucha puntuación.

- Ten en cuenta la eficiencia del algoritmo. Un problema puede resolverse de muchas formas, pero intenta resolverlo usando el menor número de recursos. Las vueltas de más que des en un bucle, por ejemplo, es una cosa importante a evitar.

- Si en el ejercicio te dicen un número concreto de datos, por ejemplo, que el máximo posible de registros va a ser 30, casi con toda seguridad tendrás que hacer uso de un array. De lo contrario, tendrás que montartelo de forma que mantengas la integridad del programa. (Por ejemplo, si en un fichero no sabes cuantos registros puede haber, no los pases a memoria (array) salvo que sea estrictamente necesario. Imagina que declaras un array de 1000 elementos y solo hay un registro en el fichero: estarías desaprovechando 999 elementos. Del mismo modo, imagina que hay 1001: no te cabrían en el array).

- Nombra las variables, funciones.. con un mismo criterio (primera letra con minúscula, o que indique de qué tipo es…) y con un nombre claro que la identifique. En programas largos o que no sólo toques tú, se agradecerá, así como será más apetitoso de leer en el examen. Procura tener el código lo más ordenado / estructurado posible, pues la impresión del profesor que evalúe el examen será muy positiva.

- Cuando revises los ejercicios del examen, pon atención en cosas como la correspondencia entre parámetros formales y parámetros actuales, que las variables que usas previamente las has declarado / inicializado, la sintaxis correcta del lenguaje usado… mucha veces se olvida y es algo que resta puntos.

- A programar, se aprende programando, no estudiando. Estudiar sirve para conocer el lenguaje concreto, algoritmos genéricos, estructuras… pero no sabrás usar todo ello si no te enfrentas a los ejercicios papel / compilador en mano.

- De vuelta a las funciones, lee con atención el enunciado. Si te piden que el subprograma ha de devolver un dato (un número, una cadena…), tendrás que hacer una función. (A una función también se le pueden pasar parámetros por referencia). Si lo haces con un procedimiento y está bien, no te lo darán por válido.

- En el típico algoritmo de búsqueda en un array, fichero… con un while, se usa dentro if, pero sin else. Es muy común al principio tender a poner un else. La idea es que el bucle while tenga dos condiciones: la primera que delimite el rango máximo de busqueda (final del array, final del fichero…) y otra que salga del bucle cuando lo haya encontrado. Dentro del bucle, se coloca un if, si en una vuelta encontramos lo que buscamos, salimos del bucle. Y fuera del bucle, preguntamos con if por qué condición se ha salido del while (si por que lo ha encontrado o por que ha llegado hasta el final del array, fichero… sin encontrarlo…)

- También en los bucles hay que poner cuidado en la variable que funciona como contador: que tenga el valor apropiado para que entre en el bucle, que al acceder al elemento de un array (tipo array[j]), j no sea mayor que el tamaño del array, que haya una condición de parada y que alguna vez entre en esa condición de parada…


Eileen, programando el tetris

October 15th, 2006

Esta semana me he centrado en Eileen, de cara a dejarla más o menos terminada. Debido a la cantidad de curro por hacer en otros proyectos, me temo que se quedará en este estado durante un tiempecillo. (Lo lamento por Rosa y compañía, viciad@s de tales juegos).

 

Una de cambios

En el post anterior, comentaba que me decidí por usar un sprite por cada pieza. Bien, debido a problemillas varios con el giro de las piezas, opté por volver al planteamiento de formar las piezas a través de cuadritos. Con ello conseguí formar cualquier pieza (antes necesitaba que la pieza comenzara por un cuadro arriba a la derecha que me servía como guía) y tener más control en el giro. Además, a la hora de quitar las líneas no tengo que hacer ninguna conversión rara, simplemente quito las líneas de Matrix (los elementos de la fila que desaparece se resetean a 0) y muevo progresivamente todas las líneas. (El progresivamente es un decir, lo hago todo de un plumazo… pero la idea es que en la 1.0 haya una especie de animación por cada línea que desaparece).

 

Niveles, puntos y game over

Una vez que el esqueleto del programa quedaba operativo.. había que implementar detalles tales como los niveles o la puntuación.

Para el control de niveles me creé otra matriz, declarada como int Levels[2][2];. El 2 hace referencia al número de niveles y, el hecho de su bidimensionalidad se debe a que en cada fila guardo por un lado el número de piezas necesarias para cambiar de nivel (tras generar ese número de piezas, se cambia de nivel) y por otro lado la velocidad de bajada de las piezas. Teniendo una función que setee los valores, en el game loop sólo queda tomar esos valores donde sea necesario.

Para la carga de niveles, utilizo un switch o interruptor… si al entrar en el game loop vale false, cargo el nivel según una variable que me indica el nivel actual; si vale true, no cargo nada.. ;)

En cuanto a la puntuación y el número de piezas generadas, son simples contadores.

Por último, el gamer over sólo consiste en comprobar si la función que valida el movimiento hacia abajo devuelve true o false. Si la pieza no puede moverse hacia abajo (tras ser generada) es que el juego ha terminado, mostrando game over durante unos segundos y volviendo al menú.

 

Siguiente pieza

Otro de los detalles a implementar consistía en mostrar las piezas siguientes. Para ello, de nuevo uso una matriz, esta vez de dimensiones de 4×4 (número máximo de cuadraditos que tiene una pieza). En cada vuelta al game loop, actualizo esa matriz en función de la siguiente pieza a generar, que previamente guardo en una variable. Luego es mostrar el contenido de esa matriz y dibujar de forma pertinente.

 

El menú

Para el menú necesité ampliar Nessi con una clase que no tenía pensada, pero que sin duda ha sido y será muy útil. Nessi_XT_Menu carga los sprites o botones del menú, teniendo soporte para dos estados, el de seleccionado y en reposo. Con funciones miembro, se lleva el control de la opción seleccionada, el paso de una a otra… así que desde el programa sólo hay que cargar los sprites y poquillo más. :D

 

Versión 0.80, en fase beta

La versión subida tiene algunas cosillas que no me ha dado tiempo a implementar i seguro que algún que otro bug no detectado. (El coding express es lo que tiene… je,je,je..). Por ejemplo, las piezas no son generadas de forma aleatoria (o medianamente aleatoria), hay pocas piezas (sólo 5) y como he comentado, sólo dos niveles, muy facilitos. Pero bueno, para hacerse una idea de lo que en un futuro será… ;) :) Está por la sección de software… con un programa llamado installer2go, he generado un instalador, que siempre igual se agradece. ;) :)

 

Versión 0.83, varias mejoras…

Por petición de una de esas amigas aficionada a los puzzles, a noche de 17 de octubre he añadido varias mejoras. En primer lugar he solucionado un bug al término de todos los niveles, que se me había olvidado controlar. En relación a ellos, he ampliado los niveles a 4, aunque sigue habiendo sólo dos mapas (que se van alternando, pero con la velocidad en aumento). Por último, he mejorado la generación aleatoria de piezas… ya sí se puede decir que es aleatoria (los entendidos sabrán que no es del todo cierto, pero el juego generará piezas diferentes en cada ejecución. ;) :)