Proyecto 1: Bingo

Usted y su colega han sido contratados por una pequeña empresa de juegos de azar. La empresa quiere personalizar más su juego de bingo. Actualmente utiliza los clásicos cartones de 25 celdas numeradas que se adquieren en algunas librerías. En la empresa quieren tener la capacidad de cambiar el diseño por uno propio, y en algunos eventos los números por otros símbolos. Por ejemplo, para un "té de canastilla" quieren permitir al cliente escoger las 75 palabras y fabricar cartones con ellas. La empresa necesita un software que además de generar cartones con palabras o símbolos arbitrarios, sea capaz de "cantar el bingo", es decir, simular una sesión de juego. Finalmente la compañía espera que durante dicha sesión el software indique cuándo y cuáles han sido los cartones ganadores de acuerdo a las diferentes modalidades de juego: cuatro esquinas, línea/diagonal, L, o cartón lleno.

El proyecto se ha dividido en varias fases. En la primera se pretende hacer un comando que realice el trabajo descrito anteriormente. Si la fase es exitosa, la empresa les podría recontratar para hacer una versión de la aplicación con interfaz gráfica más amigable para los empleados. Una tercera fase podría darse para implementar una versión web, donde los jugaores de bingo compren y jueguen en línea. Este enunciado de proyecto sólo cubre la primera fase, es decir, se debe implementar un comando que realice lo siguiente:

$ bingo --help
Create Bingo cards and simulate game sessions. Usage:
bingo generate NUMBER card.svg [-s symbols.txt] [-c callingcard.svg]
bingo call [card.idx] [-t type] [-s symbols.txt] [-w winners]

Actions:
  generate               Generate random cards in current directory
  call                   Simulate a bingo game session

Options:
  NUMBER                 Number of cards to be generated
  card.svg               Graphic design of the card with ${field} placeholders
  card.idx               Contains all the generated cards and their 24 or 25 symbols
  -c callingcard.svg     Fill out the given calling card with all 75 symbols
  -s symbols.txt         Use the given 75 symbols instead of numbers
  -t type                Pattern to be matched in order to win. Valid types:
                         four-corners, straight-line, diagonal, any-line, roving-L, blackout
  -w winners             Number of winners until finish the session

Ustedes y el personal de la pequeña empresa, han acordado tres avances de proyecto o entregables. Uno cada semana, de tal forma que permita a la empresa percibir el avance del software y poder decidir mejoras o cambios si fuese necesario. Los entregables se describen en las siguientes secciones. Los porcentajes de cada entregable indican el porcentaje de pago que la empresa les dará del proyecto total.

Entregable 1: 08-Nov-14 [30%]

En esta fase se implementa la capacidad de generar cartones de bingo. El siguiente ejemplo de invocación

$ bingo generate 1000 classic.svg

indica al comando que debe generar 1000 cartones de bingo utilizando el diseño incluido en el archivo classic.svg, al cual llamaremos la plantilla del cartón. La pequeña empresa cuenta con un diseñador gráfico que realiza el diseño de los cartones en un programa de ilustración. El diseño lo almacena en un archivo de gráficos vectoriales redimensionables (SVG, Scalable Vector Graphics). Es un archivo de texto en formato XML, similar al HTML, con la diferencia de que en lugar de ser elementos de una página web (como párrafos/imágenes/enlaces), son de diseño, como rectángulos, curvas, y efectos.

El comando bingo no debe afectar el diseño gráfico del cartón, sino, su contenido. El contenido del cartón son 25 ó 26 textos: el número de cartón, y los números/símbolos que deben ser escritos en las celdas (seleccionados al azar). Por acuerdo con la empresa, el diseñador gráfico marcará estos campos dentro del archivo SVG con el formato ${field}. El campo ${card_number} debe ser cambiado con el número de cartón, y los 24 ó 25 campos ${N,M} con el número o símbolo escogido al azar para la fila número N y columna número M. La celda del centro del cartón se marca con ${center}, que podría ser reemplazada por un carácter (como un asterisco) o un número si se quiere el cartón de 25 números. El cartón resultante de reemplazar todos los campos ${field} es otro archivo SVG, que debe ser almacenado en la misma carpeta donde se encuentra la plantilla del cartón. Los nombres de los cartones generados son escogidos por los desarrolladores, por ejemplo, classic-0001.svg, classic-0002.svg, ..., classic-1000.svg.

Ejemplo de un cartón básico de Bingo para números disponible en bingo_cards.zip

El ejemplo de invocación anterior, bingo generate 1000 classic.svg, no indica los símbolos a utilizar en las celdas, por tanto, el comando asumirá que son los números de 1 a 75. El usuario puede indicar al comando utilizar otros símbolos con el parámetro -s. Por ejemplo:

$ bingo generate 240 babyshower.svg -s babywords.txt

indica que se deben generar 240 cartones de bingo, utilizando la plantilla babyshower.svg y en lugar de utilizar números de 1 a 75, se deben utilizar las palabras almacenadas en babywords.txt. El archivo de símbolos (babywords.txt) debe contener 75 líneas no vacías. Si tuviera menos es un error, su tuviera más se ignoran a partir de la 76. Cada línea representa un símbolo, el cual puede ser un texto o [opcional] un nombre de un archivo de imagen. En cualquier caso, los 75 símbolos (sean números, palabras o nombres de archivos de imagen), se deben dividir en 5 grupos. Cada grupo se asigna a una única columna en los cartones. Por ejemplo, los primeros 75/5==15 números (de 1 a 15) se deben asignar al azar únicamente en la primera columna (usualmente rotulada con "B"), los siguientes 15 números (de 16 a 30) en la segunda columna (rotulada con "I") y así sucesivamente.

Note además que como cualquier otro comando, bingo debe ser capaz de mostrar ayuda con el parámetro --help, y su versión e información de contacto de los desarrolladores con el parámetro --version.

Dado que este entregable es parte de la primera fase del proyecto y el código se piensa reutilizar para las aplicaciones gráficas y en web, ustedes deben desarrollar clases reutilizables. Utilice la Biblioteca Estándar de C++ cuanto sea posible. El primer entregable consta de:

  1. [10%] Un diseño de clases participantes en la solución.
  2. [10%] Un comando capaz de analizar parámetros, imprimir ayuda y su versión.
  3. [20%] Capacidad de generar N número de cartones válidos (sin números repetidos) en la carpeta actual.
  4. [20%] Capacidad de "parsear" la plantilla SVG y convertir los campos por valores válidos.
  5. [20%] Capacidad de utilizar símbolos almacenados en un archivo de 75 líneas.
  6. [10%] Aplicación de buenas prácticas de desarrollo de software: documentación, apego a una convención de estilo, etc.
  7. [10%] Manejo de errores: archivos no existentes, número de símbolos menores a 75, etc.
  8. [10% Opcional] Los símbolos pueden ser nombres de archivos de imágenes en lugar de palabras.
  9. [10% Opcional] No genera cartones repetidos, es decir, cartones que contienen los mismos símbolos aunque en posiciones distintas.

Entregable 2: 17-Nov-14 [30%]

Al final de la primera semana la empresa tendrá la capacidad de producir cartones de bingo e imprimirlos. Para la segunda semana, la pequeña empresa espera poder jugar bingo al estilo tradicional, es decir, cantar los números o los símbolos sea usando una tómbola, fichas o una computadora.

Cantar el bingo con fichas

Para cartones numerados de 1 a 75 la empresa dispone de tómbolas con bolas numeradas. Pero para cartones de símbolos escogidos por el usuario, se deben fabricar las fichas manualmente. Es deseable que el programa produzca un cartón de fichas para cantar el bingo. Este cartón contiene todos los 75 símbolos. Después de impreso y recortado, se le brinda al locutor para que escoja las fichas al azar mientras canta el bingo. Su programa produciría este cartón al invocarse con la opción -c, por ejemplo:

$ bingo generate 240 babyshower.svg -s babywords.txt -c callingcard.svg

Al ejecutar el comando anterior, el programa bingo además de producir 240 cartones, produciría un archivo babywords-callingcard.svg que contiene los 75 símbolos obtenidos de babywords.txt, pero con un formato diseñado para impresión y recorte. El archivo callingcard.svg es similar al de un cartón usual de bingo, con la diferencia de que no contiene 25 celdas, sino 75 celdas con el campo ${symbol}. Cada campo debe reemplazarse por uno de los 75 símbolos. Las celdas están diseñadas para recortarse y producir las fichas que el animador del evento utilizará para escoger al azar y cantar el bingo.

Ejemplo de un cartón de fichas disponible en bingo_cards.zip

Si el usuario olvida generar el cartón de fichas durante la generación de los cartones de bingo, podrá hacerlo posteriormente, indicando lo único necesario: el archivo de símbolos (con el parámetro -s) y el diseño del cartón de fichas (con el parámetro -c). Por ejemplo:

$ bingo generate -s babywords.txt -c callingcard.svg

El nombre del cartón de fichas resultante será la combinación del nombre del archivo de símbolos, un guión, y el nombre y extensión del archivo de diseño del cartón de fichas. En la invocación anterior corresponde a babywords-callingcard.svg.

Cantar el bingo con computadora

Si no se tiene una tómbola o fichas, o el evento es virtual, resulta conveniente o necesario que la computadora "cante el bingo". Por ejemplo, la invocación

$ bingo call

provocará que el programa cante al azar los números entre 1 y 75. Los números (o símbolos) se cantan en modo interactivo. Es decir, el programa escoge un número o símbolo al azar, lo presenta pero no pasa al siguiente de inmediato, sino que espera a que se presione la tecla Entrar antes de continuar con el siguiente número o símbolo. En cada iteración se imprime además el estado de las fichas. Por ejemplo:

$ bingo call
Bingo call for symbols 1 through 75
Press ENTER key to call next number...

B -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
I -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
N -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
G -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
O -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

Number: 65 

B -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
I -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
N -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
G -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
O -- -- -- -- 65 -- -- -- -- -- -- -- -- -- --

Number: 25 

B -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
I -- -- -- -- -- -- -- -- -- 25 -- -- -- -- --
N -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
G -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
O -- -- -- -- 65 -- -- -- -- -- -- -- -- -- --

Number: 73 
...

El programa termina su ejecución al acabar todos los números/símbolos o si el usuario ingresa el carácter EOF en lugar de presionar un Enter. En la invocación anterior no se especificó un archivo de símbolos, por lo que se utilizaron números de 1 a 75. Si se especifica un archivo de símbolos, estos ocuparán mucho espacio horizontal, por lo que se debe transponer la salida. Por ejemplo:

$ bingo call -s animalescr.txt
Bingo call for symbols animalescr.txt
Press ENTER key to call next symbol...

B                  I                  N                  G                  O
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------

Symbol 1: tepezcuintle

B                  I                  N                  G                  O
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ tepezcuintle------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------

Symbol 2: cuyeo

B                  I                  N                  G                  O
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ tepezcuintle------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ cuyeo------------- ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------
------------------ ------------------ ------------------ ------------------ ------------------

Symbol 3: zompopa
...

El algoritmo para seleccionar los números o símbolos debe ser eficiente. Prefiera algoritmos de shuffle. Note que en la impresión del tablero de fichas, cada número o ficha ocupa un campo de tamaño fijo. Para los símbolos el programa deberá determinar el ancho máximo, el cual puede variar de un listado a otro de símbolos.

Generar archivo de índice

Hasta el momento, cuando se le pide al programa generar cartones de bingo, este produce N cartones en archivos SVG. Sin embargo en el próximo entregable, el programa deberá ser capaz de indicar inmediatamente cuándo un cartón ha ganado. El programa necesita información de cada cartón, pero buscar en los archivos SVG es impráctico e ineficiente.

Durante la generación de los cartones, el programa deberá además generar un archivo de índice. La función de este archivo es permitir al programa saber rápidamente cuáles números o símbolos componen a cada cartón. O dicho de otra forma, es un archivo que permite rápidamente cargar o recuperar los cartones que fueron generados previamente. El archivo de índice deberá tener el mismo nombre de la plantilla del cartón, pero con la extensión .idx. La invocación

$ bingo generate 1000 classic.svg

genera 1000 archivos en la forma classic-I.svg, donde I va de 0001 a 1000, y además un archivo classic.idx, con los 24 ó 25 números que componen a cada cartón. La invocación

$ bingo generate 240 babyshower.svg -s babywords.txt

debe generar 240 archivos SVG y un archivo babyshower.idx con los 24 ó 25 símbolos que componen a cada cartón.

Resumen del entregable

En resumen el segundo entregable consta de

  1. [20%] Genera el archivo de fichas con la opción -c. Usa el nombre de archivo convenido. Reemplaza todos los campos ${symbol}.
  2. [5%] Permite generar el archivo de fichas de la forma: bingo generate -s symbols.txt -c callingcard.svg.
  3. [25%] Canta el bingo con números de 1 a 75. Espera a que se presione ENTER antes de pasar al próximo número. Imprime el estado del tablero de fichas en cada número seleccionado. Repite hasta acabar los números o hasta que se ingrese EOF. Utiliza el algoritmo shuffle.
  4. [25%] Canta el bingo con símbolos de un archivo externo. Imprime el estado del tablero en forma transpuesta al de números. Formatea los símbolos usando campos. Calcula el tamaño máximo del campo dinámicamente.
  5. [20%] Genera un archivo de índice con el contenido de los cartones para su carga posterior. Explica en la documentación la estructura escogida del archivo de índice.
  6. [5%] Documenta todas las clases, atributos y métodos con Doxygen.

Entregable 3: 24-Nov-14 [40%]

En la tercera semana la pequeña empresa espera que el programa mientras canta los números o los símbolos, sea capaz de indicar de inmediato cuáles son los cartones ganadores. Esta funcionalidad es útil para los juegos presenciales, pero realmente indispensable para los juegos virtuales. Para que el programa pueda determinar cartones ganadores, se le debe indicar dos parámetros: el archivo de índice y el tipo de juego, por ejemplo:

$ bingo call basic_v.idx -t four-corners

Indica al programa que debe cantar números de 1 a 75, y reporte cada vez que un cartón contenido en el índice basic_v.idx llene sus cuatro esquinas. La siguiente podría ser una sesión hipotética

$ bingo call basic_v.idx -t four-corners
Four corners Bingo call for symbols 1 through 75
Press ENTER key to call next number...

[...]

Number: 48 

B -- -- 03 04 -- -- -- -- 09 10 -- -- -- 14 --
I -- -- -- -- 20 -- -- -- -- 25 26 -- -- -- --
N -- -- 33 -- 35 -- -- -- -- -- 41 -- 43 -- --
G -- -- 48 49 -- -- -- 53 -- 55 -- 57 58 -- --
O 61 -- 63 -- 65 66 67 68 69 70 -- -- 73 74 --

Number: 15
B I N G O ! ! !
Winner 1: card # 0869


B -- -- 03 04 -- -- -- -- 09 10 -- -- -- 14 15
I -- -- -- -- 20 -- -- -- -- 25 26 -- -- -- --
N -- -- 33 -- 35 -- -- -- -- -- 41 -- 43 -- --
G -- -- 48 49 -- -- -- 53 -- 55 -- 57 58 -- --
O 61 -- 63 -- 65 66 67 68 69 70 -- -- 73 74 --

Number: 28 

B -- -- 03 04 -- -- -- -- 09 10 -- -- -- 14 15
I -- -- -- -- 20 -- -- -- -- 25 26 -- 28 -- --
N -- -- 33 -- 35 -- -- -- -- -- 41 -- 43 -- --
G -- -- 48 49 -- -- -- 53 -- 55 -- 57 58 -- --
O 61 -- 63 -- 65 66 67 68 69 70 -- -- 73 74 --

Number: 6 
B I N G O ! ! !
Winner 2: card # 0448
Winner 3: card # 0107
Press ENTER key to call next number...EOF
$

El parámetro -t type indica el tipo de patrón que se debe llenar para ganar un bingo. Los valores válidos se listan a continuación. Si no se especifica el parámetro -t se asume cartón lleno (blackout).

  1. four-corners. Debe llenar las cuatro esquinas del cartón.
  2. straight-line. Cualquiera de las 10 líneas horizontales o verticales del cartón.
  3. diagonal. Cualquiera de las dos diagonales del cartón.
  4. any-line. Cualquiera de las 12 líneas horizontales, verticales o diagonales.
  5. roving-L. Cualquiera de las cuatro L.
  6. blackout. Cartón lleno (por defecto).

Si se especifica el parámetro -s symbols.txt, en lugar de cantar números de 1 a 75, se escogen al azar los símbolos encontrados en el archivo. El estado de las fichas se presenta de forma transpuesta.

Por defecto, el programa continúa cantando números aunque algún cartón haya ganado un bingo. El programa se detiene hasta que se ingrese EOF o se acaben los 75 símbolos. Sin embargo, en algunas circustancias, el organizador de un evento de bingo podría tener un número limitado de premios. Por ejemplo, si se tienen premios para los primeros 5 cartones que llenen una L en un bingo de términos de programación, se podría invocar:

$ bingo call programming.idx -s programming.txt -t roving-L -w 5

La instrucción siguiente le indica al programa cantar el bingo utilizando símbolos de programación; avisar cuándo un cartón llena una L; y terminar la ejecución cuando 5 cartones (distintos) hayan ganado, es decir, cuando se tengan 5 ganadores (winners) y por tanto, se hayan repartido los 5 premios.

Resumen del entregable

En resumen el tercer entregable consta de

  1. [10%] Carga cartones del archivo de índice. Recibe el nombre del archivo por parámetro. El índice es una estructura de datos eficiente para el cargado.
  2. [30%] Indica de inmediato cuando hay uno o más cartones ganadores al cantar cada número. Un cartón ganador no se reporta dos o más veces. Se indica la cantidad de cartones ganadore en cada ocasión. El algoritmo para detección de los cartones ganadores es relativamente eficiente.
  3. [10%] Reporta cartones ganadores cuando se trabaja con un archivo de símbolos.
  4. [40%] Reacciona correctamente al parámetro -t. Reporta los cartones ganadores de acuerdo a cada uno de los seis tipos de patrones de juego: four-corners, straight-line, diagonal, any-line, roving-L, blackout.
  5. [10%] Si se especifica el parámetro -w con un número N, termina la ejecución del programa cuando se han encontrado N ganadores.