EJ2 - Gamelib
Gamelib es una biblioteca de funciones para implementar videojuegos simples en Python.
Utilizaremos Gamelib para implementar los próximos trabajos prácticos, así que el objetivo de este ejercicio es hacer un juego simple para tomar práctica con las funcionalidades de Gamelib.
5 en línea
El 5 en línea es un juego muy simple, muy parecido al Ta-Te-Ti, pero con la diferencia de que el tablero es de 10 x 10 y se debe hacer 5 en línea para ganar.
Reglas
La grilla:
- El juego se desarrolla en una grilla rectangular de 10 filas y 10
columnas, formando así un total de
10 x 10
celdas. - Una celda puede estar vacía, o contener una
O
o unaX
.
El juego:
- El juego se juega de a 2 jugadores,
O
yX
, por turnos. - El primer turno es del jugador
O
, luegoX
y así sucesivamente. - En su turno, el jugador debe ubicar una
O
oX
(según corresponda) en una celda vacía de la grilla.
Resolución:
- El juego termina con un ganador cuando hay 5 celdas iguales no vacías en la misma fila o en la misma columna o en la misma diagonal, o en empate cuando no hay más celdas vacías.
Consigna
El objetivo de este ejercicio es implementar una aplicación gráfica que permita a 2 jugadores jugar al 5 en línea.
Requerimientos mínimos:
- Dibujar la grilla
- Mostrar de quién es el turno
- Cuando se hace click en una celda vacía, ubicar una
O
o unaX
según corresponda - Validaciones necesarias para que el programa no se cierre de forma inesperada ante errores de código
NO es necesario que el juego detecte cuando hay 5 en línea. Lo único necesario es que la aplicación permita poner círculos y cruces en una grilla como si los jugadores utilizaran una hoja y un lápiz.
Cómo utilizar Gamelib
Para utilizar Gamelib, el primer paso es descargar
gamelib.py
y ubicarlo en la misma carpeta que 5_en_linea.py
.
Luego, en 5_en_linea.py
recomendamos arrancar con el siguiente código y
modificarlo a gusto:
import gamelib
ANCHO_VENTANA = 300
ALTO_VENTANA = 300
def juego_crear():
"""Inicializar el estado del juego"""
return '???'
def juego_actualizar(juego, x, y):
"""Actualizar el estado del juego
x e y son las coordenadas (en pixels) donde el usuario hizo click.
Esta función determina si esas coordenadas corresponden a una celda
del tablero; en ese caso determina el nuevo estado del juego y lo
devuelve.
"""
return juego
def juego_mostrar(juego):
"""Actualizar la ventana"""
gamelib.draw_text('5 en línea', 150, 20)
def main():
juego = juego_crear()
# Ajustar el tamaño de la ventana
gamelib.resize(ANCHO_VENTANA, ALTO_VENTANA)
# Mientras la ventana esté abierta:
while gamelib.is_alive():
# Todas las instrucciones que dibujen algo en la pantalla deben ir
# entre `draw_begin()` y `draw_end()`:
gamelib.draw_begin()
juego_mostrar(juego)
gamelib.draw_end()
# Terminamos de dibujar la ventana, ahora procesamos los eventos (si el
# usuario presionó una tecla o un botón del mouse, etc).
# Esperamos hasta que ocurra un evento
ev = gamelib.wait()
if not ev:
# El usuario cerró la ventana.
break
if ev.type == gamelib.EventType.KeyPress and ev.key == 'Escape':
# El usuario presionó la tecla Escape, cerrar la aplicación.
break
if ev.type == gamelib.EventType.ButtonPress:
# El usuario presionó un botón del mouse
x, y = ev.x, ev.y # averiguamos la posición donde se hizo click
juego = juego_actualizar(juego, x, y)
gamelib.init(main)
Este código sirve como punto de partida para implementar el juego. Prestar
atención a las funciones juego_crear
, juego_actualizar
y juego_mostrar
que son los lugares donde seguramente habrá que agregar código para implementar
el 5 en línea.
En las funciones juego_crear
y juego_actualizar
tenemos que manipular el
estado del juego. Esto es similar a lo que hicimos en el TP1, y no debería
involucrar llamadas a funciones de Gamelib.
En la función juego_mostrar
tenemos que utilizar las funciones de Gamelib
para dibujar el tablero y mostrar de quién es el turno. ¡No es necesario dibujar nada muy
sofisticado! Debería ser suficiente con utilizar las funciones draw_text
y
draw_rectangle
/draw_line
. Leer la
referencia de Gamelib para saber
qué funciones ofrece y cómo utilizarlas.
La función main
se encarga del resto de la funcionalidad del juego. No
debería ser necesario modificarla, a menos que quieras cambiar algo en el
funcionamiento general del juego. Se permite también cambiar el valor de las
constantes ANCHO_VENTANA
y ALTO_VENTANA
Otras consideraciones de gamelib y la implementación de las funciones
Gamelib se maneja con píxeles. El origen de coordenadas está arriba a la
izquierda en la ventana. Esto sería lo que recibe la función
juego_actualizar()
*--→ x
|
↓
y
Más allá de esto, la estructura del juego de juego_crear()
no deberia tener
información de pixeles. Caso contrario, se estaría mezclando mucho la lógica
del juego con lo que es dibujar con gamelib.
Para pensarlo de una forma más simple: el juego_crear
debería devolver una
estructura para la cual se pueda llevar adelante un 5_en_linea
, implementado
con cualquier interfaz, sea gamelib, terminal, o demás.
Entonces pensemos el segundo problema: "¿cómo dibujo y actualizo el juego?". La respuesta a esto es utilizando un par de transformaciones usando simples cuentas:
- Transformación de coordenadas de una grilla a pixeles de gamelib.
- Transformación de pixeles de gamelib a coordenadas de una grilla.
La primera se usaría para el juego_mostrar
, y la segunda para
juego_actualizar()
.
Por ejemplo, suponiendo que tengo las siguientes constantes para un TaTeTi:
ANCHO_VENTANA = 300
ALTO_VENTANA = 300
DIM_GRILLA = 3
Si se cuentan con los siguientes índices (i,j)
que corresponden a una posición
en la grilla m[i][j]
, si quiero obtener la posición en pixeles (x,y)
para
dibujarla, haría lo siguiente:
i = 2
j = 1
x = j * (ANCHO_VENTANA // DIM_GRILLA)
y = i * (ALTO_VENTANA // DIM_GRILLA)
De la misma forma, si tengo posiciones en pixeles (x,y) y quiero obtener los índices
(i,j) para actualizar la grilla en la posicion m[i][j]
, haria lo siguiente:
x = 213
y = 105
j = x // (ANCHO_VENTANA // DIM_GRILLA)
i = y // (ALTO_VENTANA // DIM_GRILLA)
Y por último, ya varias veces vimos esta cuenta (ANCHO_VENTANA // DIM_GRILLA)
. Para
dejar el codigo más claro, podemos asignarle un nombre y moverla a otra constante.
ANCHO_CELDA = (ANCHO_VENTANA // DIM_GRILLA)
ALTO_CELDA = (ALTO_VENTANA // DIM_GRILLA)
Entrega
Se deberá entregar el archivo 5_en_linea.py
:
- El código debe enviarse utilizando el formulario de entregas.
- Salvo que su corrector se los indique, las entregas seran siempre de forma virtual y no en papel, y las corecciones mediante GitHub.