Python es un lenguaje versátil que permite crear desde aplicaciones simples hasta proyectos complejos. A continuación, presento una colección de proyectos funcionales que puedes implementar para practicar tus habilidades de programación.
Aplicaciones de Interfaz Gráfica
1. Calculadora
Este proyecto implementa una calculadora gráfica con operaciones básicas usando la biblioteca Tkinter. El diseño incluye una pantalla de visualización y botones organizados en una matriz.
El principio fundamental es separar la lógica de presentación de la lógica de negocio. La calculadora utiliza una varible de tipo StringVar para mostrar los resultados y mantiene una lista de operandos y operadores.
A continuación se presenta una versión optimizada del código:
import tkinter
from tkinter import messagebox
import math
class InterfazCalculadora:
def __init__(self):
self.ventana = tkinter.Tk()
self.ventana.title("Calculadora Básica")
self.ventana.geometry("280x450")
self.ventana.resizable(False, False)
self.expresion = tkinter.StringVar()
self.expresion.set("0")
self.historial_operaciones = []
self.flag_operador = False
self.construir_menu()
self.construir_interfaz()
self.ventana.mainloop()
def construir_menu(self):
menu_principal = tkinter.Menu(self.ventana)
menu_archivo = tkinter.Menu(menu_principal, tearoff=0)
menu_archivo.add_command(label="Estándar", command=self.funcion_vacia)
menu_archivo.add_command(label="Científica", command=self.funcion_vacia)
menu_archivo.add_separator()
menu_archivo.add_command(label="Salir", command=self.ventana.quit)
menu_principal.add_cascade(label="Ver", menu=menu_archivo)
menu_ayuda = tkinter.Menu(menu_principal, tearoff=0)
menu_ayuda.add_command(label="Acerca de", command=self.mostrar_acerca)
menu_principal.add_cascade(label="Ayuda", menu=menu_ayuda)
self.ventana.config(menu=menu_principal)
def construir_interfaz(self):
# Pantalla de visualización
pantalla = tkinter.Entry(self.ventana, textvariable=self.expresion,
font=('Arial', 24), justify='right', bd=10)
pantalla.place(x=5, y=5, width=270, height=50)
# Botones de memoria
botones_memoria = ['MC', 'MR', 'MS', 'M+', 'M-']
x_posiciones = [5, 60, 115, 170, 225]
for btn, x in zip(botones_memoria, x_posiciones):
tkinter.Button(self.ventana, text=btn, width=5, height=2,
command=self.funcion_vacia).place(x=x, y=60)
# Botones de operaciones especiales
operaciones_especiales = [
('←', self.eliminar_caracter),
('CE', self.limpiar_entrada),
('C', self.limpiar_todo),
('±', self.cambiar_signo),
('√', self.raiz_cuadrada)
]
y_pos = 105
for texto, comando in operaciones_especiales:
x_inicial = 5
tkinter.Button(self.ventana, text=texto, width=5, height=2,
command=comando).place(x=x_inicial, y=y_pos)
x_inicial += 55
# Botones numéricos y operadores
self.botones = [
('7', 8), ('8', 8), ('9', 8), ('/', 8), ('%', 8),
('4', 9), ('5', 9), ('6', 9), ('*', 9), ('1/x', 9),
('1', 10), ('2', 10), ('3', 10), ('-', 10), ('=', 10),
('0', 11), ('.', 11), ('+', 11)
]
self.crear_botones()
def crear_botones(self):
coordenadas = {
8: [(5, 150), (60, 150), (115, 150), (170, 150), (225, 150)],
9: [(5, 205), (60, 205), (115, 205), (170, 205), (225, 205)],
10: [(5, 260), (60, 260), (115, 260), (170, 260), (225, 260)],
11: [(5, 315), (115, 315), (170, 315)]
}
posiciones = [
(0, 0), (1, 0), (2, 0), (3, 0), (4, 0),
(0, 1), (1, 1), (2, 1), (3, 1), (4, 1),
(0, 2), (1, 2), (2, 2), (3, 2), (4, 2),
(0, 3), (1, 3), (2, 3)
]
idx = 0
for fila in [8, 9, 10, 11]:
for col in range(5 if fila != 11 else 3):
if idx < len(self.botones):
texto, cmd = self.botones[idx]
x, y = coordenadas[fila][col]
if texto == '=':
tkinter.Button(self.ventana, text=texto, width=5, height=2,
command=self.calcular_resultado).place(x=x, y=y, height=105)
elif texto == '0':
tkinter.Button(self.ventana, text=texto, width=10, height=2,
command=lambda t=texto: self.insertar_numero(t)).place(x=x, y=y)
elif texto in ['/', '*', '-', '+', '%']:
tkinter.Button(self.ventana, text=texto, width=5, height=2,
command=lambda t=texto: self.insertar_operador(t)).place(x=x, y=y)
else:
tkinter.Button(self.ventana, text=texto, width=5, height=2,
command=lambda t=texto: self.insertar_numero(t)).place(x=x, y=y)
idx += 1
def insertar_numero(self, num):
if self.flag_operador:
self.expresion.set("0")
self.flag_operador = False
valor_actual = self.expresion.get()
if valor_actual == '0' or valor_actual == 'Error':
self.expresion.set(num)
else:
self.expresion.set(valor_actual + num)
def insertar_operador(self, op):
valor = self.expresion.get()
self.historial_operaciones.append(valor)
self.historial_operaciones.append(op)
self.flag_operador = True
def calcular_resultado(self):
valor_actual = self.expresion.get()
self.historial_operaciones.append(valor_actual)
try:
expresion_completa = ''.join(self.historial_operaciones)
resultado = eval(expresion_completa)
self.expresion.set(str(resultado)[:15])
except:
self.expresion.set("Error")
self.historial_operaciones.clear()
self.flag_operador = True
def eliminar_caracter(self):
valor = self.expresion.get()
if len(valor) > 1:
self.expresion.set(valor[:-1])
else:
self.expresion.set("0")
def limpiar_entrada(self):
self.expresion.set("0")
def limpiar_todo(self):
self.expresion.set("0")
self.historial_operaciones.clear()
def cambiar_signo(self):
valor = self.expresion.get()
if valor != '0':
if valor.startswith('-'):
self.expresion.set(valor[1:])
else:
self.expresion.set('-' + valor)
def raiz_cuadrada(self):
try:
valor = float(self.expresion.get())
resultado = math.sqrt(valor)
self.expresion.set(str(resultado)[:15])
except:
self.expresion.set("Error")
def funcion_vacia(self):
messagebox.showinfo("Información", "Función en desarrollo")
def mostrar_acerca(self):
messagebox.showinfo("Acerca de", "Calculadora v1.0\nCreada con Python y Tkinter")
# Instanciar la calculadora
calculadora = InterfazCalculadora()
2. Editor de Texto
Un bloc de notas básico implementado con Tkinter que permite crear, abrir, guardar y editar archivos de texto. Utiliza el módulo filedialog para los diálogos de archivo y pickle para almacenar preferencias.
El código proporciona las funcionalidades básicas de cualquier editor de texto: nuevo archivo, abrir, guardar, guardar como, deshacer, rehacer, cortar, copiar, pegar, buscar y seleccionar todo.
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
class EditorTexto:
def __init__(self):
self.raiz = Tk()
self.raiz.title("Editor de Texto")
self.raiz.geometry("600x400")
self.nombre_archivo = None
self.crear_menu()
self.crear_area_texto()
self.crear_barra_estado()
self.raiz.mainloop()
def crear_menu(self):
barra_menu = Menu(self.raiz)
# Menú Archivo
menu_archivo = Menu(barra_menu, tearoff=0)
menu_archivo.add_command(label="Nuevo", accelerator="Ctrl+N", command=self.nuevo_archivo)
menu_archivo.add_command(label="Abrir", accelerator="Ctrl+O", command=self.abrir_archivo)
menu_archivo.add_command(label="Guardar", accelerator="Ctrl+S", command=self.guardar_archivo)
menu_archivo.add_command(label="Guardar como", accelerator="Ctrl+Shift+S",
command=self.guardar_como)
menu_archivo.add_separator()
menu_archivo.add_command(label="Salir", command=self.raiz.quit)
barra_menu.add_cascade(label="Archivo", menu=menu_archivo)
# Menú Editar
menu_editar = Menu(barra_menu, tearoff=0)
menu_editar.add_command(label="Deshacer", accelerator="Ctrl+Z", command=self.deshacer)
menu_editar.add_command(label="Rehacer", accelerator="Ctrl+Y", command=self.rehacer)
menu_editar.add_separator()
menu_editar.add_command(label="Cortar", accelerator="Ctrl+X", command=self.cortar)
menu_editar.add_command(label="Copiar", accelerator="Ctrl+C", command=self.copiar)
menu_editar.add_command(label="Pegar", accelerator="Ctrl+V", command=self.pegar)
menu_editar.add_separator()
menu_editar.add_command(label="Buscar", accelerator="Ctrl+B", command=self.buscar)
menu_editar.add_command(label="Seleccionar todo", accelerator="Ctrl+A",
command=self.seleccionar_todo)
barra_menu.add_cascade(label="Editar", menu=menu_editar)
# Menú Ayuda
menu_ayuda = Menu(barra_menu, tearoff=0)
menu_ayuda.add_command(label="Acerca de", command=self.mostrar_info)
barra_menu.add_cascade(label="Ayuda", menu=menu_ayuda)
self.raiz.config(menu=barra_menu)
def crear_area_texto(self):
frame_central = Frame(self.raiz)
frame_central.pack(fill=BOTH, expand=YES)
scroll_y = Scrollbar(frame_central)
self.area_texto = Text(frame_central, wrap=NONE, undo=True,
yscrollcommand=scroll_y.set, xscrollcommand=self.scroll_horizontal)
scroll_y.config(command=self.area_texto.yview)
scroll_y.pack(side=RIGHT, fill=Y)
self.area_texto.pack(fill=BOTH, expand=YES)
# Bindings de teclado
self.area_texto.bind('<Control-N>', lambda e: self.nuevo_archivo())
self.area_texto.bind('<Control-n>', lambda e: self.nuevo_archivo())
self.area_texto.bind('<Control-O>', lambda e: self.abrir_archivo())
self.area_texto.bind('<Control-o>', lambda e: self.abrir_archivo())
self.area_texto.bind('<Control-S>', lambda e: self.guardar_archivo())
self.area_texto.bind('<Control-s>', lambda e: self.guardar_archivo())
self.area_texto.bind('<Control-A>', lambda e: self.seleccionar_todo())
self.area_texto.bind('<Control-a>', lambda e: self.seleccionar_todo())
# Menú contextual
self.menu_contexto = Menu(self.area_texto, tearoff=0)
self.menu_contexto.add_command(label="Cortar", command=self.cortar)
self.menu_contexto.add_command(label="Copiar", command=self.copiar)
self.menu_contexto.add_command(label="Pegar", command=self.pegar)
self.area_texto.bind('<Button-3>', self.mostrar_menu_contexto)
def scroll_horizontal(self, *args):
pass # Implementar si es necesario
def crear_barra_estado(self):
self.barra_estado = Label(self.raiz, text="Línea 1, Columna 1",
anchor=E, bd=1, relief=SUNKEN)
self.barra_estado.pack(side=BOTTOM, fill=X)
self.area_texto.bind('<KeyPress>', self.actualizar_estado)
self.area_texto.bind('<Button-1>', self.actualizar_estado)
def actualizar_estado(self, event=None):
cursor_pos = self.area_texto.index(INSERT)
fila, columna = cursor_pos.split('.')
self.barra_estado.config(text=f"Línea {fila}, Columna {int(columna)+1}")
def nuevo_archivo(self):
self.nombre_archivo = None
self.raiz.title("Sin título")
self.area_texto.delete(1.0, END)
def abrir_archivo(self):
archivo = askopenfilename(defaultextension=".txt",
filetypes=[("Archivos de texto", "*.txt"),
("Todos los archivos", "*.*")])
if archivo:
self.nombre_archivo = archivo
self.raiz.title(f"{basename(archivo)} - Editor de Texto")
self.area_texto.delete(1.0, END)
with open(archivo, 'r', encoding='utf-8') as f:
self.area_texto.insert(1.0, f.read())
def guardar_archivo(self):
if self.nombre_archivo:
try:
with open(self.nombre_archivo, 'w', encoding='utf-8') as f:
contenido = self.area_texto.get(1.0, END)
f.write(contenido)
except:
showerror("Error", "No se pudo guardar el archivo")
else:
self.guardar_como()
def guardar_como(self):
archivo = asksaveasfilename(initialfile="sin_titulo.txt",
defaultextension=".txt",
filetypes=[("Archivos de texto", "*.txt"),
("Todos los archivos", "*.*")])
if archivo:
self.nombre_archivo = archivo
self.guardar_archivo()
self.raiz.title(f"{basename(archivo)} - Editor de Texto")
def deshacer(self):
try:
self.area_texto.edit_undo()
except:
pass
def rehacer(self):
try:
self.area_texto.edit_redo()
except:
pass
def cortar(self):
self.area_texto.event_generate("<<Cut>>")
def copiar(self):
self.area_texto.event_generate("<<Copy>>")
def pegar(self):
self.area_texto.event_generate("<<Paste>>")
def buscar(self):
ventana_busqueda = Toplevel(self.raiz)
ventana_busqueda.title("Buscar")
ventana_busqueda.geometry("300x80")
Label(ventana_busqueda, text="Buscar:").grid(row=0, column=0, padx=5, pady=5)
entrada = Entry(ventana_busqueda, width=30)
entrada.grid(row=0, column=1, padx=5, pady=5)
entrada.focus_set()
def seleccionar_todo(self):
self.area_texto.tag_add("sel", "1.0", END)
def mostrar_menu_contexto(self, event):
self.menu_contexto.tk_popup(event.x_root, event.y_root)
def mostrar_info(self):
showinfo("Acerca del Editor", "Editor de Texto v1.0\nDesarrollado con Python y Tkinter")
# Iniciar la aplicación
editor = EditorTexto()
3. Sistema de Autenticación
Este proyecto implementa un sistema de inicio de sesión y registro de usuarios. Utiliza pickle para la persistencia de datos de usuarios de forma serializada.
La aplicación permite a los usuarios iniciar sesión con credenciales existentes o registrarse como nuevos usuarios. Los datos se almacenan en un archivo pickle para su recuperación posterior.
import tkinter as tk
from tkinter import messagebox
import pickle
import os
class SistemaAutenticacion:
def __init__(self):
self.ventana_principal = tk.Tk()
self.ventana_principal.title("Sistema de Acceso")
self.ventana_principal.geometry("400x250")
self.crear_interfaz_principal()
self.ventana_principal.mainloop()
def crear_interfaz_principal(self):
# Fondo decorativo
lienzo = tk.Canvas(self.ventana_principal, height=150, width=400)
lienzo.pack()
# Etiquetas
tk.Label(self.ventana_principal, text="Usuario:").place(x=80, y=120)
tk.Label(self.ventana_principal, text="Contraseña:").place(x=80, y=160)
# Variables de texto
self.variable_usuario = tk.StringVar()
self.variable_usuario.set("")
self.variable_password = tk.StringVar()
# Campos de entrada
self.campo_usuario = tk.Entry(self.ventana_principal, textvariable=self.variable_usuario)
self.campo_usuario.place(x=150, y=120)
self.campo_password = tk.Entry(self.ventana_principal,
textvariable=self.variable_password, show="*")
self.campo_password.place(x=150, y=160)
# Botones
tk.Button(self.ventana_principal, text="Iniciar Sesión",
command=self.iniciar_sesion).place(x=100, y=200)
tk.Button(self.ventana_principal, text="Registrarse",
command=self.mostrar_ventana_registro).place(x=210, y=200)
# Bind para ENTER
self.campo_password.bind('<Return>', lambda e: self.iniciar_sesion())
def iniciar_sesion(self):
usuario = self.variable_usuario.get()
password = self.variable_password.get()
try:
with open('datos_usuarios.pkl', 'rb') as archivo:
datos_usuarios = pickle.load(archivo)
except FileNotFoundError:
with open('datos_usuarios.pkl', 'wb') as archivo:
datos_usuarios = {'admin': 'admin123'}
pickle.dump(datos_usuarios, archivo)
if usuario in datos_usuarios:
if password == datos_usuarios[usuario]:
messagebox.showinfo("Bienvenido", f"Hola {usuario}, acceso concedido")
else:
messagebox.showerror("Error", "Contraseña incorrecta")
else:
respuesta = messagebox.askyesno("Registro",
"Usuario no encontrado. ¿Desea registrarse?")
if respuesta:
self.mostrar_ventana_registro()
def mostrar_formulario_registro(self):
self.ventana_principal.withdraw()
ventana_registro = tk.Toplevel(self.ventana_principal)
ventana_registro.title("Nuevo Usuario")
ventana_registro.geometry("350x220")
tk.Label(ventana_registro, text="Nuevo Usuario:").place(x=20, y=30)
nuevo_usuario = tk.StringVar()
tk.Entry(ventana_registro, textvariable=nuevo_usuario).place(x=120, y=30)
tk.Label(ventana_registro, text="Nueva Contraseña:").place(x=20, y=70)
nueva_password = tk.StringVar()
tk.Entry(ventana_registro, textvariable=nueva_password, show="*").place(x=120, y=70)
tk.Label(ventana_registro, text="Confirmar:").place(x=20, y=110)
confirm_password = tk.StringVar()
tk.Entry(ventana_registro, textvariable=confirm_password, show="*").place(x=120, y=110)
def registrar():
if nueva_password.get() != confirm_password.get():
messagebox.showerror("Error", "Las contraseñas no coinciden")
return
try:
with open('datos_usuarios.pkl', 'rb') as archivo:
usuarios_existentes = pickle.load(archivo)
except FileNotFoundError:
usuarios_existentes = {}
if nuevo_usuario.get() in usuarios_existentes:
messagebox.showerror("Error", "El usuario ya existe")
return
usuarios_existentes[nuevo_usuario.get()] = nueva_password.get()
with open('datos_usuarios.pkl', 'wb') as archivo:
pickle.dump(usuarios_existentes, archivo)
messagebox.showinfo("Éxito", "Usuario registrado correctamente")
ventana_registro.destroy()
self.ventana_principal.deiconify()
tk.Button(ventana_registro, text="Registrarse", command=registrar).place(x=130, y=160)
def cerrar():
ventana_registro.destroy()
self.ventana_principal.deiconify()
ventana_registro.protocol("WM_DELETE_WINDOW", cerrar)
def mostrar_ventana_registro(self):
self.mostrar_formulario_registro()
# Ejecutar aplicación
if __name__ == "__main__":
SistemaAutenticacion()
Desarrollo de Juegos
1. Juego 2048
El clásico juego 2048 implementado con Pygame. El objetivo es combinar números iguales hasta obtener 2048. El juego utiliza una matriz 4x4 y responde a las teclas de dirección.
La lógica principal maneja el movimiento de loseta en las cuatro direcciones, combinando valores iguales y generando nuevos números aleatorios.
import pygame
import random
pygame.init()
TAMAÑO_CELDA = 100
CUADRO_TAMANO = 4
ANCHO_PANTALLA = TAMAÑO_CELDA * CUADRO_TAMANO
ALTO_PANTALLA = TAMAÑO_CELDA * CUADRO_TAMANO
COLORES = [
(238, 228, 218), (237, 224, 200), (242, 177, 121),
(245, 149, 99), (246, 124, 95), (246, 94, 59),
(237, 207, 114), (237, 204, 97), (237, 200, 80),
(237, 197, 63), (237, 194, 46)
]
COLOR_TEXTO = (119, 110, 101)
COLOR_TEXTO_CLARO = (249, 246, 226)
class Juego2048:
def __init__(self):
self.pantalla = pygame.display.set_mode((ANCHO_PANTALLA, ALTO_PANTALLA))
pygame.display.set_caption("2048")
self.reloj = pygame.time.Clock()
self.matriz = [[0] * CUADRO_TAMANO for _ in range(CUADRO_TAMANO)]
self.puntuacion = 0
self.agregar_numero()
self.agregar_numero()
self.ejecutar()
def agregar_numero(self):
espacios_vacios = [(i, j) for i in range(CUADRO_TAMANO)
for j in range(CUADRO_TAMANO) if self.matriz[i][j] == 0]
if espacios_vacios:
x, y = random.choice(espacios_vacios)
self.matriz[x][y] = 2 if random.random() < 0.9 else 4
self.puntuacion += self.matriz[x][y]
def comprimir(self, fila):
nueva_fila = [num for num in fila if num != 0]
nueva_fila += [0] * (len(fila) - len(nueva_fila))
return nueva_fila
def combinar(self, fila):
for i in range(len(fila) - 1):
if fila[i] == fila[i + 1] and fila[i] != 0:
fila[i] *= 2
self.puntuacion += fila[i]
fila[i + 1] = 0
return fila
def mover_izquierda(self):
movido = False
for i in range(CUADRO_TAMANO):
original = self.matriz[i][:]
self.matriz[i] = self.comprimir(self.matriz[i])
self.matriz[i] = self.combinar(self.matriz[i])
self.matriz[i] = self.comprimir(self.matriz[i])
if original != self.matriz[i]:
movido = True
return movido
def mover_derecha(self):
movido = False
for i in range(CUADRO_TAMANO):
original = self.matriz[i][:]
self.matriz[i] = self.matriz[i][::-1]
self.matriz[i] = self.comprimir(self.matriz[i])
self.matriz[i] = self.combinar(self.matriz[i])
self.matriz[i] = self.comprimir(self.matriz[i])
self.matriz[i] = self.matriz[i][::-1]
if original != self.matriz[i]:
movido = True
return movido
def mover_arriba(self):
movido = False
for j in range(CUADRO_TAMANO):
columna = [self.matriz[i][j] for i in range(CUADRO_TAMANO)]
original = columna[:]
columna = self.comprimir(columna)
columna = self.combinar(columna)
columna = self.comprimir(columna)
for i in range(CUADRO_TAMANO):
self.matriz[i][j] = columna[i]
if original != columna:
movido = True
return movido
def mover_abajo(self):
movido = False
for j in range(CUADRO_TAMANO):
columna = [self.matriz[i][j] for i in range(CUADRO_TAMANO)]
original = columna[:]
columna = columna[::-1]
columna = self.comprimir(columna)
columna = self.combinar(columna)
columna = self.comprimir(columna)
columna = columna[::-1]
for i in range(CUADRO_TAMANO):
self.matriz[i][j] = columna[i]
if original != columna:
movido = True
return movido
def game_over(self):
for i in range(CUADRO_TAMANO):
for j in range(CUADRO_TAMANO):
if self.matriz[i][j] == 0:
return False
if j < CUADRO_TAMANO - 1 and self.matriz[i][j] == self.matriz[i][j + 1]:
return False
if i < CUADRO_TAMANO - 1 and self.matriz[i][j] == self.matriz[i + 1][j]:
return False
return True
def dibujar(self):
self.pantalla.fill((187, 173, 160))
for i in range(CUADRO_TAMANO):
for j in range(CUADRO_TAMANO):
valor = self.matriz[i][j]
x = j * TAMAÑO_CELDA + 5
y = i * TAMAÑO_CELDA + 5
ancho = TAMAÑO_CELDA - 10
if valor == 0:
color = (205, 193, 180)
else:
indice = min(int(pow(valor, 0.5) - 1), len(COLORES) - 1)
color = COLORES[indice]
pygame.draw.rect(self.pantalla, color, (x, y, ancho, ancho), border_radius=8)
if valor != 0:
fuente = pygame.font.SysFont('arial', 40, bold=True)
texto = fuente.render(str(valor), True, COLOR_TEXTO_CLARO if valor > 4 else COLOR_TEXTO)
rect = texto.get_rect(center=(x + ancho // 2, y + ancho // 2))
self.pantalla.blit(texto, rect)
pygame.display.flip()
def ejecutar(self):
ejecutando = True
while ejecutando:
self.reloj.tick(30)
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
ejecutando = False
if evento.type == pygame.KEYDOWN:
movido = False
if evento.key == pygame.K_LEFT:
movido = self.mover_izquierda()
elif evento.key == pygame.K_RIGHT:
movido = self.mover_derecha()
elif evento.key == pygame.K_UP:
movido = self.mover_arriba()
elif evento.key == pygame.K_DOWN:
movido = self.mover_abajo()
if movido:
self.agregar_numero()
self.dibujar()
if self.game_over():
print(f"Game Over! Puntuación: {self.puntuacion}")
ejecutando = False
self.dibujar()
pygame.quit()
if __name__ == "__main__":
juego = Juego2048()
2. Juego de la Serpiente
El tradicional juego de la serpiente donde controlas una serpiente que debe comer alimentos para crecer. El juego termina cuando la serpiente choca con las paredes o consigo misma.
El código implementa la lógica de movimiento,colisión, generación de alimentos y puntuación.
import pygame
import random
import sys
pygame.init()
ANCHO = 640
ALTO = 480
TAMAÑO_BLOQUE = 20
NEGRO = (0, 0, 0)
BLANCO = (255, 255, 255)
VERDE = (0, 255, 0)
ROJO = (255, 0, 0)
AZUL = (0, 0, 255)
class Serpiente:
def __init__(self):
self.pantalla = pygame.display.set_mode((ANCHO, ALTO))
pygame.display.set_caption("Serpiente Clásica")
self.reloj = pygame.time.Clock()
self.posicion = [[100, 100], [80, 100], [60, 100]]
self.direccion = 'DERECHA'
self.direccion_anterior = 'DERECHA'
self.comida = None
self.puntuacion = 0
self.generar_comida()
self.juego_activo = False
self.juego_terminado = False
self.mostrar_pantalla_inicio()
def generar_comida(self):
columna_vacia = ANCHO // TAMAÑO_BLOQUE
fila_vacia = ALTO // TAMAÑO_BLOQUE
posiciones_vacias = [(x, y) for x in range(columna_vacia)
for y in range(fila_vacia)]
posiciones_cuerpo = [(pos[0] // TAMAÑO_BLOQUE, pos[1] // TAMAÑO_BLOQUE)
for pos in self.posicion]
for pos in posiciones_cuerpo:
if pos in posiciones_vacias:
posiciones_vacias.remove(pos)
if posiciones_vacias:
x, y = random.choice(posiciones_vacias)
self.comida = [x * TAMAÑO_BLOQUE, y * TAMAÑO_BLOQUE]
def mover(self):
cabeza = self.posicion[-1][:]
movimientos = {
'ARRIBA': [0, -TAMAÑO_BLOQUE],
'ABAJO': [0, TAMAÑO_BLOQUE],
'IZQUIERDA': [-TAMAÑO_BLOQUE, 0],
'DERECHA': [TAMAÑO_BLOQUE, 0]
}
movimiento = movimientos[self.direccion]
cabeza[0] += movimiento[0]
cabeza[1] += movimiento[1]
self.posicion.append(cabeza)
if self.comida and cabeza == self.comida:
self.puntuacion += 10
self.generar_comida()
else:
self.posicion.pop(0)
def verificar_colision(self):
cabeza = self.posicion[-1]
# Colisión con paredes
if cabeza[0] < 0 or cabeza[0] >= ANCHO or cabeza[1] < 0 or cabeza[1] >= ALTO:
return True
# Colisión con el cuerpo
for segmento in self.posicion[:-1]:
if cabeza == segmento:
return True
return False
def cambiar_direccion(self, nueva_direccion):
opuesto = {
'ARRIBA': 'ABAJO',
'ABAJO': 'ARRIBA',
'IZQUIERDA': 'DERECHA',
'DERECHA': 'IZQUIERDA'
}
if nueva_direccion != opuesto.get(self.direccion_anterior):
self.direccion = nueva_direccion
def dibujar(self):
self.pantalla.fill(NEGRO)
# Dibujar comida
if self.comida:
pygame.draw.rect(self.pantalla, ROJO,
(self.comida[0], self.comida[1], TAMAÑO_BLOQUE, TAMAÑO_BLOQUE))
# Dibujar serpiente
for i, segmento in enumerate(self.posicion):
color = VERDE if i == len(self.posicion) - 1 else (0, 200, 0)
pygame.draw.rect(self.pantalla, color,
(segmento[0], segmento[1], TAMAÑO_BLOQUE, TAMAÑO_BLOQUE))
pygame.draw.rect(self.pantalla, NEGRO,
(segmento[0], segmento[1], TAMAÑO_BLOQUE, TAMAÑO_BLOQUE), 1)
# Mostrar puntuación
fuente = pygame.font.SysFont('arial', 24)
texto = fuente.render(f'Puntuación: {self.puntuacion}', True, BLANCO)
self.pantalla.blit(texto, (10, 10))
pygame.display.flip()
def mostrar_pantalla_inicio(self):
fuente_titulo = pygame.font.SysFont('arial', 48)
fuente_opciones = pygame.font.SysFont('arial', 24)
while True:
self.pantalla.fill(NEGRO)
titulo = fuente_titulo.render("SNAKE", True, VERDE)
texto_iniciar = fuente_opciones.render("Presiona ESPACIO para iniciar", True, BLANCO)
texto_salir = fuente_opciones.render("Presiona ESC para salir", True, BLANCO)
self.pantalla.blit(titulo, (ANCHO//2 - titulo.get_width()//2, ALTO//3))
self.pantalla.blit(texto_iniciar, (ANCHO//2 - texto_iniciar.get_width()//2, ALTO//2))
self.pantalla.blit(texto_salir, (ANCHO//2 - texto_salir.get_width()//2, ALTO//2 + 40))
pygame.display.flip()
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
pygame.quit()
sys.exit()
if evento.type == pygame.KEYDOWN:
if evento.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
elif evento.key == pygame.K_SPACE:
self.iniciar_juego()
def iniciar_juego(self):
self.posicion = [[100, 100], [80, 100], [60, 100]]
self.direccion = 'DERECHA'
self.puntuacion = 0
self.generar_comida()
self.juego_activo = True
self.juego_terminado = False
while self.juego_activo:
self.reloj.tick(10)
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
self.juego_activo = False
pygame.quit()
sys.exit()
if evento.type == pygame.KEYDOWN:
self.direccion_anterior = self.direccion
if evento.key == pygame.K_UP or evento.key == pygame.K_w:
self.cambiar_direccion('ARRIBA')
elif evento.key == pygame.K_DOWN or evento.key == pygame.K_s:
self.cambiar_direccion('ABAJO')
elif evento.key == pygame.K_LEFT or evento.key == pygame.K_a:
self.cambiar_direccion('IZQUIERDA')
elif evento.key == pygame.K_RIGHT or evento.key == pygame.K_d:
self.cambiar_direccion('DERECHA')
elif evento.key == pygame.K_ESCAPE:
self.juego_activo = False
if not self.juego_terminado:
self.mover()
if self.verificar_colision():
self.mostrar_game_over()
self.juego_terminado = True
pygame.time.wait(2000)
self.mostrar_pantalla_inicio()
return
self.dibujar()
def mostrar_game_over(self):
fuente = pygame.font.SysFont('arial', 48)
texto = fuente.render("GAME OVER", True, ROJO)
fuente_puntos = pygame.font.SysFont('arial', 24)
puntos = fuente_puntos.render(f"Puntuación final: {self.puntuacion}", True, BLANCO)
self.pantalla.blit(texto, (ANCHO//2 - texto.get_width()//2, ALTO//2 - 30))
self.pantalla.blit(puntos, (ANCHO//2 - puntos.get_width()//2, ALTO//2 + 20))
pygame.display.flip()
if __name__ == "__main__":
juego = Serpiente()
3. Tetris
El clásico juego de Tetris implementado con Pygame. Las piezas caen desde la parte superior y el jugador debe organizarlas para completar líneas horizontales.
Las piezas tienen diferentes formas (I, J, L, O, S, T, Z) y pueden rotar. El juego detecta cuando las piezas tocan el fondo o堆积 otras piezas.
import pygame
import random
pygame.init()
ANCHO_CELDA = 30
COLUMNAS = 10
FILAS = 20
ANCHO_VENTANA = ANCHO_CELDA * COLUMNAS + 150
ALTO_VENTANA = ANCHO_CELDA * FILAS
COLOR_FONDO = (20, 20, 30)
COLOR_REJILLA = (40, 40, 50)
BLANCO = (255, 255, 255)
NEGRO = (0, 0, 0)
PALETA_COLORES = [
(0, 255, 255), (0, 0, 255), (255, 165, 0),
(255, 255, 0), (0, 255, 0), (128, 0, 128), (255, 0, 0)
]
class Pieza:
FORMAS = {
'I': [[1, 1, 1, 1]],
'J': [[1, 0, 0], [1, 1, 1]],
'L': [[0, 0, 1], [1, 1, 1]],
'O': [[1, 1], [1, 1]],
'S': [[0, 1, 1], [1, 1, 0]],
'T': [[0, 1, 0], [1, 1, 1]],
'Z': [[1, 1, 0], [0, 1, 1]]
}
def __init__(self):
self.tipo = random.choice(list(self.FORMAS.keys()))
self.forma = [fila[:] for fila in self.FORMAS[self.tipo]]
self.color = random.choice(PALETA_COLORES)
self.x = COLUMNAS // 2 - len(self.forma[0]) // 2
self.y = 0
def rotar(self):
filas = len(self.forma)
columnas = len(self.forma[0])
rotada = [[self.forma[filas - 1 - j][i] for j in range(filas)]
for i in range(columnas)]
return rotada
class Tetris:
def __init__(self):
self.pantalla = pygame.display.set_mode((ANCHO_VENTANA, ALTO_VENTANA))
pygame.display.set_caption("Tetris")
self.reloj = pygame.time.Clock()
self.tablero = [[None for _ in range(COLUMNAS)] for _ in range(FILAS)]
self.pieza_actual = Pieza()
self.puntuacion = 0
self.nivel = 1
self.lineas = 0
self.game_over = False
self.tiempo_caida = 500
self.ultimo_movimiento = pygame.time.get_ticks()
self.ejecutar()
def colision(self, pieza, dx=0, dy=0, forma=None):
if forma is None:
forma = pieza.forma
for y, fila in enumerate(forma):
for x, valor in enumerate(fila):
if valor:
nuevo_x = pieza.x + x + dx
nuevo_y = pieza.y + y + dy
if nuevo_x < 0 or nuevo_x >= COLUMNAS:
return True
if nuevo_y >= FILAS:
return True
if nuevo_y >= 0 and self.tablero[nuevo_y][nuevo_x]:
return True
return False
def asegurar_pieza(self):
for y, fila in enumerate(self.pieza_actual.forma):
for x, valor in enumerate(fila):
if valor:
pos_y = self.pieza_actual.y + y
pos_x = self.pieza_actual.x + x
if pos_y < 0:
self.game_over = True
return
self.tablero[pos_y][pos_x] = self.pieza_actual.color
self.eliminar_lineas()
self.pieza_actual = Pieza()
if self.colision(self.pieza_actual):
self.game_over = True
def eliminar_lineas(self):
lineas_eliminadas = 0
for y in range(FILAS - 1, -1, -1):
if all(self.tablero[y]):
del self.tablero[y]
self.tablero.insert(0, [None] * COLUMNAS)
lineas_eliminadas += 1
if lineas_eliminadas > 0:
self.lineas += lineas_eliminadas
self.puntuacion += lineas_eliminadas * 100 * lineas_eliminadas
self.nivel = self.lineas // 10 + 1
self.tiempo_caida = max(100, 500 - (self.nivel - 1) * 50)
def mover_pieza(self, dx, dy):
if not self.colision(self.pieza_actual, dx, dy):
self.pieza_actual.x += dx
self.pieza_actual.y += dy
return True
return False
def rotar_pieza(self):
forma_nueva = self.pieza_actual.rotar()
if not self.colision(self.pieza_actual, forma=forma_nueva):
self.pieza_actual.forma = forma_nueva
elif not self.colision(self.pieza_actual, -1, forma=forma_nueva):
self.pieza_actual.x -= 1
self.pieza_actual.forma = forma_nueva
elif not self.colision(self.pieza_actual, 1, forma=forma_nueva):
self.pieza_actual.x += 1
self.pieza_actual.forma = forma_nueva
def caer_rapido(self):
if self.mover_pieza(0, 1):
self.ultimo_movimiento = pygame.time.get_ticks()
def caer_automatico(self):
ahora = pygame.time.get_ticks()
if ahora - self.ultimo_movimiento > self.tiempo_caida:
if not self.mover_pieza(0, 1):
self.asegurar_pieza()
self.ultimo_movimiento = ahora
def dibujar(self):
self.pantalla.fill(COLOR_FONDO)
# Dibujar rejilla
for x in range(COLUMNAS + 1):
pygame.draw.line(self.pantalla, COLOR_REJILLA,
(x * ANCHO_CELDA, 0), (x * ANCHO_CELDA, ALTO_VENTANA))
for y in range(FILAS + 1):
pygame.draw.line(self.pantalla, COLOR_REJILLA,
(0, y * ANCHO_CELDA), (ANCHO_VENTANA - 150, y * ANCHO_CELDA))
# Dibujar piezas en el tablero
for y in range(FILAS):
for x in range(COLUMNAS):
if self.tablero[y][x]:
pygame.draw.rect(self.pantalla, self.tablero[y][x],
(x * ANCHO_CELDA + 1, y * ANCHO_CELDA + 1,
ANCHO_CELDA - 2, ANCHO_CELDA - 2))
# Dibujar pieza actual
if not self.game_over:
for y, fila in enumerate(self.pieza_actual.forma):
for x, valor in enumerate(fila):
if valor:
pos_x = (self.pieza_actual.x + x) * ANCHO_CELDA + 1
pos_y = (self.pieza_actual.y + y) * ANCHO_CELDA + 1
pygame.draw.rect(self.pantalla, self.pieza_actual.color,
(pos_x, pos_y, ANCHO_CELDA - 2, ANCHO_CELDA - 2))
# Panel lateral
pygame.draw.rect(self.pantalla, (30, 30, 40),
(ANCHO_VENTANA - 150, 0, 150, ALTO_VENTANA))
fuente = pygame.font.SysFont('arial', 20)
texto_puntos = fuente.render(f'Puntos: {self.puntuacion}', True, BLANCO)
texto_nivel = fuente.render(f'Nivel: {self.nivel}', True, BLANCO)
texto_lineas = fuente.render(f'Líneas: {self.lineas}', True, BLANCO)
self.pantalla.blit(texto_puntos, (ANCHO_VENTANA - 140, 50))
self.pantalla.blit(texto_nivel, (ANCHO_VENTANA - 140, 100))
self.pantalla.blit(texto_lineas, (ANCHO_VENTANA - 140, 150))
if self.game_over:
fuente_grande = pygame.font.SysFont('arial', 32)
juego_terminado = fuente_grande.render("GAME OVER", True, (255, 0, 0))
self.pantalla.blit(juego_terminado,
(ANCHO_VENTANA//2 - 80, ALTO_VENTANA//2 - 20))
pygame.display.flip()
def ejecutar(self):
while True:
self.reloj.tick(30)
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
pygame.quit()
return
if evento.type == pygame.KEYDOWN and not self.game_over:
if evento.key == pygame.K_LEFT:
self.mover_pieza(-1, 0)
elif evento.key == pygame.K_RIGHT:
self.mover_pieza(1, 0)
elif evento.key == pygame.K_DOWN:
self.caer_rapido()
elif evento.key == pygame.K_UP:
self.rotar_pieza()
elif evento.key == pygame.K_SPACE:
while self.mover_pieza(0, 1):
pass
self.asegurar_pieza()
if not self.game_over:
self.caer_automatico()
self.dibujar()
if __name__ == "__main__":
juego = Tetris()
4. Juego de Conexión (Link Link)
Un juego de correspondencia donde debes conectar pares de iconos idénticos. La conexión no puede tener más de dos esquinas y no puede pasar a través de otras piezas.
La lógica verifica tres tipos de conexiones: línea recta, una esquina, y dos esquinas. El algoritmo busca el camino más corto entre dos piezas seleccionadas.
import tkinter as tk
from tkinter import messagebox
import random
class JuegoConexion:
def __init__(self):
self.ventana = tk.Tk()
self.ventana.title("Juego de Conexión")
self.ventana.geometry("500x550")
self.TAMANO_TABLERO = 8
self.TAMANO_CELDA = 50
self.SELECT_DELAY = 0.3
self.inicializar_juego()
self.ventana.mainloop()
def inicializar_juego(self):
# Generar pares de elementos
total_celdas = self.TAMANO_TABLERO * self.TAMANO_TABLERO
num_pares = total_celdas // 2
self.elementos = []
for i in range(num_pares):
self.elementos.extend([i, i])
random.shuffle(self.elementos)
# Crear matriz del juego
self.tablero = [[None for _ in range(self.TAMANO_TABLERO)]
for _ in range(self.TAMANO_TABLERO)]
idx = 0
for i in range(self.TAMANO_TABLERO):
for j in range(self.TAMANO_TABLERO):
self.tablero[i][j] = self.elementos[idx]
idx += 1
# Estado del juego
self.seleccion_primera = None
self.id_seleccion_1 = None
self.id_seleccion_2 = None
self.id_linea = None
# Crear interfaz
self.crear_interfaz()
def crear_interfaz(self):
# Lienzo para dibujar
self.lienzo = tk.Canvas(self.ventana, width=450, height=450, bg='#2E8B57')
self.lienzo.pack(pady=10)
# Dibujar tablero
self.dibujar_tablero()
# Botones de control
marco_botones = tk.Frame(self.ventana)
marco_botones.pack(pady=10)
tk.Button(marco_botones, text="Reiniciar", command=self.reiniciar,
width=15).pack(side=tk.LEFT, padx=5)
tk.Button(marco_botones, text="Mezclar", command=self.mezclar,
width=15).pack(side=tk.LEFT, padx=5)
# Etiqueta de estado
self.etiqueta_estado = tk.Label(self.ventana, text="", font=('Arial', 12))
self.etiqueta_estado.pack()
def dibujar_tablero(self):
self.lienzo.delete("all")
self.botones_tablero = [[None for _ in range(self.TAMANO_TABLERO)]
for _ in range(self.TAMANO_TABLERO)]
for i in range(self.TAMANO_TABLERO):
for j in range(self.TAMANO_TABLERO):
x1 = j * self.TAMANO_CELDA + 25
y1 = i * self.TAMANO_CELDA + 25
x2 = x1 + self.TAMANO_CELDA - 2
y2 = y1 + self.TAMANO_CELDA - 2
if self.tablero[i][j] is not None:
color = self.obtener_color(self.tablero[i][j])
rect = self.lienzo.create_rectangle(x1, y1, x2, y2,
fill=color, outline='white', width=2)
self.botones_tablero[i][j] = rect
# Bind del clic
self.lienzo.tag_bind(rect, '<Button-1>',
lambda e, x=i, y=j: self.al_hacer_clic(x, y))
def obtener_color(self, indice):
colores = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
'#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F']
return colores[indice % len(colores)]
def al_hacer_clic(self, fila, columna):
if self.tablero[fila][columna] is None:
return
if self.seleccion_primera is None:
# Primera selección
self.seleccion_primera = (fila, columna)
self.id_seleccion_1 = self.botones_tablero[fila][columna]
self.lienzo.itemconfig(self.id_seleccion_1, outline='yellow', width=4)
self.etiqueta_estado.config(text="Selecciona la segunda ficha")
else:
# Segunda selección
fila1, col1 = self.seleccion_primera
fila2, columna2 = columna
if (fila1, col1) == (fila, columna):
return
self.id_seleccion_2 = self.botones_tablero[fila][columna]
self.lienzo.itemconfig(self.id_seleccion_2, outline='yellow', width=4)
# Verificar si son del mismo tipo
if self.tablero[fila1][col1] == self.tablero[fila][columna]:
# Verificar si se pueden conectar
if self.verificar_conexion(fila1, col1, fila, columna):
# Eliminar fichas después de delay
self.etiqueta_estado.config(text="¡Conexión válida!")
self.ventana.after(300, lambda: self.eliminar_fichas(
fila1, col1, fila, columna))
else:
self.etiqueta_estado.config(text="No se pueden conectar")
self.ventana.after(300, self.limpiar_seleccion)
else:
self.etiqueta_estado.config(text="Tipos diferentes")
self.ventana.after(300, self.limpiar_seleccion)
def verificar_conexion(self, x1, y1, x2, y2):
# Verificar conexión directa
if self.conexion_directa(x1, y1, x2, y2):
self.dibujar_linea(x1, y1, x2, y2, [])
return True
# Verificar conexión con una esquina
if self.conexion_una_esquina(x1, y1, x2, y2):
return True
# Verificar conexión con dos esquinas
if self.conexion_dos_esquinas(x1, y1, x2, y2):
return True
return False
def conexion_directa(self, x1, y1, x2, y2):
# Misma fila
if x1 == x2:
for y in range(min(y1, y2) + 1, max(y1, y2)):
if self.tablero[x1][y] is not None:
return False
return True
# Misma columna
if y1 == y2:
for x in range(min(x1, x2) + 1, max(x1, x2)):
if self.tablero[x][y1] is not None:
return False
return True
return False
def conexion_una_esquina(self, x1, y1, x2, y2):
# Esquina 1: (x1, y2)
if self.tablero[x1][y2] is None:
if self.conexion_directa(x1, y1, x1, y2) and self.conexion_directa(x1, y2, x2, y2):
self.dibujar_linea(x1, y1, x1, y2, [(x2, y2)])
return True
# Esquina 2: (x2, y1)
if self.tablero[x2][y1] is None:
if self.conexion_directa(x1, y1, x2, y1) and self.conexion_directa(x2, y1, x2, y2):
self.dibujar_linea(x1, y1, x2, y1, [(x2, y2)])
return True
return False
def conexion_dos_esquinas(self, x1, y1, x2, y2):
# Buscar fila vacía para conexión horizontal
for y in range(self.TAMANO_TABLERO):
if y == y1 or y == y2:
continue
if (self.tablero[x1][y] is None and self.tablero[x2][y] is None and
self.conexion_directa(x1, y1, x1, y) and
self.conexion_directa(x1, y, x2, y) and
self.conexion_directa(x2, y, x2, y2)):
self.dibujar_linea(x1, y1, x1, y, [(x1, y), (x2, y)])
return True
# Buscar columna vacía para conexión vertical
for x in range(self.TAMANO_TABLERO):
if x == x1 or x == x2:
continue
if (self.tablero[x][y1] is None and self.tablero[x][y2] is None and
self.conexion_directa(x1, y1, x, y1) and
self.conexion_directa(x, y1, x, y2) and
self.conexion_directa(x, y2, x2, y2)):
self.dibujar_linea(x1, y1, x, y1, [(x, y1), (x, y2)])
return True
return False
def dibujar_linea(self, x1, y1, x2, y2, puntos_intermedios):
coords = []
# Convertir a coordenadas de píxeles
coords.append(y1 * self.TAMANO_CELDA + 25 + self.TAMANO_CELDA // 2)
coords.append(x1 * self.TAMANO_CELDA + 25 + self.TAMANO_CELDA // 2)
for px, py in puntos_intermedios:
coords.append(py * self.TAMANO_CELDA + 25 + self.TAMANO_CELDA // 2)
coords.append(px * self.TAMANO_CELDA + 25 + self.TAMANO_CELDA // 2)
coords.append(y2 * self.TAMANO_CELDA + 25 + self.TAMANO_CELDA // 2)
coords.append(x2 * self.TAMANO_CELDA + 25 + self.TAMANO_CELDA // 2)
self.id_linea = self.lienzo.create_line(coords, fill='red', width=3)
def eliminar_fichas(self, x1, y1, x2, y2):
# Eliminar línea
if self.id_linea:
self.lienzo.delete(self.id_linea)
self.id_linea = None
# Eliminar fichas
self.tablero[x1][y1] = None
self.tablero[x2][y2] = None
# Redibujar sin las fichas eliminadas
self.limpiar_seleccion()
self.dibujar_tablero()
# Verificar si ganó
if self.verificar_victoria():
messagebox.showinfo("¡Felicidades!", "¡Has completado el juego!")
def limpiar_seleccion(self):
if self.id_seleccion_1:
self.lienzo.itemconfig(self.id_seleccion_1, outline='white', width=2)
if self.id_seleccion_2:
self.lienzo.itemconfig(self.id_seleccion_2, outline='white', width=2)
self.seleccion_primera = None
self.id_seleccion_1 = None
self.id_seleccion_2 = None
self.etiqueta_estado.config(text="")
def verificar_victoria(self):
for fila in self.tablero:
for celda in fila:
if celda is not None:
return False
return True
def reiniciar(self):
self.inicializar_juego()
def mezirar(self):
elementos_no_nulos = [elem for fila in self.tablero for elem in fila if elem is not None]
random.shuffle(elementos_no_nulos)
idx = 0
for i in range(self.TAMANO_TABLERO):
for j in range(self.TAMANO_TABLERO):
if self.tablero[i][j] is not None:
self.tablero[i][j] = elementos_no_nulos[idx]
idx += 1
self.dibujar_tablero()
self.limpiar_seleccion()
if __name__ == "__main__":
juego = JuegoConexion()
Estos proyectos cubren diferentes aspectos del desarrollo en Python: desde aplicaciones de interfaz gráfica básicas hasta juegos interactivos. Cada proyecto puede expandirse y mejorarse según las necesidades específicas del desarrollador.