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, embargo, podrán realizarse realizarse simultáneamente simultáneamente varios de estos movimientos movimientos,, siempre 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 in_lT a as b∧ #lCe
or ol _uP m a ≤nt #rCa o son l _uT ma
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, r ealizarMovimiento 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 , 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: 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. o
8
Abajo a la derecha: nombre y carnet de los integrantes del equipo Introducción: Breve descripción del problema atacado en el proyecto. o Contenido del informe. o Diseño: o Resultado del análisis descendente del problema (Diferencias con el análisis presentado en este instructivo). Demostraciones de correctitud (De las entregas). o Optimizaciones. o Detalles de implementacion. Estructuras de datos (Diferencias con el análisis presentado en este o instructivo). Código fuente documentado. o Estado actual. o Operatividad del programa: decir si funciona perfectamente o no. En caso negativo, describir las anomalías. Manual de operación: nombre del archivo a ejecutar y modo de operar el o programa. Conclusiones. Resultados obtenidos. o Dificultades presentadas. o Recomendaciones. o Bibliografía. Libros, revistas o cualquier recurso bibliográfico consultado. Una referencia bibliográfica debe incluir la siguiente información: Libro: autor(es), nombre, editorial y año. 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. o
•
•
•
•
•
•
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