El juego de adivinar números es un proyecto excelente para principiantes porque combina números aleatorios, bucles, condiciones, entrada de datos, funciones y validación. El programa elige un número secreto y el jugador intenta descubrirlo con pistas de “más alto” o “más bajo”. A partir de esa idea sencilla puedes añadir intentos limitados, niveles de dificultad, puntuación, historial y pruebas automatizadas.
Antes de empezar, revisa el módulo random, los bucles en Python, la función input(), las funciones y el manejo de excepciones.
La documentación oficial del módulo random describe randint, choice, semillas y otras funciones. El tutorial oficial de control de flujo explica condiciones, bucles y funciones.
Generar el número secreto
import random
numero_secreto = random.randint(1, 100)
randint(1, 100) incluye ambos extremos. El jugador puede recibir cualquier entero desde 1 hasta 100.
Leer una respuesta válida
def leer_entero(mensaje, minimo, maximo):
while True:
texto = input(mensaje).strip()
try:
valor = int(texto)
except ValueError:
print("Debes escribir un número entero.")
continue
if valor < minimo or valor > maximo:
print(f"El número debe estar entre {minimo} y {maximo}.")
continue
return valor
La función repite la pregunta hasta obtener un entero dentro del rango. Separar la validación evita duplicar código en cada nivel.
Crear una ronda básica
def jugar_ronda(minimo=1, maximo=100):
secreto = random.randint(minimo, maximo)
intentos = 0
while True:
propuesta = leer_entero(
f"Adivina un número entre {minimo} y {maximo}: ",
minimo,
maximo,
)
intentos += 1
if propuesta < secreto:
print("El número secreto es mayor.")
elif propuesta > secreto:
print("El número secreto es menor.")
else:
print(f"¡Correcto! Necesitaste {intentos} intentos.")
return intentos
El bucle termina únicamente cuando la propuesta coincide con el secreto. La función devuelve la cantidad de intentos para poder calcular puntuaciones.
Añadir intentos limitados
def jugar_con_limite(minimo, maximo, max_intentos):
secreto = random.randint(minimo, maximo)
for intento in range(1, max_intentos + 1):
restantes = max_intentos - intento + 1
print(f"Intentos disponibles: {restantes}")
propuesta = leer_entero(
f"Número entre {minimo} y {maximo}: ",
minimo,
maximo,
)
if propuesta == secreto:
print(f"¡Ganaste en {intento} intentos!")
return True, intento
if propuesta < secreto:
print("Más alto.")
else:
print("Más bajo.")
print(f"Perdiste. El número era {secreto}.")
return False, max_intentos
Un for es apropiado cuando conocemos de antemano el máximo de repeticiones.
Definir niveles de dificultad
NIVELES = {
"1": {"nombre": "Fácil", "min": 1, "max": 20, "intentos": 7},
"2": {"nombre": "Normal", "min": 1, "max": 100, "intentos": 8},
"3": {"nombre": "Difícil", "min": 1, "max": 500, "intentos": 9},
}
def elegir_nivel():
print("1. Fácil")
print("2. Normal")
print("3. Difícil")
while True:
opcion = input("Nivel: ").strip()
if opcion in NIVELES:
return NIVELES[opcion]
print("Opción inválida.")
Guardar la configuración en un diccionario hace sencillo añadir o ajustar niveles.
Calcular una puntuación
def calcular_puntos(gano, intentos, max_intentos, rango):
if not gano:
return 0
eficiencia = max_intentos - intentos + 1
return eficiencia * 10 + rango // 10
La puntuación debe recompensar la dificultad y la eficiencia. Evita fórmulas demasiado complejas si el jugador no puede entender cómo mejorar.
Evitar propuestas repetidas
def pedir_propuesta_no_repetida(minimo, maximo, usadas):
while True:
valor = leer_entero("Tu propuesta: ", minimo, maximo)
if valor in usadas:
print("Ya probaste ese número.")
continue
usadas.add(valor)
return valor
Un set permite comprobar rápidamente si un valor ya fue utilizado.
Dar pistas adicionales
def pista_distancia(propuesta, secreto):
diferencia = abs(propuesta - secreto)
if diferencia <= 3:
return "Muy caliente"
if diferencia <= 10:
return "Caliente"
if diferencia <= 25:
return "Tibio"
return "Frío"
Las pistas hacen el juego más amigable, pero reducen la dificultad. Puedes activarlas solo en el nivel fácil.
Permitir varias 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("Escribe s o n.")
def main():
total_puntos = 0
while True:
nivel = elegir_nivel()
rango = nivel["max"] - nivel["min"] + 1
gano, intentos = jugar_con_limite(
nivel["min"],
nivel["max"],
nivel["intentos"],
)
puntos = calcular_puntos(
gano,
intentos,
nivel["intentos"],
rango,
)
total_puntos += puntos
print(f"Puntos de la ronda: {puntos}")
print(f"Puntuación total: {total_puntos}")
if not quiere_repetir():
break
if __name__ == "__main__":
main()
Usar una semilla durante las pruebas
Los números aleatorios dificultan las pruebas si el resultado cambia cada vez. Puedes inyectar un generador:
def crear_secreto(minimo, maximo, generador=random):
return generador.randint(minimo, maximo)
En un test, utiliza un objeto falso que siempre devuelva un número conocido. También puedes usar random.seed() para experimentos reproducibles, pero no la uses para generar contraseñas o tokens.
Pruebas de la lógica
def test_pista_distancia():
assert pista_distancia(48, 50) == "Muy caliente"
assert pista_distancia(20, 50) == "Frío"
def test_puntuacion_perdida():
assert calcular_puntos(False, 8, 8, 100) == 0
Para probar la ronda completa sin input(), separa la comparación en una función:
def comparar(propuesta, secreto):
if propuesta < secreto:
return "mayor"
if propuesta > secreto:
return "menor"
return "correcto"
Guardar el récord
import json
from pathlib import Path
RUTA_RECORD = Path("record_adivinacion.json")
def guardar_record(puntos):
datos = {"mejor_puntuacion": puntos}
RUTA_RECORD.write_text(
json.dumps(datos, indent=2),
encoding="utf-8",
)
Valida el archivo al leerlo y utiliza una escritura temporal si quieres reducir el riesgo de corrupción.
Mejoras posibles
Puedes añadir temporizador, tabla de récords, modo para dos jugadores, pistas pares o impares, logros, interfaz con Tkinter y estadísticas de promedio de intentos. Mantén la lógica independiente de la interfaz para reutilizarla.
Errores frecuentes
Los fallos habituales son olvidar que randint incluye el límite superior, permitir números fuera del rango, revelar el secreto en un mensaje de depuración, no controlar entradas vacías y mezclar puntuación, interfaz y generación aleatoria en una sola función.
Conclusión
El juego de adivinar números enseña a trabajar con aleatoriedad, validación, bucles y funciones pequeñas. Comienza con una ronda ilimitada y después añade intentos, niveles, puntuación y récords. Al separar la lógica del terminal, el proyecto se vuelve fácil de probar y puede evolucionar hacia una aplicación gráfica o web.






