(!!) Ahora disponible de forma remota en https://cazarrecompensas-cyan-piano.reflex.run gracias a la nube de Reflex.
- Introducción
- Manual
- Pre-requisitos
- Instalación
- Uso
- Metodología
- Descripción técnica
- Requisitos funcionales, no funcionales y NOT LIST
- Casos de Uso
- Arquitectura de la aplicación
- Diseño
- Diagrama de Componentes
- Implementación
- Tecnologías y Herramientas utilizadas
- Pruebas
- Análisis del tiempo invertido
- Conclusiones
- Posibles mejoras
- Dificultades
- Uso de Inteligencia Artificial
Cazarrecompensas es publicado por MMSS99 y Desteveco como resultado del proyecto Quien es Quién al estilo death march, a evaluar durante la 1º evaluación del CS de Desarrollo de Aplicaciones Multiplataforma (IES TEIS, 2024-2025).
El proyecto está escrito enteramente en Python 3.12.6, haciendo uso del framework Reflex para los eventos que controlan la lógica, la representación visual y la ejecución de la aplicación.
Python3
Git
pip3
Recomendamos utilizar un entorno virtual para instalar y manejar las dependencias del programa.
- Abre una terminal en la carpeta donde desees descargar el juego. Desde ella, clona el repositorio, que creará su propia carpeta Cazarrecompensas.
PS git clone https://github.com/MMSS99/Cazarrecompensas.git
- Entra a la carpeta Cazarrecompensas, crea un entorno virtual de Python, actívalo e instala los requerimientos.
PS cd Cazarrecompensas
PS py -m venv venv
PS .\venv\Scripts\activate
(venv) PS pip install -r requirements.txt
- Inicializa Reflex y lánzalo: el juego se ejecutará como una página localhost, cuya dirección aparecerá en la terminal.
(venv) PS reflex init
(venv) PS reflex run
Cazarrecompensas presenta una interfaz gráfica intuitiva y fácilmente navegable basada en eventos Reflex. Un usuario que quiera jugar una partida seguirá los siguientes pasos:
- 1. Hará click sobre el botón 'Nueva Partida'.
(La lógica de la aplicación iniciará una nueva partida, aleatorizando el tablero de juego y escogiendo un personaje aleatorio que servirá de condición de victoria para el jugador)
- 2. Introducirá una pregunta sobre las caracterísitcas de un personaje...
... y hará click sobre el botón comprobar, que eliminará personajes dependiendo de si la característica preguntada se encuentra (o no) entre las características del personaje a buscar.
(La aplicación aceptará cualquier entrada. Si ningún personaje cuenta con la característica preguntada ni ningún sinónimo de la misma, la aplicación no mostrará cambios. El jugador podrá repetir este paso tantas veces como lo desee.)
- 3. Hará click sobre el personaje que crea ser la condición de victoria.
(La aplicación le mostrará si su adivinanza es correcta o incorrecta, y le permitirá iniciar una nueva partida o retornar al menú)
Durante los primeros 10 días del desarrollo del proyecto, adoptamos de forma natural una metodología de desarrollo en cascada; nuestro desconocimiento sobre Reflex alentó a nuestra decisión de comenzar a construir la aplicación de una forma lineal que nos permitiese preparar la lógica pura en Python puro mientras nos documentábamos sobre las posibilidades ofrecidas por Reflex, realizando cambios a nuestro prototipo de diseño pero no implementándolos hasta adquirir un nivel suficiente de competencia para hacerlo de forma cómoda y holgada.
Con el desarrollo del proyecto acercándose al fin de la fase de implementación, se nos impuso con obligatoriedad el uso de un marco de trabajo y metodología Kanban a través de los Projects de Github, que ralentizó considerablemente el flujo de desarrollo durante una corta fase de adaptación.
El usuario podrá esperar...
- Inicio de partida: La aplicación creará y mostrará un tablero de juego y escogerá a un personsaje aleatorio que el usuario deberá adivinar.
- Interacción via escrita con el juego: El usuario podrá escribir preguntas a la aplicación y enviarlas, recibiendo una respuesta visual.
- Finalizar una partida: Sea por una adivinanza interactuando con el tablero de juego o por la interacción con uno de los componentes de navegación, el usuario podrá finalizar o abandonar una partida cuando lo considere oportuno.
El usuario podrá disfrutar de...
- Interfaz intuitiva que requiere de poca o ninguna explicación para ser utilizada.
- Lógica blindada a prueba de entradas del usuario distintas.
- Funcionalidades multijugador.
- Hosting de la aplicación en red.
- Permanencia de datos, como puntuación.
A continuación aparecerán enlaces a el Proyecto de Github asociado a la aplicación:
- Caso #1: Iniciar una partida.
- Caso #2: Consultar atributos de personaje.
- Caso #3: Adivinar un personaje.
- Caso #4: Volver al menú
-
Capa de presentación:
- Aquí encontramos nuestra página índice (cazarrecompensas.py) y las dos páginas de función. Se encuentran escritas en su totalidad a través de Reflex.
-
Capa de servicio
- Capa dedicada a los rx.State, donde se encuentran los event handlers de los distintos eventos que Reflex puede enviar.
-
Capa de dominio
- interprete (Módulo) se encarga de recibir las entradas de texto de los usuarios, interpretarlas e inyectarlas a la lógica de la partida.
- partida (Módulo) usa la información recibida del interprete y la compara con la información guardada en las bases de datos para ejecutar funciones que producen cambios en la partida.
-
Capa de datos
- Modulos de Python que sólamente guardan constantes.
- Páginas rx.app y rx.State:
cazarrecompensas.py
/state_menu.py
(Rojo): El índice de las páginas, así como la página de entrada a la aplicación.quien_es_quien.py
/state_juego.py
(Naranja): La pantalla de juego, donde ocurre la magia.areadepruebas.py
/state_areapruebas.py
(Amarillo): Remanente del primer prototipo de la aplicación donde se pueden comprobar características de personajes. Mantenido para futura extensión.
- Partida (Azul):
personajes_aleatorios.py
: Toma los personajes depersonajes.py
y genera una lista aleatoria con los nombres de todos los personajes, así como uno que pasará a ser la condición de victoria.comparador_caracteristicas.py
: Compara las características sobre las que pregunta el jugador con el personaje condición de victoria y el resto de personajes.comprobador_caracteristicas.py
: Comprueba si cada uno de los personajes tiene o no la característica preguntada.personajes.py
: Alberga una constante con los personajes y sus características.
- Intérprete (Morado):
lector_input.py
: Toma el string de entrada del usuario y lo convierte a un formato legible para la lógica de Partida.limpiador_entrada.py
: Elimina cualquier espacio inservible, así como carácteres no interpretables por la lógica.estandarizador_valores.py
: Toma los valores desinonimos_caracteristicas.py
y los estandariza a un valor comparable con los almacenados enpersonajes.py
.sinonimos_caracteristicas.py
: Almacena sinónimos para características.
- Reflex (Framework base de la aplicación)
- Python
- Random (librería utilizada para elección aleatoria)
- Pytest y Coverage (TDD y pruebas de cobertura del backend)
- Time (librería utilizada para esperas en bucles)
- Asyncio (librería utilizada para código que requería constante actualización de las variables involucradas durante su ejecución)
- Adobe Photoshop 2023 (Creación de estilo de componentes y gráficos de documentación)
- Canva (Creación de gráficos de tiempo)
- Git y Github (Control de versiones)
- Markdown (Lo que estás leyendo)
El backend de la aplicación se encuentra escrito completamente en Python a través de prácticas TDD (coverage del 98% tras finalizado del desarrollo). Su tarea es simple, y se divide en dos pasos:
En la página quien_es_quien.py
, el usuario cuenta con un input donde puede introducir cualquier texto. Una vez presiona enter o hace click sobre el botón 'comprobar', su manuscrito inicia el siguiente viaje:
- En
limpiador_entrada.py
, cualquier carácter innecesario será eliminado:
def limpiador_entrada(entrada : str):
return (''.join(caracter for caracter in entrada.lower() if caracter.isalpha() or caracter.isspace()).strip())
- En
estandarizador_valores.py
, si la lógica encuentra una palabra que se encuentre en las claves de los diccionarios desinonimos_características.py
, la suplantará por su valor, que es el que podrá interpretar el móduloPartida
.
if termino.lower() == sinonimo:
return SINONIMOS_CLAVES[clave]
- Por último, en
lector_input.py
, el string enviado por el usuario será desgranado en una tupla que contendrá la [característica a comprobar], [su valor] y un booleano que indicará a la lógica si se debe comprobar que la característica es la indicada, o si NO es la indicada, si no la contraria.
negativo = False
clave = []
valor = []
...
return clave, valor, negativo
Tras recibir la información de la pregunta del usuario, la aplicación representará cambios dependiendo del contenido de la misma:
- Inicialmente, apoyándose en
personajes_aleatorios.py
,state_juego.py
habrá creado un tablero de juego aleatorio formando una lista compuesta de todas las claves del diccionario de menor profundidad depersonajes.py
y escogido de entre ellas al personaje condición de victoria. comprobador_caracteristicas.py
usará la entrada recibida delector_input.py
para comprobar si cada uno de los personajes tiene o no la característica buscada por el jugador, guardando ese valor:
if valor_entrada == PERSONAJES[personaje][clave_entrada]:
if es_negativo == False:
return "Si"
if es_negativo == True:
return "No"
else:
if es_negativo == True:
return "Si"
if es_negativo == False:
return "No"
(También es el encargado de atrapar errores, en caso de que el usuario envíe una pregunta que la aplicación no sepa responder.)
try:
...
except IndexError:
return "No sé cómo responder a eso"
comparador_caracteristicas.py
comenzará de inmediato a comparar la respuesta decomprobador_caracteristicas.py
para cada uno de los personajes aún en el tablero contra la respuesta del personaje condición de victoria, modificando al lista de personajes (que controla el tablero) cuando encuentra discrepancias:
listacambiada = []
for personaje in listadepj:
...
return listacambiada
El frontend se encuentra escrito en Python a través del framework Reflex, que mimetiza componentes y estilos encontrados en HTML5 y CSS. Cuenta con dos partes:
... que será con la que el usuario interactúe, llena de botones y decoraciones, así como representaciones visuales de qué está pasando en el backend del código. Existen un total de tres páginas distintas, de las cuales una cuenta con dos permutaciones disintas.
... que se encuentra recogida en los States, y se encarga de modificar el estado visual de la página. Es también donde ocurre la conexión con la lógica de Python.
return rx.button(
on_click=State.salida("/"),
...
),
async def salida(self, direccion):
while self.blursalidaentrada < 100:
time.sleep(0.01)
self.blursalidaentrada += 1
yield
yield rx.redirect(direccion)
(Un test fue añadido a posteriori para alcanzar el 100% de cobertura en los módulos de lógica, ya que su contenido no fue contemplado durante el desarrollo.) -> Commit 6edcd23
Sólamente contamos con dos módulos independientes: limpiador_entrada.py
y personajes_aleatorios.py
.
El resto de los módulos de la lógica contienen dependencias entre ellos:
La información mostrada en los gráficos carece el nivel de precisión deseado, ya que en muchas ocasiones realizamos trabajo trasversal en forma de spikes. En la carpeta Wakatime hemos incluido capturas de nuestros registros de Wakatime, también terriblemente imprecisos por su forma de registrar la actividad (dejando atrás tiempo pasado navegando documentación, teorizando o creando componentes fuera de VS Code).
No recomendamos el uso de esta herramienta para futuros proyectos.
-
Predicción: 69 tokens (17 horas)
-
Real: 197 tokens (49 horas)
(Debido al desconocimineto del framework, la predicción de tiempo es extremadamente inexacta. Tareas que fueron predecidas a 2 tokens basándonos en conocimientos previos han llegado a consumir 20 en su implementación real.)
El trabajar con un framework es una experiencia interesante, pero intimidante cuando la evaluación de un trimestre de iniciación a la programación depende de ello. Por fortuna, contábamos con los conocimientos previos necesarias para coger al proyecto por los cuernos.
Reflex, en si, puede ser frustrante a la hora de preparar sus componentes y sus estilos, ya que como su compilación a otros lenguajes es automática, el grado de control sobre ellos es entre bajo y nulo. Creemos que es una clara demostración de las limitaciones y beneficios que un framework impone.
-
Implementacion de una Scoreboard donde se reflejen los mejores intentos, los cuales sean guardados entre lanzamientos de Reflex.
-
Seguimiento de las preguntas ya realizadas y atributos descartados para asegurar la coherencia en el juego.
-
Denegar la capacidad de adivinar a un personaje ya eliminado.
No hemos experimentado grandes dificultades (mas allá de trabajar por primera vez con Reflex) ya que no consiste en una logica elaborada, si no mas bien en algo divertido y funcional.
Falta de tiempo en clase, truncado por explicaciones poco relevantes para el estado de nuestro proyecto, provocó que nuestro ritmo de trabajo se descompasase: los mayores avances se dieron trabajando de forma remota.
... Y también, la redacción de la documentación del proyecto cuyos apartados pedían información que generó confusión tomó mucho más de lo esperado.
Redactado por Marcelo, quien está en contra del uso de la IA en el ámbito educacional. a 13 de Diciembre de 2024.
ChatGPT
, versión GPT-4.
Texto simple
- Apoyo al aprendizaje de Reflex (funcionamiento de componentes, colocación de estilos...)
De ninguna manera: toda la información obtenida de ChatGPT fue suplantada por una información de mayor calidad obtenida directamente de las fuentes ofrecidas por Reflex en sus páginas de documentación. No existe ninguna permanencia en el código recogido en este repositorio de influencias de Inteligencia Artificial.
... En este repo, apostamos por un código artesano.