- Descripción del Proyecto
- Características
- Requisitos del Sistema
- Instalación y Ejecución
- Estructura del Proyecto
- Explicación Detallada del Código
- Cómo Funciona el Código
- Conceptos de Programación Utilizados
- Sistema de Puntuación
- Reglas del Sudoku
- Solución de Problemas
- Créditos
Este proyecto es un juego completo de Sudoku implementado en Python con interfaz gráfica usando Tkinter. El juego permite a los usuarios resolver tableros de Sudoku de diferentes niveles de dificultad mientras entrena sus habilidades cognitivas como la memoria de trabajo, razonamiento lógico y toma de decisiones.
Sudoku es un juego de lógica que consiste en completar una cuadrícula de 9×9 celdas dividida en subcuadrículas de 3×3, con números del 1 al 9. El objetivo es llenar la cuadrícula de manera que:
- Cada fila contenga los números del 1 al 9 sin repetir
- Cada columna contenga los números del 1 al 9 sin repetir
- Cada subcuadrícula de 3×3 contenga los números del 1 al 9 sin repetir
- Interfaz gráfica moderna: Ventana bonita y colorida con tkinter (no es consola)
- 3 niveles de dificultad: Fácil, Medio y Difícil
- Completamente interactivo: Click para seleccionar celdas y números
- Generación automática de tableros: Cada partida es única
- Validación en tiempo real: Verifica que las reglas se cumplan
- Modo ayuda: Valida si tu movimiento es correcto
- Sistema de pistas: Revela el número correcto en una celda
- Cronómetro: Mide tu tiempo de resolución
- Sistema de puntuación: Basado en tiempo, errores y pistas usadas
- Retroalimentación visual: Celdas correctas (verde) e incorrectas (rojo)
- Estadísticas persistentes: Se guardan automáticamente en JSON
- Historial de partidas: Guarda las últimas 50 partidas
- Arquitectura modular con separación de responsabilidades
- Algoritmo de backtracking para generar tableros válidos
- Persistencia de datos con JSON
- Interfaz responsive con eventos de click y hover
- Manejo de errores robusto
- Código documentado y comentado
- Python 3.7 o superior: Descargar Python
- Tkinter: Viene incluido con Python (no requiere instalación adicional)
- Windows 7/8/10/11
- macOS 10.12+
- Linux (Ubuntu, Debian, Fedora, etc.)
python --version
# O en algunos sistemas:
python3 --versionDescarga todos los archivos del proyecto en una carpeta:
Proyect_Sudoku/
├── main.py
├── config.py
├── sudoku_generator.py
├── sudoku_game.py
├── sudoku_gui.py
├── statistics.py
└── README.md
Abre una terminal o línea de comandos y navega hasta la carpeta del proyecto:
cd "ruta/a/tu/carpeta/Proyect_Sudoku"python main.pyO en algunos sistemas:
python3 main.pyEl juego se abrirá en una ventana nueva. Selecciona la dificultad y comienza a jugar.
Un ejecutable es un archivo que puede correr directamente sin necesidad de tener Python instalado. Es perfecto para:
- Compartir el juego con amigos o familiares
- Ejecutar en computadoras sin Python
- Distribución fácil (solo un archivo .exe)
Paso 1: Doble click en el archivo build_exe.bat
Paso 2: Espera a que termine (puede tardar 1-2 minutos)
Paso 3: El ejecutable estará en la carpeta dist/Sudoku.exe
# Dar permisos de ejecución
chmod +x build_exe.sh
# Ejecutar el script
./build_exe.shEl ejecutable estará en dist/Sudoku
Paso 1: Instalar PyInstaller
pip install pyinstallerPaso 2: Generar el ejecutable
# Windows - Crear Sudoku.exe
pyinstaller --onefile --windowed --name "Sudoku" main.py
# Linux/Mac - Crear ejecutable Sudoku
pyinstaller --onefile --windowed --name "Sudoku" main.pyPaso 3: Encuentra tu ejecutable
dist/
└── Sudoku.exe # Windows
o
└── Sudoku # Linux/Mac
--onefile: Crea un solo archivo ejecutable (todo incluido)--windowed: No muestra la consola negra, solo la ventana del juego--name "Sudoku": Nombre del archivo ejecutable
Una vez creado, puedes:
- Copiar el archivo
Sudoku.exea cualquier carpeta - Compartirlo con USB, correo, etc.
- Ejecutarlo haciendo doble click (no necesita Python)
- El archivo es grande (20-40 MB) porque incluye Python y todas las librerías
- Windows puede mostrar advertencia de seguridad (es normal, tu lo creaste)
- El primer inicio puede tardar unos segundos
Si tienes un archivo de ícono (.ico):
pyinstaller --onefile --windowed --name "Sudoku" --icon=sudoku_icon.ico main.pyProyect_Sudoku/
│
├── main.py # Punto de entrada principal
├── config.py # Configuración global (colores, constantes)
├── sudoku_generator.py # Generador de tableros Sudoku
├── sudoku_game.py # Lógica del juego
├── sudoku_gui.py # Interfaz gráfica con Tkinter
├── statistics.py # Gestión de estadísticas
├── sudoku_stats.json # Archivo de estadísticas (se crea automáticamente)
└── README.md # Este archivo
Propósito: Almacenar todas las constantes del juego en un solo lugar.
# Colores del juego
COLORS = {
'bg': '#F0F0F0', # Fondo general
'grid_bg': '#FFFFFF', # Fondo de celdas
'fixed_cell': '#E8E8E8', # Celdas fijas (iniciales)
'selected': '#BBE5FF', # Celda seleccionada
'correct': '#C8E6C9', # Celda correcta (verde)
'incorrect': '#FFCDD2', # Celda incorrecta (rojo)
# ... más colores
}
# Dimensiones
CELL_SIZE = 60 # Tamaño de cada celda en píxeles
GRID_SIZE = 9 # Tamaño del tablero (9x9)
WINDOW_WIDTH = 800 # Ancho de la ventana
WINDOW_HEIGHT = 700 # Alto de la ventana
# Dificultades (cantidad de celdas vacías)
DIFFICULTY_LEVELS = {
'Fácil': 30, # 30 celdas vacías
'Medio': 45, # 45 celdas vacías
'Difícil': 55 # 55 celdas vacías
}
# Sistema de puntuación
BASE_SCORE = 1000 # Puntuación base
TIME_BONUS_POINTS = 1 # +1 punto cada 5 segundos restantes
ERROR_PENALTY = 5 # -5 puntos por error
HINT_PENALTY = 10 # -10 puntos por pista usada¿Por qué usar un archivo de configuración?
- Mantenibilidad: Cambiar colores o valores en un solo lugar
- Legibilidad: Valores con nombres descriptivos
- Reutilización: Importar desde cualquier módulo
Propósito: Generar tableros válidos de Sudoku con solución única.
¿Qué hace? Genera un tablero de Sudoku completo y luego remueve celdas para crear el puzzle.
Parámetros:
empty_cells(int): Cantidad de celdas vacías (define dificultad)
Retorna:
- Tupla con dos matrices 9x9:
puzzle: Tablero con celdas vacías para jugarsolution: Tablero completo con la solución
Algoritmo:
-
Crear tablero vacío: Matriz 9x9 con ceros
self.grid = [[0 for _ in range(9)] for _ in range(9)]
-
Llenar tablero usando backtracking (método
_fill_grid()):- Para cada celda vacía:
- Probar números del 1-9 en orden aleatorio
- Verificar si el número es válido (no se repite en fila/columna/subcuadro)
- Si es válido, colocarlo y continuar recursivamente
- Si no hay solución, retroceder (backtrack) y probar otro número
- Para cada celda vacía:
-
Copiar solución:
solution = [row[:] for row in self.grid]
-
Remover celdas aleatoriamente:
- Crear lista de todas las posiciones (81 celdas)
- Mezclar aleatoriamente
- Poner 0 en las primeras N posiciones
Ejemplo de uso:
generator = SudokuGenerator()
puzzle, solution = generator.generate(30) # Genera puzzle fácil¿Qué hace? Verifica si un número puede colocarse en una posición según las reglas del Sudoku.
Parámetros:
row(int): Fila (0-8)col(int): Columna (0-8)num(int): Número a validar (1-9)
Retorna:
Truesi es válido,Falsesi no lo es
Verifica:
-
Fila: El número no existe en la fila
if num in self.grid[row]: return False
-
Columna: El número no existe en la columna
if num in [self.grid[i][col] for i in range(9)]: return False
-
Subcuadro 3x3: El número no existe en el subcuadro
box_row, box_col = 3 * (row // 3), 3 * (col // 3) for i in range(box_row, box_row + 3): for j in range(box_col, box_col + 3): if self.grid[i][j] == num: return False
Valida si un movimiento del jugador es correcto.
Dos modos de validación:
- Con solución: Compara directamente con la solución correcta
- Sin solución: Solo verifica que no rompa las reglas básicas
Verifica si el tablero está completo y cuenta errores.
Retorna: (está_completo, cantidad_de_errores)
Propósito: Manejar el estado del juego, movimientos y puntuación.
Inicializa una nueva partida:
def __init__(self, difficulty: str):
self.difficulty = difficulty # 'Fácil', 'Medio', o 'Difícil'
self.grid = [] # Tablero actual
self.solution = [] # Solución correcta
self.initial_grid = [] # Tablero inicial (celdas fijas)
self.start_time = time.time() # Tiempo de inicio
self.hints_used = 0 # Contador de pistas
self.game_over = False # ¿Juego terminado?
self.won = False # ¿Ganó el juego?¿Qué hace? Intenta colocar un número en una celda.
Validaciones:
- Juego no terminado
- Celda no es fija
- Número válido (0 para borrar, 1-9 para colocar)
Proceso:
if num == 0: # Borrar celda
self.grid[row][col] = 0
else: # Colocar número
self.grid[row][col] = numRetorna: (éxito, mensaje)
¿Qué hace? Revela el número correcto de una celda.
Proceso:
- Verificar que la celda no sea fija
- Incrementar contador de pistas
- Retornar el número correcto de la solución
def use_hint(self, row: int, col: int) -> Optional[int]:
if self.is_fixed_cell(row, col):
return None
self.hints_used += 1
return self.solution[row][col]¿Qué hace? Calcula la puntuación final del juego.
Fórmula:
Puntuación = BASE_SCORE (1000)
+ Bonificación de tiempo
- Penalización por pistas
Bonificación de tiempo = (tiempo_restante // 5) * 1
Penalización por pistas = hints_used * 10
Ejemplo:
Dificultad: Medio (límite 40 minutos = 2400 segundos)
Tiempo usado: 1200 segundos (20 minutos)
Tiempo restante: 1200 segundos
Pistas usadas: 3
Cálculo:
Base: 1000
+ Bonificación: (1200 // 5) * 1 = 240
- Pistas: 3 * 10 = 30
= 1000 + 240 - 30 = 1210 puntos
¿Qué hace? Determina el estado visual de una celda.
Estados posibles:
'fixed': Celda fija (del tablero inicial)'empty': Celda vacía'user': Celda llenada por el usuario'correct': Celda correcta (al terminar)'incorrect': Celda incorrecta (al terminar)
Propósito: Guardar y cargar estadísticas del juego en formato JSON.
{
"games_played": 10, # Total partidas jugadas
"games_won": 7, # Total partidas ganadas
"total_score": 7500, # Suma de puntuaciones
"best_score": 1250, # Mejor puntuación
"best_time": 456, # Mejor tiempo (segundos)
"by_difficulty": {
"Fácil": {
"played": 5,
"won": 4,
"best_score": 1100,
"best_time": 300,
"avg_time": 350.5
},
# ... Medio, Difícil
},
"history": [
{
"date": "2025-01-15 14:30:00",
"difficulty": "Medio",
"won": true,
"score": 1150,
"time": 1200,
"hints_used": 2
},
# ... más partidas (máx 50)
]
}¿Qué hace? Registra una partida completada y actualiza todas las estadísticas.
Proceso paso a paso:
-
Actualizar estadísticas generales:
self.stats['games_played'] += 1 if won: self.stats['games_won'] += 1 self.stats['total_score'] += score
-
Actualizar mejor puntuación:
if score > self.stats['best_score']: self.stats['best_score'] = score
-
Actualizar mejor tiempo:
if self.stats['best_time'] is None or time_seconds < self.stats['best_time']: self.stats['best_time'] = time_seconds
-
Actualizar estadísticas por dificultad:
diff_stats = self.stats['by_difficulty'][difficulty] diff_stats['played'] += 1 if won: diff_stats['won'] += 1
-
Calcular tiempo promedio (fórmula de promedio ponderado):
# Fórmula: nuevo_promedio = (promedio_anterior * (n-1) + nuevo_valor) / n total_won = diff_stats['won'] current_avg = diff_stats['avg_time'] diff_stats['avg_time'] = ((current_avg * (total_won - 1)) + time_seconds) / total_won
-
Agregar al historial:
game_record = { 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'difficulty': difficulty, 'won': won, 'score': score, 'time': time_seconds, 'hints_used': hints_used } self.stats['history'].append(game_record)
-
Limitar historial a 50 partidas:
if len(self.stats['history']) > 50: self.stats['history'] = self.stats['history'][-50:]
-
Guardar en archivo JSON:
self._save_stats()
Calcula el porcentaje de victorias:
if self.stats['games_played'] == 0:
return 0.0
return (self.stats['games_won'] / self.stats['games_played']) * 100Convierte segundos a formato MM:SS:
if seconds is None:
return "--:--"
minutes = seconds // 60
secs = seconds % 60
return f"{minutes:02d}:{secs:02d}"Propósito: Crear la interfaz visual del juego con Tkinter.
def __init__(self):
self.root = tk.Tk() # Ventana principal
self.root.title("🎮 Sudoku - Juego de Lógica")
self.game = None # Instancia del juego
self.stats_manager = StatisticsManager() # Gestor de estadísticas
self.selected_cell = None # Celda seleccionada
self.cells = [[None for _ in range(9)] for _ in range(9)] # Referencias a celdas¿Qué hace? Muestra la pantalla inicial con los botones de dificultad.
Estructura visual:
╔════════════════════════════╗
║ SUDOKU ║
║ Entrena tu mente... ║
║ ║
║ Selecciona dificultad: ║
║ ║
║ [🟢 Fácil ] ║
║ Para principiantes ║
║ ║
║ [🟡 Medio ] ║
║ Desafío moderado ║
║ ║
║ [🔴 Difícil ] ║
║ Para expertos ║
║ ║
║ [📊 Ver Estadísticas] ║
╚════════════════════════════╝
Código clave:
for diff, emoji, desc in difficulties:
btn = tk.Button(
...,
text=f"{emoji} {diff}",
command=lambda d=diff: self.start_game(d, start_frame)
)¿Qué hace? Crea el tablero visual de 9x9 con celdas clickeables.
Proceso:
- Crear un Frame con borde grueso
- Para cada celda (i, j):
- Crear Frame individual
- Aplicar bordes más gruesos cada 3 celdas (subcuadros 3x3)
- Crear Label con el número
- Asignar color según estado de la celda
- Vincular evento de click
Código clave:
for i in range(9):
for j in range(9):
# Bordes más gruesos para subcuadros
padx = (3 if j % 3 == 0 else 1, 3 if j == 8 else 1)
pady = (3 if i % 3 == 0 else 1, 3 if i == 8 else 1)
cell = tk.Label(...)
cell.bind('<Button-1>', lambda e, row=i, col=j: self.select_cell(row, col))
self.cells[i][j] = cell¿Qué hace? Crea el teclado numérico y botones de acción.
Botones:
- Números 1-9: Para colocar en la celda seleccionada
- Borrar: Limpia la celda seleccionada
- Pista: Revela el número correcto
- Verificar: Revisa si el tablero es correcto
- Reiniciar: Reinicia el juego actual
- Menú: Vuelve al menú principal
¿Qué hace? Selecciona una celda al hacer click.
Proceso:
- Deseleccionar celda anterior (restaurar color)
- Seleccionar nueva celda (color azul)
- Guardar posición seleccionada
# Deseleccionar anterior
if self.selected_cell:
old_row, old_col = self.selected_cell
self.cells[old_row][old_col]['bg'] = self.get_cell_color(old_row, old_col)
# Seleccionar nueva
self.selected_cell = (row, col)
self.cells[row][col]['bg'] = COLORS['selected']¿Qué hace? Coloca un número en la celda seleccionada.
Proceso:
- Verificar que hay celda seleccionada
- Si modo ayuda activo, validar el número
- Colocar número en el juego
- Actualizar visualización
if self.help_var.get(): # Modo ayuda activo
is_valid, message = self.game.validate_current_move(row, col, num)
if not is_valid:
messagebox.showwarning("Movimiento Incorrecto", message)
return
success, message = self.game.make_move(row, col, num)
if success:
self.cells[row][col]['text'] = str(num)¿Qué hace? Verifica si el tablero está completo y correcto.
Proceso:
- Llamar a
game.check_victory() - Si ganó:
- Calcular puntuación
- Guardar estadísticas
- Mostrar mensaje de victoria
- Ofrecer jugar otra partida
- Si hay errores:
- Actualizar colores (rojo para incorrectas)
- Mostrar cantidad de errores
- Si incompleto:
- Informar al usuario
Mensaje de victoria:
message = f"🎉 ¡FELICITACIONES! 🎉\n\n"
message += f"Has completado el Sudoku de nivel {self.game.difficulty}\n\n"
message += f"⏱️ Tiempo: {time_str}\n"
message += f"💡 Pistas usadas: {self.game.hints_used}\n"
message += f"🏆 Puntuación: {score} puntos\n\n"¿Qué hace? Actualiza el cronómetro cada segundo.
Proceso:
- Obtener tiempo transcurrido
- Formatear como MM:SS
- Actualizar etiqueta
- Programar siguiente actualización (recursión con after)
def update_time(self):
if self.game and not self.game.game_over:
elapsed = self.game.get_elapsed_time()
minutes = elapsed // 60
seconds = elapsed % 60
self.time_label['text'] = f"{minutes:02d}:{seconds:02d}"
self.root.after(1000, self.update_time) # Llamar de nuevo en 1 segundoPropósito: Iniciar la aplicación.
def main():
"""Punto de entrada principal del programa"""
try:
# Crear e iniciar la interfaz gráfica
app = SudokuGUI()
app.run()
except Exception as e:
print(f"Error al iniciar el juego: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()¿Qué hace if __name__ == "__main__"?
- Verifica que el archivo se está ejecutando directamente (no importado)
- Permite que los módulos se importen sin ejecutar código automáticamente
1. Usuario ejecuta: python main.py
↓
2. main.py crea instancia de SudokuGUI()
↓
3. SudokuGUI muestra pantalla de inicio
↓
4. Usuario selecciona dificultad (ej: "Medio")
↓
5. Se llama a start_game("Medio")
↓
6. SudokuGame genera tablero:
- SudokuGenerator crea tablero completo
- Remueve 45 celdas (dificultad Medio)
↓
7. Se muestra el tablero en la interfaz
↓
8. Usuario juega (ciclo):
│
├─ Click en celda → select_cell()
│ └─ Celda se marca con color azul
│
├─ Click en número → place_number()
│ ├─ Si modo ayuda: valida contra solución
│ └─ Coloca número en grid
│
├─ Click en pista → use_hint()
│ ├─ Incrementa contador de pistas
│ └─ Muestra número correcto
│
└─ Click en verificar → check_solution()
│
├─ Si está completo y correcto:
│ ├─ Calcular puntuación
│ ├─ Guardar estadísticas en JSON
│ ├─ Mostrar mensaje de victoria
│ └─ Ofrecer jugar otra partida
│
├─ Si tiene errores:
│ ├─ Marcar celdas incorrectas en rojo
│ └─ Mostrar cantidad de errores
│
└─ Si está incompleto:
└─ Informar al usuario
9. Usuario decide:
├─ Jugar otra → Vuelve a paso 3
└─ Salir → Cierra ventana
# Tablero de Sudoku 9x9
grid = [
[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
# ... 7 filas más
]
# Acceso a elementos
valor = grid[2][4] # Fila 2, Columna 4
grid[2][4] = 7 # Asignar valor# Configuración con clave-valor
COLORS = {
'bg': '#F0F0F0',
'selected': '#BBE5FF'
}
# Acceso
color_fondo = COLORS['bg']# Historial de partidas
history = [
{'date': '2025-01-15', 'score': 1100},
{'date': '2025-01-16', 'score': 1200}
]
# Agregar elemento
history.append(nueva_partida)
# Últimos N elementos
ultimas_10 = history[-10:]# Ejemplo 1: Validación de rango
if num < 1 or num > 9:
return False, "Número inválido"
# Ejemplo 2: Estado de celda
if self.is_fixed_cell(row, col):
return 'fixed'
elif self.grid[row][col] == 0:
return 'empty'
else:
return 'user'
# Ejemplo 3: Verificación de victoria
if complete and errors == 0:
self.won = True
elif errors > 0:
self.show_errors()
else:
self.show_incomplete_message()# Recorrer matriz
for i in range(9):
for j in range(9):
print(grid[i][j])
# Recorrer lista
for difficulty in ['Fácil', 'Medio', 'Difícil']:
print(difficulty)
# Con índice
for index, item in enumerate(lista):
print(f"Elemento {index}: {item}")# Juego principal (mientras no termine)
while not game_over:
esperar_movimiento()
if movimiento_valido:
aplicar_movimiento()# Función simple
def saludar(nombre):
return f"Hola, {nombre}"
# Función con tipo de retorno
def sumar(a: int, b: int) -> int:
return a + b
# Función con valores por defecto
def crear_usuario(nombre, edad=18):
return {'nombre': nombre, 'edad': edad}
# Función con múltiples retornos
def dividir(a, b):
if b == 0:
return False, "Error: división por cero"
return True, a / bdef _fill_grid(self):
"""Llena el tablero usando recursión"""
for i in range(9):
for j in range(9):
if self.grid[i][j] == 0: # Celda vacía
for num in range(1, 10): # Probar 1-9
if self._is_valid(i, j, num):
self.grid[i][j] = num
# Llamada recursiva
if self._fill_grid():
return True # Solución encontrada
# Backtrack: deshacer y probar otro
self.grid[i][j] = 0
return False # No hay solución
return True # Tablero completoCómo funciona el backtracking:
- Intentar colocar un número
- Si funciona, continuar con la siguiente celda (recursión)
- Si no funciona, retroceder y probar otro número
- Repetir hasta encontrar solución
class SudokuGame:
"""Clase que representa un juego"""
def __init__(self, difficulty):
"""Constructor: inicializa atributos"""
self.difficulty = difficulty
self.grid = []
self.score = 0
def make_move(self, row, col, num):
"""Método: realizar un movimiento"""
self.grid[row][col] = num
# Crear objeto (instancia)
juego = SudokuGame("Fácil")
juego.make_move(0, 0, 5)class StatisticsManager:
def __init__(self):
self.stats = {} # Atributo privado (convención con _)
def _load_stats(self): # Método privado
"""Solo usado internamente"""
pass
def get_stats(self): # Método público
"""Acceso controlado a datos"""
return self.statsimport json
# Guardar datos
datos = {'nombre': 'Juan', 'puntos': 100}
with open('datos.json', 'w', encoding='utf-8') as f:
json.dump(datos, f, indent=2)
# Cargar datos
with open('datos.json', 'r', encoding='utf-8') as f:
datos = json.load(f)# Vincular evento de click
button = tk.Button(text="Click me", command=self.on_click)
# Evento con parámetros
button.bind('<Button-1>', lambda e: self.on_click(param))
# Hover effects
def on_enter(event):
button['background'] = 'blue'
button.bind("<Enter>", on_enter)import time
from datetime import datetime
# Medir tiempo transcurrido
start_time = time.time()
# ... hacer algo ...
elapsed = time.time() - start_time
# Fecha y hora actual
now = datetime.now()
formatted = now.strftime('%Y-%m-%d %H:%M:%S')
# Resultado: "2025-01-15 14:30:00"Puntuación Final = BASE_SCORE + Bonificación de Tiempo - Penalizaciones
Donde:
BASE_SCORE = 1000 puntos
Bonificación de Tiempo:
tiempo_restante = límite_de_tiempo - tiempo_usado
bonificación = (tiempo_restante // 5) * 1
Penalizaciones:
- Por cada pista usada: -10 puntos
- Por cada celda incorrecta al finalizar: -5 puntos
| Dificultad | Tiempo Límite | Celdas Vacías |
|---|---|---|
| Fácil | 30 minutos | 30 celdas |
| Medio | 40 minutos | 45 celdas |
| Difícil | 60 minutos | 55 celdas |
Dificultad: Fácil
Tiempo usado: 10 minutos (600 segundos)
Tiempo restante: 20 minutos (1200 segundos)
Pistas usadas: 0
Errores: 0
Cálculo:
Base: 1000
+ Bonificación: (1200 // 5) * 1 = 240
- Pistas: 0 * 10 = 0
- Errores: 0 * 5 = 0
= 1000 + 240 = 1240 puntos ⭐⭐⭐
Dificultad: Medio
Tiempo usado: 25 minutos (1500 segundos)
Tiempo restante: 15 minutos (900 segundos)
Pistas usadas: 5
Errores: 0
Cálculo:
Base: 1000
+ Bonificación: (900 // 5) * 1 = 180
- Pistas: 5 * 10 = 50
- Errores: 0 * 5 = 0
= 1000 + 180 - 50 = 1130 puntos ⭐⭐
Dificultad: Difícil
Tiempo usado: 55 minutos (3300 segundos)
Tiempo restante: 5 minutos (300 segundos)
Pistas usadas: 8
Errores al finalizar: 3
Cálculo:
Base: 1000
+ Bonificación: (300 // 5) * 1 = 60
- Pistas: 8 * 10 = 80
- Errores: 3 * 5 = 15
= 1000 + 60 - 80 - 15 = 965 puntos ⭐
Completar la cuadrícula 9×9 con dígitos del 1 al 9 cumpliendo:
- Cada fila debe contener los números 1-9 sin repetir
- Cada columna debe contener los números 1-9 sin repetir
- Cada subcuadro 3×3 debe contener los números 1-9 sin repetir
┌───────┬───────┬───────┐
│ 5 3 . │ . 7 . │ . . . │ ← Fila: no repetir 1-9
│ 6 . . │ 1 9 5 │ . . . │
│ . 9 8 │ . . . │ . 6 . │
├───────┼───────┼───────┤
│ 8 . . │ . 6 . │ . . 3 │
│ 4 . . │ 8 . 3 │ . . 1 │
│ 7 . . │ . 2 . │ . . 6 │
├───────┼───────┼───────┤
│ . 6 . │ . . . │ 2 8 . │
│ . . . │ 4 1 9 │ . . 5 │
│ . . . │ . 8 . │ . 7 9 │
└───────┴───────┴───────┘
↑ ↑ ↑
Columna Subcuadro Subcuadro
no 3x3: no 3x3: no
repetir repetir repetir
1-9 1-9 1-9
- Eliminación: Si un número ya está en fila/columna/subcuadro, no puede ir ahí
- Únicos: Si solo queda una opción para una celda, esa es la respuesta
- Pares/Tripletas: Técnicas avanzadas de eliminación
- Modo ayuda: Activa el modo ayuda del juego para validar movimientos
Problema: Al ejecutar python main.py no pasa nada o hay error.
Soluciones:
-
Verificar versión de Python:
python --version # Debe ser 3.7 o superior -
Intentar con
python3:python3 main.py
-
Verificar que tkinter está instalado:
python -m tkinter # Debe abrir una ventana de prueba -
En Linux, instalar tkinter si falta:
sudo apt-get install python3-tk
Problema: Python no encuentra los módulos.
Solución:
- Asegúrate de estar en la carpeta correcta:
cd "ruta/a/Proyect_Sudoku" python main.py
Problema: La generación de tableros tarda mucho.
Solución:
- Esto es normal para nivel Difícil (puede tardar 2-5 segundos)
- Si tarda más de 10 segundos, verifica que no haya otros programas consumiendo CPU
Problema: Al cerrar y abrir, las estadísticas se perdieron.
Soluciones:
- Verificar que el archivo
sudoku_stats.jsonse creó en la carpeta del juego - Verificar permisos de escritura en la carpeta
- En Windows, ejecutar como administrador si es necesario
Problema: La ventana está cortada o muy grande.
Solución:
- Ajustar constantes en
config.py:CELL_SIZE = 50 # Reducir tamaño de celdas WINDOW_WIDTH = 700 # Ajustar ancho WINDOW_HEIGHT = 600 # Ajustar alto
-
Estructuras de Datos:
- Matrices (listas de listas)
- Diccionarios
- Listas
-
Control de Flujo:
- Condicionales (if/elif/else)
- Bucles (for/while)
- Recursión (backtracking)
-
Funciones:
- Parámetros y retornos
- Funciones con múltiples retornos
- Type hints
-
Programación Orientada a Objetos:
- Clases y objetos
- Atributos y métodos
- Encapsulación
-
Interfaz Gráfica (GUI):
- Tkinter básico
- Eventos y callbacks
- Layout management
-
Persistencia de Datos:
- Lectura/escritura de archivos
- Formato JSON
- Manejo de errores
-
Algoritmos:
- Backtracking
- Validación
- Búsqueda
Proyecto desarrollado como parte del curso:
- Pensamiento Computacional para Ingeniería (Gpo 401)
- TEC 1ER AÑO DE PROFE
- 2025
Tecnologías utilizadas:
- Python 3.7+
- Tkinter (GUI)
- JSON (persistencia)
Autor: Sistema de Sudoku
Leonardo Montoya Chavarría - A01613677 Alonso Osuna Maruri - A01613556
Para iniciar el juego, simplemente ejecuta:
python main.py¡Disfruta entrenando tu mente con Sudoku!!!!!