Este tutorial adapta pedagógicamente el Día 84. La sección 📝 Contenido original del curso proviene del único archivo de la carpeta:
dia84-tarea3.md(enunciado de la tarea y pregunta de reflexión, en inglés; aquí se presenta traducido). Como en todos los proyectos de portafolio, el curso no ofrece guía paso a paso ni solución, y este tutorial no entrega el juego terminado: ofrece análisis del problema, piezas verificadas, pruebas y retos. Todo lo que no está en ese archivo (representación del tablero, funciones de ejemplo, errores frecuentes, algoritmo minimax, retos 🟢🟡🟠🔴, proyecto extra y la etapa 3 de Mi Portafolio Profesional) es creación de esta adaptación.
Nota sobre las verificaciones: el 28 de septiembre de 2026, con Python 3.14, se ejecutaron las funciones de las secciones 3 a 5: se comprobó la detección de ganador en filas, columnas y diagonales, el empate, la validación de casillas y que la IA minimax de la sección 6 nunca pierde (se recorrieron todas las partidas posibles del jugador humano contra ella: el peor resultado para la IA es empate).
Tabla de contenidos
- La tarea: Tres en línea para dos jugadores
- Analizar el problema antes de programar
- 📖 Representar el tablero
- 📖 Detectar al ganador sin 8
ifdistintos - 📖 Validar la entrada del jugador
- 📖 El reto opcional: un jugador artificial
- 🚀 Proyecto del día: construcción por versiones
- 🌟 Proyecto extra: Conecta 4 en consola
- 🏗️ Mi Portafolio Profesional — Etapa 3
- 🧩 Comprueba lo aprendido
- 🤖 Programar con apoyo de IA
- 🛠️ Observaciones sobre el material original
- 📋 Matriz de trazabilidad
- 📦 Evidencias de aprendizaje del Día 84
1. La tarea: Tres en línea para dos jugadores
(Fuente: dia84-tarea3.md)
📝 Contenido original del curso
Usando lo que aprendiste de programación en Python, construye una versión de texto del juego Tres en línea (Tic Tac Toe). Debe poder jugarse en la línea de comandos, igual que el Blackjack del Día 11. Es un juego de 2 jugadores: uno es "X" y el otro "O".
Puedes ver cómo funciona el juego buscando "tic tac toe" en Google. Tú eliges cómo se ve: lo más simple es dibujar el tablero con los símbolos | y _, pero el diseño es tuyo. (El enunciado incluye una imagen de ejemplo de un tablero dibujado con esos símbolos.)
Si tienes más tiempo, rétate a construir un jugador de IA que juegue contra ti.
Pregunta de la tarea — Momento de reflexión: escribe cómo abordaste el proyecto; qué fue difícil y qué fácil; cómo mejorarías en el próximo; cuál fue tu mayor aprendizaje; y qué harías distinto si lo hicieras de nuevo.
Prerequisitos: variables y
input()(Días 1–2), condicionales (Día 3), listas (Día 4), ciclosforywhile(Día 5), funciones con parámetros y retorno (Días 8 y 10), el juego de consola del Día 11 (Blackjack), depuración (Día 13).
2. Analizar el problema antes de programar
(Sección creada para esta adaptación.)
🔍 Antes de programar
| Pregunta | Respuesta que debes construir tú |
|---|---|
| ¿Qué información tenemos? | Un tablero de 3 × 3, dos jugadores con símbolos distintos |
| ¿Qué información necesitamos obtener? | La casilla que elige cada jugador en su turno |
| ¿Qué datos debemos almacenar? | El contenido de las 9 casillas y de quién es el turno |
| ¿Qué proceso se repite? | Mostrar tablero → pedir jugada → validarla → colocarla → revisar si terminó → cambiar turno |
| ¿Qué resultado esperamos? | Un mensaje de victoria ("Gana X") o de empate |
| ¿Qué condiciones hay que tener en cuenta? | Casillas ocupadas, entradas que no son números, números fuera de rango, empate con el tablero lleno |
Diagrama de flujo del juego
┌──────────────┐
│ Crear tablero│
│ turno = "X" │
└──────┬───────┘
▼
┌───► Mostrar tablero
│ ▼
│ Pedir casilla ◄────────┐
│ ▼ │
│ ¿Es válida? ── No ─────┘ (mostrar el motivo)
│ │ Sí
│ ▼
│ Colocar el símbolo
│ ▼
│ ¿Hay ganador? ── Sí ──► Anunciar ganador ──► FIN
│ │ No
│ ▼
│ ¿Tablero lleno? ── Sí ──► Anunciar empate ──► FIN
│ │ No
│ ▼
└──── Cambiar de turno📝 Divide y vencerás: cada caja del diagrama se convierte en una función pequeña que puedes probar por separado. Es la misma estrategia del Blackjack (Día 11).
3. 📖 Representar el tablero
(Sección creada para esta adaptación.)
🎯 Objetivo de aprendizaje
Elegir una estructura de datos adecuada para el tablero y justificar la elección.
📖 Explicación conceptual
Hay varias formas válidas. Compararlas es parte del trabajo de un desarrollador:
| Representación | Ejemplo | Ventajas | Desventajas |
|---|---|---|---|
| Lista de 9 elementos | [" ", "X", " ", ...] |
Simple; la casilla 1 a 9 del jugador es el índice 0 a 8 | Hay que pensar qué índices forman una fila |
| Lista de listas (3 × 3) | [[" ", "X", " "], [...], [...]] |
Se parece al tablero real: tablero[fila][columna] |
El jugador debe dar dos números o hay que convertir |
| Diccionario | {1: " ", 2: "X", ...} |
Las claves coinciden con lo que escribe el jugador | Recorrer filas y diagonales es menos directo |
En este tutorial usamos una lista de 9 elementos, numerada así para el jugador:
1 | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9🔎 Ejemplo explicado
def crear_tablero():
return [" "] * 9
def dibujar(tablero):
filas = []
for i in range(0, 9, 3):
filas.append(f" {tablero[i]} | {tablero[i+1]} | {tablero[i+2]} ")
return "\n---+---+---\n".join(filas)
print(dibujar(crear_tablero()))[" "] * 9crea una lista con 9 espacios: todas las casillas vacías.range(0, 9, 3)produce 0, 3 y 6: el primer índice de cada fila.dibujardevuelve el texto en lugar de imprimirlo. Así puedes probarla (comparar el texto) y decidir después dónde mostrarlo."\n---+---+---\n".join(filas)pone la línea separadora entre filas, no al final.
⚠️ Errores frecuentes
| Error | Qué ocurre | Solución |
|---|---|---|
tablero = [[" "] * 3] * 3 |
Al marcar una casilla, cambia la misma columna en las 3 filas | Las 3 filas son la misma lista repetida. Usa [[" "] * 3 for _ in range(3)] |
Casillas vacías con "" en lugar de " " |
El tablero se "encoge" al dibujarlo | Usa un espacio, o un número guía ("1", "2"…) |
| Confundir casilla del jugador (1–9) con índice (0–8) | Se marca la casilla de al lado | Convierte una sola vez: indice = casilla - 1 |
4. 📖 Detectar al ganador sin 8 if distintos
(Sección creada para esta adaptación.)
🎯 Objetivo de aprendizaje
Usar datos (una lista de tuplas) en lugar de código repetido para comprobar las 8 líneas ganadoras.
📖 Explicación conceptual
Hay 8 formas de ganar: 3 filas, 3 columnas y 2 diagonales. Podrías escribir 8 condiciones, pero es fácil equivocarse en una. Mejor: guarda las 8 líneas como datos y recórrelas con un ciclo.
LINEAS = [
(0, 1, 2), (3, 4, 5), (6, 7, 8), # filas
(0, 3, 6), (1, 4, 7), (2, 5, 8), # columnas
(0, 4, 8), (2, 4, 6), # diagonales
]
def ganador(tablero):
for a, b, c in LINEAS:
if tablero[a] != " " and tablero[a] == tablero[b] == tablero[c]:
return tablero[a]
return NoneCómo funciona:
for a, b, c in LINEASdesempaqueta cada tupla: en la primera vuelta,a = 0,b = 1,c = 2.tablero[a] == tablero[b] == tablero[c]es una comparación encadenada: equivale atablero[a] == tablero[b] and tablero[b] == tablero[c].- La condición
tablero[a] != " "es imprescindible (ver errores frecuentes). - Devuelve
"X","O"oNone. Un valor que "significa nada" es más claro que devolverFalseo una cadena vacía.
Tu turno: escribe tablero_lleno(tablero), que devuelva True si no queda ninguna casilla vacía. Pista: el operador in (Día 4) la resuelve en una línea.
⚠️ Errores frecuentes
| Error | Qué ocurre | Por qué | Solución |
|---|---|---|---|
Olvidar tablero[a] != " " |
En el primer turno el juego anuncia un ganador vacío | Tres casillas vacías también son iguales entre sí | Comprueba que la casilla no esté vacía |
| Revisar el empate antes que el ganador | Si la última jugada gana, el juego dice "empate" | Con la 9.ª jugada el tablero está lleno y hay ganador | Primero ganador, después empate |
Escribir mal una línea ((2, 4, 7)) |
Hay partidas ganadas que el juego no detecta | Error de datos, no de lógica | Pruebas de la sección 7: una por cada línea |
5. 📖 Validar la entrada del jugador
(Sección creada para esta adaptación.)
Un programa que se cierra porque alguien escribió "hola" no es un proyecto de portafolio. Buenas prácticas: validar y explicar el error al usuario.
def leer_casilla(texto, tablero):
if not texto.isdigit():
return None, "Escribe un número del 1 al 9."
posicion = int(texto) - 1
if posicion not in range(9):
return None, "El número debe estar entre 1 y 9."
if tablero[posicion] != " ":
return None, "Esa casilla ya está ocupada."
return posicion, ""- La función no llama a
input(): recibe el texto. Así se puede probar sin teclear (leer_casilla("10", tablero)). - Devuelve dos valores (una tupla): la posición (o
None) y un mensaje. Quien la llama decide si vuelve a preguntar. - El orden importa: primero se comprueba que sea número, luego el rango, luego si está ocupada. ¿Qué pasaría si se comprobara
tablero[posicion]antes del rango?
Alternativa: usar try / except ValueError con int(texto) (Día 30). Diferencia: isdigit() rechaza "-3" y " 5"; int() acepta " 5" (ignora espacios) y "-3". Decide cuál prefieres y justifícalo en tu diario.
Tu turno: escribe el ciclo while que pide la casilla hasta que sea válida, usando leer_casilla.
6. 📖 El reto opcional: un jugador artificial
(El enunciado lo propone como reto "si tienes más tiempo". Las estrategias y el código son creación de esta adaptación.)
Tres niveles de "inteligencia"
| Nivel | Estrategia | Dificultad de programar | ¿Se le puede ganar? |
|---|---|---|---|
| 🟢 Fácil | Elegir una casilla vacía al azar (random.choice, Día 4) |
Baja | Sí, con facilidad |
| 🟡 Medio | Reglas en orden: 1) si puedo ganar, gano; 2) si el rival va a ganar, lo bloqueo; 3) centro; 4) esquina; 5) cualquiera | Media | A veces |
| 🔴 Perfecto | Minimax: probar todas las partidas posibles y elegir la mejor | Alta (recursión) | Nunca: como mucho, empate |
📖 La idea de minimax
La IA ("O") imagina cada jugada posible, luego cada respuesta del humano ("X"), y así hasta el final de la partida. A cada final le asigna un puntaje: +1 si gana O, −1 si gana X, 0 si hay empate. Supone que el rival juega lo mejor posible: en el turno de O se escoge el máximo; en el de X, el mínimo.
def minimax(tablero, turno):
g = ganador(tablero)
if g == "O":
return 1
if g == "X":
return -1
if tablero_lleno(tablero):
return 0
puntajes = []
for i in range(9):
if tablero[i] == " ":
tablero[i] = turno # probar la jugada
siguiente = "X" if turno == "O" else "O"
puntajes.append(minimax(tablero, siguiente)) # la función se llama a sí misma
tablero[i] = " " # deshacer la jugada
return max(puntajes) if turno == "O" else min(puntajes)Prerequisito nuevo — recursión: una función recursiva se llama a sí misma con un problema más pequeño (aquí, un tablero con una casilla menos) hasta llegar a un caso base (partida terminada). Sin caso base, se llamaría para siempre.
Tu turno: escribe mejor_jugada(tablero): para cada casilla vacía, coloca "O", calcula minimax(tablero, "X"), deshaz la jugada y quédate con la casilla de mayor puntaje.
Observa: en un tablero vacío, minimax explora cientos de miles de posiciones y tarda un momento. ¿Cómo lo acelerarías? (Ideas: jugar el centro sin calcular en la primera jugada; guardar resultados ya calculados en un diccionario.)
7. 🚀 Proyecto del día: construcción por versiones
(Desarrolla la tarea dia84-tarea3.md. Las versiones son una propuesta de esta adaptación.)
Problema
Construir un Tres en línea jugable en consola por dos personas.
Contexto
Quieres un juego para los descansos que funcione en cualquier computador del aula, sin internet ni instalaciones.
Objetivo
Un programa de consola, organizado en funciones, que dos personas puedan jugar de principio a fin sin que se cierre por errores.
Requisitos funcionales
- Muestra el tablero en cada turno y dice a quién le toca.
- Pide una casilla del 1 al 9 y rechaza entradas no válidas con un mensaje claro.
- Detecta victoria (8 líneas) y empate.
- Al terminar, pregunta si se quiere jugar otra vez.
Restricciones técnicas
- Una función por responsabilidad (dibujar, leer jugada, detectar ganador, detectar empate, cambiar turno).
- Las líneas ganadoras como datos (sección 4), no como 8
if. - Sin variables globales modificadas dentro de funciones (Día 12): pasa el tablero como parámetro.
Planificación
- Comprender el juego (juega una partida en papel). 2. Entradas: el número de casilla. 3. Procesos: el diagrama de la sección 2. 4. Salidas: el tablero y los mensajes. 5. Dividir en funciones. 6. Diseñar el tablero en texto. 7. Implementar por versiones. 8. Probar con la tabla de pruebas. 9. Corregir. 10. Mejorar (retos).
Construcción paso a paso
- Versión 1 — Tablero: crear y dibujar el tablero; colocar una "X" a mano en el código y verla.
- Versión 2 — Turnos: ciclo que alterna X y O y pide casillas (todavía sin validar ni detectar final; el ciclo termina tras 9 jugadas).
- Versión 3 — Validación:
leer_casillay el ciclo que repite la pregunta. - Versión 4 — Final de partida:
ganador,tablero_llenoy los mensajes. - Versión 5 — Revancha y marcador: jugar varias partidas y llevar la cuenta de victorias de cada símbolo.
- Versión final — Pulido: limpiar la pantalla entre turnos (opcional),
README.mdy diario.
Pruebas
Escríbelas con assert en un archivo pruebas.py (se verificaron todas con las funciones de este tutorial):
| # | Tablero (índices 0–8) | Resultado esperado |
|---|---|---|
| 1 | ["X","X","X"," "," "," "," "," "," "] |
ganador → "X" (fila) |
| 2 | ["O"," "," ","O"," "," ","O"," "," "] |
ganador → "O" (columna) |
| 3 | ["X"," "," "," ","X"," "," "," ","X"] |
ganador → "X" (diagonal) |
| 4 | Tablero vacío | ganador → None (¡no " "!) |
| 5 | list("XOXXOOOXX") |
ganador → None y tablero_lleno → True (empate) |
| 6 | leer_casilla("a", t), leer_casilla("10", t), leer_casilla("0", t) |
None con el mensaje adecuado |
| 7 | Casilla ocupada | None, "Esa casilla ya está ocupada." |
assert ganador(["X", "X", "X", " ", " ", " ", " ", " ", " "]) == "X"
assert ganador(crear_tablero()) is None🧪 Prueba y depuración
Este programa tiene cuatro errores. Encuéntralos antes de ejecutarlo:
tablero = [" "] * 9
turno = "X"
while not tablero_lleno(tablero):
casilla = int(input(f"Turno de {turno}: "))
tablero[casilla] = turno
if turno == "X":
turno = "O"
if turno == "O":
turno = "X"
if ganador(tablero):
print(f"Gana {turno}")Pistas: ¿qué pasa si escribo "9"? ¿Cambia realmente el turno? ¿De quién es el turno cuando se anuncia el ganador? ¿El ciclo termina cuando alguien gana?
⭐ Extensión del proyecto
- 🟢 Reto 1 — Aplicación: que cada jugador escriba su nombre y el marcador los muestre.
- 🟡 Reto 2 — Ampliación: modo 1 jugador contra la IA "fácil" o "media" (sección 6), elegido en un menú.
- 🟠 Reto 3 — Desafío: IA perfecta con minimax; mide cuánto tarda la primera jugada con
time.perf_counter()y acelérala con un diccionario de posiciones ya calculadas (memoización). - 🔴 Reto 4 — Pensamiento computacional: generaliza el juego a un tablero de N × N donde gana quien alinea N símbolos. Ya no puedes escribir
LINEASa mano: diseña cómo generarlas con ciclos y pruébalo con N = 3 (deben salir exactamente 8 líneas) y N = 4 (¿cuántas deben salir?).
8. 🌟 Proyecto extra: Conecta 4 en consola
(Proyecto creado para esta adaptación; no existe en el curso original.)
Situación
El club de juegos de mesa del colegio quiere una versión digital de Conecta 4: un tablero de 6 filas × 7 columnas donde las fichas caen hasta la posición libre más baja de la columna elegida. Gana quien alinea 4 fichas en horizontal, vertical o diagonal.
Requisitos
- El jugador elige una columna (1–7), no una casilla.
- La ficha cae a la fila libre más baja; si la columna está llena, se rechaza la jugada.
- Se detectan las victorias en las 4 direcciones y el empate.
Decisiones que debes tomar (y justificar)
- ¿Lista de 42 elementos o lista de listas? (Aquí la comparación de la sección 3 cambia: ¿por qué?)
- ¿Generas todas las líneas de 4 posibles (como
LINEAS) o revisas solo alrededor de la última ficha? ¿Cuál es más eficiente?
Casos de prueba
- Cuatro fichas seguidas en la fila de abajo ganan.
- Una diagonal que sube hacia la derecha gana (y otra que baja, también).
- La 7.ª ficha en una columna se rechaza.
9. 🏗️ Mi Portafolio Profesional — Etapa 3
(Proyecto integrador creado para esta adaptación; etapas 1 y 2 en los Días 82 y 83.)
Etapa 3 (hoy):
- Crea un repositorio para el Tres en línea con commits por versión y un
README.mdque muestre, en un bloque de texto, cómo se ve una partida. - Incluye
pruebas.pyy explica en elREADME.mdcómo ejecutarlo. - Escribe el
DIARIO.mddel Día 84 (la reflexión que pide el curso). - Agrega la ficha del proyecto a tu
proyectos.jsony comprueba que aparece en tu sitio (Día 83) sin modificar el HTML.
Producto de la etapa: el repositorio, la ficha y el sitio actualizado.
10. 🧩 Comprueba lo aprendido
Conceptuales
- ¿Por qué conviene que
dibujardevuelva un texto en lugar de imprimirlo? - ¿Qué ventaja tiene guardar las líneas ganadoras en una lista de tuplas?
- ¿Qué es un caso base en una función recursiva y qué pasa si falta?
- ¿Por qué hay que revisar el ganador antes que el empate?
Análisis de código
for a, b, c in LINEAS[3:6]:
print(a, b, c)¿Qué líneas del tablero imprime y qué representan?
Predicción
t = [[" "] * 3] * 3
t[0][0] = "X"
print(t)¿Qué se imprime? ¿Por qué no es lo que esperabas?
Depuración
Un estudiante dice: "Mi juego anuncia 'Gana ' (con espacios) apenas empieza". ¿Qué le falta a su función ganador?
Modificación
Cambia dibujar para que las casillas vacías muestren su número (1–9) como guía para el jugador.
Creación
Escribe casillas_libres(tablero), que devuelva la lista de números de casilla (1–9) aún disponibles. Úsala en la IA "fácil".
11. 🤖 Programar con apoyo de IA
En un proyecto de portafolio, la IA es tutora, no autora (Día 82).
Ciclo de trabajo: formula el problema → inténtalo tú primero → consulta a la IA sobre un punto concreto → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.
Actividad 1 — Pregunta por piezas. En lugar de "hazme un tic tac toe", pregunta: "¿cómo recorro una lista de tuplas desempaquetando sus elementos en Python?". Compara la respuesta con el tutorial oficial de Python (sección de tuplas y secuencias).
Actividad 2 — Caza de errores. Pide a una IA un ganador(tablero) y aplícale las 7 pruebas de la sección 7. Si falla alguna (por ejemplo, la del tablero vacío), explica por qué y corrígela tú.
Actividad 3 — Comprobar una afirmación. Pregunta a una IA "¿cuántas partidas distintas de tres en línea existen?". Compara su respuesta con una fuente confiable y, si te animas, cuéntalas tú con una función recursiva parecida a minimax.
Declara el uso: agrega al README.md una sección "Uso de IA".
12. 🛠️ Observaciones sobre el material original
| Archivo | Observación | Tratamiento |
|---|---|---|
dia84-tarea3.md |
Remite a una búsqueda de Google para ver el juego y a una imagen alojada en Udemy | Se reemplazan con el tablero en texto de la sección 3 y el diagrama de la sección 2 |
dia84-tarea3.md |
El reto de IA no indica ningún método | Sección 6: tres niveles, de aleatorio a minimax, presentados como alternativas |
dia84-tarea3.md |
Menciona el Blackjack del Día 11 como referencia | Se retoma la estrategia de dividir el juego en funciones |
| Carpeta del Día 84 | Solo contiene el enunciado (sin código ni videos) | Coherente con el mensaje del Día 83: los proyectos de portafolio no traen solución |
13. 📋 Matriz de trazabilidad
| Contenido original | Archivo de origen | Concepto desarrollado | Ejercicio original | Reto adicional | Proyecto relacionado |
|---|---|---|---|---|---|
| Tres en línea de texto, 2 jugadores, X y O, en la línea de comandos | dia84-tarea3.md |
Secciones 2 a 5 | Juego Tic Tac Toe | Retos 1, 2 y 4 de la sección 7 | Secciones 7 y 9 |
Diseño del tablero libre (con | y _) |
dia84-tarea3.md |
Sección 3 | — | Modificación de la sección 10 | Sección 7 |
| Reto opcional: jugador de IA | dia84-tarea3.md |
Sección 6 | Reto de IA | Retos 2 y 3 de la sección 7 | Sección 7 |
| Momento de reflexión | dia84-tarea3.md |
Sección 7 (versión final) | Pregunta de reflexión | — | Sección 9 |
| (No existe en el curso original) | — | Validación, recursión, pruebas | — | Conecta 4 | Sección 8 |
| (No existe en el curso original) | — | — | — | Mi Portafolio Profesional, etapa 3 | Sección 9 |
14. 📦 Evidencias de aprendizaje del Día 84
- El juego funcionando: captura o grabación de una partida con victoria y otra con empate.
pruebas.pycon al menos las 7 pruebas de la sección 7, todas superadas.- El diagrama de flujo (en papel o digital).
- El
DIARIO.mddel Día 84. - El repositorio en GitHub y la ficha en el sitio del portafolio.
- Cuaderno de errores: lista de listas con referencias compartidas, ganador "vacío", empate antes que victoria, índice 1–9 frente a 0–8,
ValueErroral convertir texto, recursión sin caso base.