El juego del ahorcado es un proyecto excelente para practicar strings, listas, sets, bucles, condiciones, funciones y validación. El programa elige una palabra secreta y el jugador intenta descubrirla letra por letra antes de quedarse sin vidas. La idea es sencilla, pero permite añadir niveles, categorías, dibujos ASCII, archivos externos, puntuaciones y pruebas.
Antes de comenzar, revisa las guías sobre strings, sets, random, bucles y funciones. Como fuentes externas, consulta la documentación oficial de random y el tutorial oficial de control de flujo.
Definir las palabras
PALABRAS = [
"python",
"variable",
"funcion",
"diccionario",
"programacion",
]
Para una primera versión, una lista es suficiente. Más adelante puedes cargar las palabras desde un archivo o agruparlas por categorías.
Elegir una palabra aleatoria
import random
palabra_secreta = random.choice(PALABRAS)
choice() devuelve un elemento de la secuencia. No utilices este módulo para contraseñas o tokens, pero es apropiado para un juego.
Representar el progreso
def mostrar_progreso(palabra, letras_acertadas):
return " ".join(
letra if letra in letras_acertadas else "_"
for letra in palabra
)
Si la palabra es python y el jugador acertó p y o, el resultado será p _ _ _ o _.
Validar una letra
def leer_letra(usadas):
while True:
entrada = input("Escribe una letra: ").strip().lower()
if len(entrada) != 1:
print("Debes escribir una sola letra.")
continue
if not entrada.isalpha():
print("Solo se admiten letras.")
continue
if entrada in usadas:
print("Ya utilizaste esa letra.")
continue
return entrada
La validación evita entradas vacías, palabras completas, números y repeticiones.
Comprobar la victoria
def palabra_completa(palabra, acertadas):
return all(letra in acertadas for letra in palabra)
all() devuelve verdadero cuando todas las condiciones son verdaderas. Las letras repetidas no requieren intentos adicionales porque se comprueba pertenencia.
Dibujos ASCII
ESTADOS = [
"""
+---+
|
|
|
===
""",
"""
+---+
O |
|
|
===
""",
"""
+---+
O |
| |
|
===
""",
"""
+---+
O |
/| |
|
===
""",
"""
+---+
O |
/|\ |
|
===
""",
"""
+---+
O |
/|\ |
/ |
===
""",
"""
+---+
O |
/|\ |
/ \ |
===
""",
]
El índice representa la cantidad de errores. El último estado corresponde a la derrota.
Crear una ronda completa
def jugar_ronda(palabra):
acertadas = set()
falladas = set()
max_errores = len(ESTADOS) - 1
while len(falladas) < max_errores:
print(ESTADOS[len(falladas)])
print(mostrar_progreso(palabra, acertadas))
print("Fallos:", " ".join(sorted(falladas)) or "ninguno")
letra = leer_letra(acertadas | falladas)
if letra in palabra:
acertadas.add(letra)
print("¡Correcto!")
else:
falladas.add(letra)
print("La letra no aparece.")
if palabra_completa(palabra, acertadas):
print(mostrar_progreso(palabra, acertadas))
print("¡Ganaste!")
return True
print(ESTADOS[-1])
print(f"Perdiste. La palabra era: {palabra}")
return False
Los sets son ideales porque evitan duplicados y permiten búsquedas rápidas.
Repetir partidas
def quiere_repetir():
while True:
respuesta = input("¿Jugar otra vez? (s/n): ").strip().lower()
if respuesta in {"s", "si", "sí"}:
return True
if respuesta == "n":
return False
print("Respuesta inválida.")
def main():
victorias = 0
partidas = 0
while True:
palabra = random.choice(PALABRAS)
partidas += 1
if jugar_ronda(palabra):
victorias += 1
print(f"Victorias: {victorias}/{partidas}")
if not quiere_repetir():
break
if __name__ == "__main__":
main()
Trabajar con acentos
Si incluyes palabras con acentos, debes decidir si a debe coincidir con á. Una opción es normalizar para comparar, pero conservar la palabra original para mostrarla. El módulo unicodedata ayuda a eliminar marcas diacríticas.
import unicodedata
def sin_acentos(texto):
normalizado = unicodedata.normalize("NFD", texto)
return "".join(
caracter
for caracter in normalizado
if unicodedata.category(caracter) != "Mn"
)
Documenta las reglas para que el jugador entienda qué entradas se aceptan.
Cargar palabras desde un archivo
from pathlib import Path
def cargar_palabras(ruta):
lineas = Path(ruta).read_text(encoding="utf-8").splitlines()
palabras = [linea.strip().lower() for linea in lineas if linea.strip()]
if not palabras:
raise ValueError("No hay palabras disponibles")
return palabras
No confíes ciegamente en el archivo. Filtra líneas vacías y valida que contengan caracteres permitidos.
Categorías
CATEGORIAS = {
"animales": ["gato", "caballo", "tigre"],
"tecnologia": ["python", "servidor", "teclado"],
"frutas": ["manzana", "naranja", "platano"],
}
Permite al usuario elegir una categoría y después utiliza random.choice sobre la lista correspondiente.
Pistas
Una pista puede revelar la categoría, la longitud o una letra. Si revelas una letra, añádela a acertadas. Para mantener el equilibrio, resta puntos o consume una vida.
Calcular puntuación
def calcular_puntos(palabra, errores, pistas):
base = len(set(palabra)) * 10
penalizacion = errores * 3 + pistas * 5
return max(0, base - penalizacion)
Una fórmula sencilla es mejor que una regla imposible de comprender.
Separar lógica e interfaz
La función que compara una letra no debería depender de input(). Puedes crear un estado:
def procesar_letra(palabra, letra, acertadas, falladas):
if letra in palabra:
acertadas.add(letra)
return "acierto"
falladas.add(letra)
return "fallo"
Esto facilita pruebas y permite reutilizar el juego en Tkinter o una página web.
Pruebas
def test_mostrar_progreso():
assert mostrar_progreso("casa", {"a"}) == "_ a _ a"
def test_palabra_completa():
assert palabra_completa("casa", {"c", "a", "s"}) is True
def test_procesar_fallo():
acertadas = set()
falladas = set()
resultado = procesar_letra("python", "z", acertadas, falladas)
assert resultado == "fallo"
assert "z" in falladas
No es necesario esperar entrada real en los tests. Prueba la lógica con datos controlados.
Evitar palabras repetidas
Para una sesión, guarda las palabras utilizadas en un set y elige entre las restantes. Cuando no queden, reinicia el conjunto o termina el juego.
Dificultad
El nivel fácil puede usar palabras cortas y más vidas. El difícil puede usar palabras largas, menos vidas y ninguna pista. No cambies las reglas a mitad de una ronda.
Guardar récords
Guarda nombre, puntos y fecha en JSON. Valida el archivo al leerlo y escribe mediante una ruta temporal. No necesitas guardar datos personales adicionales.
Errores frecuentes
Los problemas más comunes son permitir letras repetidas, descontar vidas por entradas inválidas, no tratar mayúsculas, mostrar la palabra secreta en mensajes de depuración y mezclar toda la lógica dentro de un único bucle. También debes cuidar las barras invertidas del dibujo ASCII.
Mejoras posibles
Añade interfaz gráfica, temas, temporizador, sonidos, varios idiomas, palabras obtenidas de una base local, modo para dos jugadores y estadísticas. Mantén una lista de palabras apropiada para el público y evita depender de una API para la funcionalidad básica.
Conclusión
El ahorcado reúne muchos fundamentos de Python en un proyecto entretenido. Usa sets para letras, funciones para separar responsabilidades y validación para proteger el flujo. Comienza con una lista pequeña y una ronda simple; después añade dibujos, categorías, pistas, puntuación, archivos y pruebas sin sacrificar la claridad.






