Está en la página 1de 10

Universidad Simón Bolívar

Departamento de Computación y T.I.


CI2615 – Algoritmos y Estructuras I
Abril – Junio 2004

Proyecto Rompecabezas

Planteamiento del problema.

El rompecabezas es una figura dividida en piezas que debe ensamblarse. Se trata de un


tablero de MxN casillas y (MxN)-1 fichas que pueden deslizarse horizontal y verticalmente
hacia el espacio vacío restante. Por ejemplo:

Donde el rectángulo en negro representa la casilla vacía. El mecanismo es el siguiente:


dado un tablero desordenado, mover las fichas hasta encontrar un patrón prestablecido, con
la restricción de que el único tipo de movimiento permitido es el desplazamiento hacia la
casilla vacía de una de las fichas adyacentes a la misma, excluyendo las diagonales. Sin
embargo, podrán realizarse simultáneamente varios de estos movimientos, siempre que el
grupo de fichas a desplazar pertenezca a la misma fila o (exclusivo) columna de la casilla
vacía, por ejemplo:

El tamaño del patrón puede ser menor que el del tablero, por lo que su ubicación en el
mismo puede variar. Dado el patrón (a), en los estados (b) y (c) ya se ha armado el
rompecabezas:

a) b) c)

1
Se desea que usted diseñe e implemente un programa en GCL que permita armar un
rompecabezas. El programa debe tener la siguiente funcionalidad:

1. Al comenzar, debe preguntar el nombre del jugador y generar aleatoriamente el


patrón que desea armar (especificando las dimensiones del patrón).
2. El patrón a armar debe mostrarse gráficamente.
3. Debe permitir armar varios rompecabezas consecutivamente, con estados iniciales
aleatorios.
4. El jugador puede decidir abandonar el juego en cualquier momento.
5. El tablero debe desplegarse en modo gráfico. Las fichas estarán representadas por
círculos de colores.
6. Cuando el patrón es encontrado, resaltarlo en el tablero y felicitar al jugador.

Solución general

El proyecto involucra dos componentes: uno de lógica y uno de interfaz. Comenzamos con
el componente lógico. Lo primero que debemos hacer es tener una visión general de la
solución. Podemos aplicar análisis descendente para obtener una primera versión:

do se desea armar un nuevo rompecabezas→


obtener patrón y tablero inicial;
do patrón no detectado y jugador no abandona →
obtener jugada, donde jugada es abandonar o dar un movimiento;
if movimiento es válido →
mover ficha(s)
 movimiento no es valido y jugador decidió no abandonar→
dar mensaje de error
 jugador decidió abandonar →
skip
fi
od

if Patrón detectado →
felicitar y resaltar
 jugador decidió abandonar →
skip
fi
od

2
Estructuras de datos

Un tablero podemos considerarlo como un arreglo de arreglos o arreglo bidimensional de


colores. Cada casilla estará representada por un elemento del arreglo y contendrá el color
correspondiente a la ficha que la ocupa (El negro representa la casilla vacía). Al momento
de especificar un procedimiento o función que reciba como parámetro un arreglo
representando un tablero, se incluirán dos parámetros adicionales, que indicarán el número
de filas y columnas del arreglo. Los patrones para armar estarán representados por arreglos
bidimensionales tales que:

# F i _lPa as ≤t #rF o i _nlT a as b∧ #lCe or ol _uP m a ≤nt #rCa o son l _uT ma bn la e s r o


Un movimiento está representado por las coordenadas fila y columna de la ficha que se va a
desplazar hacia la casilla vacía. En el caso de un movimiento múltiple se indican fila y
columna de la ficha mas alejada a mover hacia la casilla vacía.

Sub-problema de verificar si un movimiento es válido.

Un sub-problema derivado del análisis descendente es el de determinar si un movimiento


suministrado por el usuario es válido. La forma de la función de validación es la siguiente

booleano func esValido (entrada vaciaX, vaciaY, fila, columna : entero)


{ Pre: ...PreEsValido...}
{ Post: ...PostEsValido...}

donde vaciaX y vaciaY indican las coordenadas de la posición actual de la ficha vacía y
fila, columna las coordenadas de la ficha que indica la movida, como fue especificado en el
aparte 3.

Sub-problema de verificar si se ha armado el patrón.

Según la descripción del problema, el objetivo se logra cuando se ensambla el patrón dado
en algún sitio del tablero. Para verificarlo, se deben examinar todas las submatrices del
tablero que son de tamaño igual al patrón buscado. El problema puede descomponerse de la
siguiente manera: compararArea - una función booleana que devuelve verdadero si el área
examinada se corresponde con el patrón buscado y el procedimiento detectarPatron - que
desplaza el chequeo por todo el tablero, donde área es una submatriz del tablero del tamaño
del patrón. Ambas acciones deben trabajar conjuntamente, de tal manera que el
procedimiento detectarPatron, indique si el patrón se encuentra en algún sitio del tablero
actual y si ese es el caso, nos suministre las coordenadas del mismo. Este tiene la siguiente
forma:

3
booleano proc detectarPatron (entrada tablero : arreglo de arreglos de colores;
entrada M, N: entero;
entrada patron: arreglo de arreglos de colores,
entrada Q, R: entero,
salida esta: booleano,
salida posX, posY: entero)
{ Pre: ...PreDetectarP...}
{ Post: ...PostDetectarP...}

donde, esta es una variable que indica el estado de la detección y posX, posY las
coordenadas de la esquina superior izquierda del patrón encontrado cuando la variable esta
es verdadero. Se sugiere implementar este procedimiento como una función cuyo valor de
retorno corresponda al parámetro esta y que posX y posY se implementen mediante un
parámetro de entrada-salida de tipo arreglo de tamaño 2.

El procedimiento detectarPatron como fue descrito anteriormente es ineficiente. Si se


analiza mejor el problema y se usan sus características particulares, podemos introducir
optimizaciones que mejoren sustancialmente su eficiencia. Se sugiere que piense al
respecto y trate de plantear sus propias mejoras. Posiblemente no siempre sea necesario
recorrer todo el tablero para encontrar el patrón.

Sub-problema de mover una ficha.

La solución a este sub-problema, realizarMovimiento – actualiza la estructura de datos del


tablero. A su vez, realizarMovimiento se puede apoyar en los procedimientos: moverAbajo,
moverArriba, moverDerecha, moverIzquierda según se requiera desplazar la(s) ficha(s)
indicadas por el usuario hacia abajo, arriba etc.

proc realizarMovimiento (entrada-salida tablero : arreglo de arreglos de colores;


entrada M, N: entero;
entrada fila, columna, vaciaX, vaciaY: entero)
{ Pre: ...PreRealizarM...}
{ Post: ...PostRealizarM...}

proc mover??? (entrada-salida tablero : arreglo de arreglos de colores;


entrada M, N: entero;
entrada fila, columna, vaciaX, vaciaY: entero)
{ Pre: ...PreMover?...}
{ Post: ...Post Mover?...}

El juego

Con los sub-problemas hasta ahora definidos, podemos completar el conjunto de


instrucciones necesarias para realizar varias sesiones de juego :

4
[ var tablero : arreglo de arreglos de colores;
M, N, fila, columna: entero;
patron : arreglo de arreglos de colores;
Q, R: entero;
abandona: booleano;
detectado: booleano;
otro: booleano;

otro := verdad;
do otro →
tomar patron y tablero inicial;
dibujar tablero inicial;
detectado := detectarPatron(tablero, M, N, patron, Q, R, fila, columna, esta, posX, posY);
abandona := falso;
do ¬detectado ∧¬abandona →
solicitar al usuario su próxima jugada, donde jugada es abandonar o dar un
movimiento y, de acuerdo con esto, dar valores a fila, columna y abandona;
if esValido(vaciaX, vaciaY, fila, columna) →
realizarMovimiento(tablero, M, N, fila, columna, vaciaX, vaciaY)
dibujar el movimiento;
detectado := detectarPatron(tablero, M, N, patron, Q, R, fila, columna, esta,
posX, posY);
 ¬esValido(vaciaX, vaciaY, fila, columna) →
dar mensaje de error
 abandona →
skip
fi
od

if detectado →
felicitar y resaltar
 abandona →
skip
fi
preguntar al usuario si quiere armar otro rompecabezas
y almacenar respuesta en “otro”;
od
]

3. La interfaz

Con las actividades anteriores hemos diseñado un programa que captura la parte lógica o de
cálculo del problema del Rompecabezas. Necesitamos ahora instrucciones que manejen la
parte de interfaz. En esta parte introduciremos los elementos de interfaz requeridos, para
que el programa pueda ser utilizado efectivamente.

5
Para facilitar la programación vamos a limitar las operaciones de entrada de información
por teclado en modo texto. Para el despliegue de información utilizaremos dos modos de
manera conjunta: modo texto y modo gráfico.

Repasando los requerimientos del programa, vemos que inicialmente el programa debe
solicitar del jugador su nombre. Esto lo podemos hacer usando instrucciones provistas por
el lenguaje para tal fin y almacenar el nombre en una variable NombreJugador de tipo
secuencia de caracteres. Luego, cada vez que se requiera, se puede desplegar un mensaje
como:

Por favor <NombreJugador> , introduzca un movimiento:

También podríamos dar mensajes en caso de que una jugada no sea válida. Al final de cada
sesión, se puede utilizar el nombre del jugador para preguntar si se desea jugar de nuevo.

Para la salida gráfica del programa se utilizará la Maquina de Trazados, con la que es
posible dibujar las diferentes figuras geométricas requeridas por el programa.

El primer procedimiento necesario es dibujarTablero , que dibuja un tablero vacío para


colocar el rompecabezas. El tamaño del tablero puede establecerse a conveniencia. Tiene la
siguiente forma:

proc dibujarTablero(entrada-salida mt: Maquina de trazados;


entrada tablero : arreglo de arreglos de colores;
entrada M, N: entero)

Análogamente, se requiere un procedimiento dibujar_patrón que despliegue el patrón


generado.

Por otra parte necesitamos obtener del usuario el movimiento que desea realizar, incluida la
posibilidad de abandonar. El procedimiento correspondiente tendrá la siguiente forma:

proc obtenerMovimiento(salida abandona: booleano;


salida fila, columna: entero)

Cada vez que se realice un movimiento, hay que actualizar el tablero. El procedimiento
correspondiente es como sigue:

proc dibujarMovimiento(entrada-salida mt: Maquina de trazados;


entrada tablero : arreglo de arreglos de colores;
entrada M, N, fila, columna, vaciaX, vaciaY: entero)

Finalmente, necesitamos preguntar al usuario si desea armar un nuevo rompecabezas, y si


ese es el caso, preguntarle el nombre del patrón que desea armar. El procedimiento
podemos declararlo de la siguiente manera:

proc otraSesion(salida otro: booleano;

6
salida nombrePatron: secuencia de caracteres)

Para implementar los procedimientos con varias variables de salida, se recomienda seguir la
sugerencia indicada en el aparte 5 para detectarPatron. A continuación la versión final de
nuestra solución incluyendo los procedimientos de interfaz:

[ var tablero : arreglo de arreglos de colores;


M, N, fila, columna: entero;
patron : arreglo de arreglos de colores;
Q, R: entero;
mt: MaquinaTrazados;
abandona: booleano;
detectado: booleano;
otro: booleano;

otro := verdad;
do otro →
tomar patron y tablero inicial;
dibujarTablero(mt, tablero, M, N);
detectado := detectarPatron(tablero, M, N, patron, Q, R, fila, columna, esta, posX, posY);
abandona := falso;
do ¬detectado ∧¬abandona →
obtenerMovimiento(abandona, fila, columna);
if esValido(vaciaX, vaciaY, fila, columna) →
realizarMovimiento(tablero, M, N, fila, columna, vaciaX, vaciaY)
dibujarMovimiento(mt, tablero, M, N, fila, columna, vaciaX, vaciaY);
detectado := detectarPatron(tablero, M, N, patron, Q, R, fila, columna, esta,
posX, posY)
 ¬esValido(vaciaX, vaciaY, fila, columna) →
dar mensaje de error
 abandona →
skip
fi
od

if detectado →
felicitar y resaltar
 abandona →
skip
fi

otro := otraSesion(otro, nombrePatron);


od
]

7
4. Material de apoyo

Cualquier material requerido para la realización del proyecto estará disponible en la pagina
web del curso.

5. Entregas

Primera entrega (Semana 10)

a) Traducción a GCL del programa de la sección 8, utilizando procedimientos y


funciones dummy o stubs, los cuales simplemente retornan un valor arbitrario del
tipo correcto o un mensaje indicando la operación que realizan, para probar el flujo
de control de un programa principal. Este programa debe mostrar el tablero inicial.

b) Especificación formal (incluidos los invariantes), cuerpo del procedimiento y


prueba de correctitud para el procedimiento MoverAbajo.

Segunda entrega (Semana 11)

a) Traducción a GCL de los procedimientos realizarMovimiento y dibujarMovimiento.


b) Especificación formal (incluidos los invariantes) y cuerpo del procedimiento para
compararArea.

Entrega final (Semana 12)

Para el día de la revisión usted deberá llevar una versión operativa del programa en GCL
que permita armar rompecabezas según las especificaciones indicadas en el planteamiento
del problema. Es importante que el equipo trabaje de manera integrada. Durante la revisión
se verificará el funcionamiento del programa y se hará un corto interrogatorio particular a
ambos miembros del equipo.

La versión operativa del programa deberá ser entregada al profesor en un disquete


debidamente identificado.

Además, deberá entregar un informe que describa en detalle todo el proceso de diseño y
desarrollo de la solución. El informe no se debe limitar a reportar las actividades indicadas
en este enunciado; las actividades deben servir de guía para elaborar el informe.

En este sentido, se quiere que se entregue un informe que contenga las siguientes secciones:

• Portada. Una hoja que contenga:


o Arriba a la izquierda el nombre de la universidad, carrera y materia.
o Centrado: nombre del proyecto.
o Abajo a la izquierda: nombre del profesor de laboratorio.

8
o Abajo a la derecha: nombre y carnet de los integrantes del equipo
• Introducción:
o Breve descripción del problema atacado en el proyecto.
o Contenido del informe.
• Diseño:
o Resultado del análisis descendente del problema (Diferencias con el análisis
presentado en este instructivo).
o Demostraciones de correctitud (De las entregas).
o Optimizaciones.
• Detalles de implementacion.
o Estructuras de datos (Diferencias con el análisis presentado en este
instructivo).
o Código fuente documentado.
• Estado actual.
o Operatividad del programa: decir si funciona perfectamente o no. En caso
negativo, describir las anomalías.
o Manual de operación: nombre del archivo a ejecutar y modo de operar el
programa.
• Conclusiones.
o Resultados obtenidos.
o Dificultades presentadas.
o Recomendaciones.
• Bibliografía. Libros, revistas o cualquier recurso bibliográfico consultado. Una
referencia bibliográfica debe incluir la siguiente información:
o Libro: autor(es), nombre, editorial y año.
o Artículo: autor(es), nombre, revista, volumen, número y año.
o Página Web: autor(es), nombre, url, fecha última visita.

Los siguientes son ejemplos de referencias bibliográficas:

• Libro:
Blair, D.: Language and Representation in Information Retrieval.
Elsevier Science Publisher, 1990.
• Revista:
Isaacson, M.: What is SMDI?, Electronic Musician 9(6). p.79-82
• Página Web:
Sun Microsystems: The Java Tutorial. http://java.sun.com. Noviembre
2000.

Al inicio de la clase de la semana 12, usted deberá entregar en un sobre sellado y


debidamente identificado (nombre y carnet de los integrantes del equipo y nombre del
proyecto):

9
• Un disquete debidamente identificado (nombre y carnet de los
integrantes del equipo y nombre del proyecto) que contenga en el
directorio raíz los archivos .gcl de su proyecto. El disquette debe estar
libre de defectos físicos y de virus. En caso de que el disquete no tenga la
información requerida o presente algún tipo de defecto se asignará una
nota de cero (0) a la revisión de la corrida.
• Un informe según la estructura sugerida anteriormente.
• El listado del programa debidamente documentado.

Los sobres deberán ser entregados a las 12:30pm (3:30pm) en punto en el laboratorio de
clases. No se recibirán proyectos después de las 12:30pm (3:30pm).

10