Skip to content
Merged

PRO1 #20

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions 1r/Q1/PRO1/simulador-parcials/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@

# Binarios compilados en make grade (quedan dentro de cada carpeta de intento).
intentos/*/*.bin

# Estado de la sesion interactiva (cronometro: start/end time). Es runtime,
# no forma parte del intento en si (el .cpp del usuario si se conserva).
intentos/*/.estado
20 changes: 13 additions & 7 deletions 1r/Q1/PRO1/simulador-parcials/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ all: start
help:
@echo "Simulador de parciales de PRO1 (no oficial)"
@echo ""
@echo " make / make start Descarga (si falta) la cache de parciales,"
@echo " elige uno al azar, muestra sus enunciados,"
@echo " crea un intento nuevo en $(INTENTOS_DIR)/ y"
@echo " arranca el cronometro (EXAM_MINUTES=$(EXAM_MINUTES))."
@echo " make grade Compila y ejecuta cada .cpp del ultimo intento"
@echo " (o del indicado con ATTEMPT=<ruta>) y muestra"
@echo " la solucion de referencia para comparar a ojo."
@echo " make / make start Descarga (si falta) la cache de parciales y"
@echo " arranca una sesion de examen en MODO REAL:"
@echo " disclaimer, confirmacion, parcial al azar,"
@echo " intento nuevo en $(INTENTOS_DIR)/ y una sesion"
@echo " interactiva (examshell>) con cronometro real"
@echo " (EXAM_MINUTES=$(EXAM_MINUTES)). Ver 'help' dentro"
@echo " de la sesion para los comandos (status, grademe,"
@echo " enunciado, finish...)."
@echo " make grade Atajo fuera de sesion: compila y ejecuta cada"
@echo " .cpp del ultimo intento (o del indicado con"
@echo " ATTEMPT=<ruta>) y muestra la solucion de"
@echo " referencia para comparar a ojo. Util para"
@echo " repasar despues de terminar la sesion."
@echo " NO es correccion automatica pass/fail."
@echo " make clean Borra la cache descargada y los binarios"
@echo " compilados. NO toca $(INTENTOS_DIR)/ (tu trabajo)."
Expand Down
61 changes: 44 additions & 17 deletions 1r/Q1/PRO1/simulador-parcials/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ en el repo original.
## Uso

```bash
make # = make start: descarga cache si falta, elige parcial al azar,
# muestra enunciados, crea intento nuevo, arranca cronómetro
make grade # compila y ejecuta (interactivo) el último intento, y muestra
# la solución de referencia de cada problema para comparar
make # = make start: arranca una sesión de examen en MODO REAL
make grade # atajo FUERA de sesión: corrige el último intento (ver abajo)
make clean # borra cache y binarios compilados (NO toca intentos/)
make help # lista de comandos
```
Expand All @@ -54,17 +52,45 @@ Variables:
- `ATTEMPT` — ruta del intento a corregir con `make grade` (por defecto, el
más reciente en `intentos/`).

Al ejecutar `make`/`make start`:
1. Si no hay cache local de los 7 parciales, los descarga.
2. Elige uno al azar entre los disponibles en cache.
3. Muestra de golpe los enunciados de todos sus problemas (como el examen
real, no de uno en uno).
4. Crea `intentos/<timestamp>-<parcial>/` con un `.cpp` vacío por problema
(cabecera ASCII estándar de 42 ya puesta, nombrado `<CODIGO>.cpp`).
5. Arranca el cronómetro en terminal. Ctrl+C para pararlo cuando acabes (o
déjalo llegar a 0).

Al ejecutar `make grade`:
### `make` / `make start`: sesión en modo REAL

Inspirado en el modo "real" de Maestro42 (examshell de 42): no es un script
de un solo disparo, es una **sesión interactiva persistente** con cronómetro
real, igual de fiel al examen oficial como sea posible:

1. Si no hay cache local de los 7 parciales, la descarga.
2. Disclaimer: qué es esto, qué no es, qué comandos hay disponibles durante
la sesión. Pide confirmación (`y/n`) antes de arrancar nada.
3. Animación de "conexión" (puramente estética).
4. Elige un parcial al azar entre los disponibles en cache, crea
`intentos/<timestamp>-<parcial>/` con un `.cpp` vacío por problema
(cabecera ASCII estándar de 42 ya puesta, nombrado `<CODIGO>.cpp`), y
pide pulsar una tecla para comenzar. **El cronómetro arranca de verdad en
ese momento**, no antes.
5. Muestra de golpe los enunciados de todos los problemas (como el examen
real, no de uno en uno) y entra en el prompt interactivo `examshell>`,
que se queda esperando comandos hasta que termines o se acabe el tiempo:

| Comando | Qué hace |
|---|---|
| `help` | Lista de comandos. |
| `status` | Tiempo restante real, parcial actual y estado de compilación de cada problema. |
| `enunciado` (alias `subject`) | Vuelve a imprimir los enunciados completos. |
| `grademe` | Compila, ejecuta (interactivo) y compara cada `.cpp` con la solución de referencia — ver detalle abajo. |
| `finish` | Pide confirmación (`yes`) y muestra el resumen final (tiempo usado, estado de cada problema) antes de salir. |

Si el tiempo ya se acabó cuando ejecutas cualquier comando, la sesión lo
avisa ("SE ACABÓ EL TIEMPO") y fuerza el cierre con resumen, no deja
seguir trabajando como si nada. Ctrl+C / Ctrl+D también cierran la
sesión de forma limpia (resumen incluido), sin pedir confirmación.

### `grademe` (dentro de sesión) / `make grade` (fuera de sesión)

Misma lógica de corrección en los dos casos — `grademe` es simplemente el
comando interno para invocarla sin salir de la sesión; `make grade` sigue
disponible como atajo standalone para repasar después de que la sesión ya
haya terminado:

- Compila cada `.cpp` del intento con los mismos flags que el resto del repo
(`clang++ -Wall -Wextra -fsanitize=address,undefined -O0`).
- Ejecuta tu binario de forma **interactiva**: tú introduces tu propia
Expand All @@ -83,8 +109,9 @@ simulador-parcials/
├── scripts/
│ ├── common.sh # config compartida (lista de parciales, slug())
│ ├── fetch_examenes.sh # descarga y separa enunciado/solución por problema
│ ├── iniciar_examen.sh # elige parcial, muestra enunciados, crea intento, cronómetro
│ ├── grade.sh # compila, ejecuta y muestra solución de referencia
│ ├── iniciar_examen.sh # disclaimer, confirmación, elige parcial, crea intento, arranca cronómetro
│ ├── sesion_examen.sh # bucle interactivo examshell> (status/enunciado/grademe/finish)
│ ├── grade.sh # compila, ejecuta y muestra solución de referencia (grademe / make grade)
│ └── header_template.cpp # plantilla de la cabecera 42 (placeholders de nombre/fecha)
├── .cache/ # (generado, gitignored) enunciados y soluciones descargados
└── intentos/ # (generado, SÍ trackeado) tus .cpp de cada intento
Expand Down
115 changes: 72 additions & 43 deletions 1r/Q1/PRO1/simulador-parcials/scripts/iniciar_examen.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/usr/bin/env bash
# Elige un parcial al azar de los que hay en cache, muestra los enunciados de
# todos sus problemas de golpe (como el examen real), crea una carpeta de
# intento con un .cpp vacio por problema (cabecera 42 incluida) y arranca un
# cronometro visible en terminal.
set -euo pipefail
# Arranca una sesion de examen en modo REAL: disclaimer, confirmacion,
# animacion de "conexion", seleccion de parcial al azar, creacion del
# intento (.cpp con cabecera 42 por problema) y entrada en el bucle
# interactivo de comandos (examshell>). El bucle en si vive en
# scripts/sesion_examen.sh.
set -uo pipefail

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/common.sh"
Expand All @@ -12,7 +13,37 @@ CACHE_DIR="${1:-.cache}"
INTENTOS_DIR="${2:-intentos}"
EXAM_MINUTES="${3:-90}"

# Solo se puede elegir entre los parciales que ya tengan cache poblada.
cat <<'EOF'
==================================================================
SIMULADOR DE PARCIALES DE PRO1 -- MODO REAL (no oficial)
==================================================================

Herramienta personal, NO oficial de la FIB/UPC, inspirada en el modo
"real" de Maestro42: en cuanto confirmes, el tiempo empieza a contar
de verdad y entraras en una sesion interactiva (examshell>) con estos
comandos disponibles durante el examen:

help Lista de comandos.
status Tiempo restante, parcial actual y estado de cada problema.
enunciado Vuelve a mostrar los enunciados completos del parcial.
grademe Compila, ejecuta y compara con la solucion de referencia.
finish Termina la sesion (pide confirmacion) y muestra el resumen.

No hay pass/fail automatico (PRO1 no tiene jutge con casos de test
para estos examenes): 'grademe' solo ayuda a la autocorreccion manual.
==================================================================
EOF
echo
read -e -p "¿Empezar un parcial aleatorio en modo REAL ahora? (y/n) " confirm
case "$confirm" in
y|Y|yes|YES) ;;
*)
echo "Cancelado."
exit 0
;;
esac

# --- seleccion de parcial: solo entre los que ya tengan cache poblada ---
disponibles=()
for parcial in "${PARCIALES[@]}"; do
pslug=$(slug "$parcial")
Expand All @@ -31,28 +62,25 @@ parcial="${disponibles[$idx]}"
pslug=$(slug "$parcial")
pdir="$CACHE_DIR/$pslug"

echo "=================================================================="
echo " PARCIAL SELECCIONADO: $parcial"
echo "=================================================================="
# --- animacion de "conexion" (puramente estetica, no afecta al cronometro) ---
echo

problemas=("$pdir"/*/)
for probdir in "${problemas[@]}"; do
code=$(basename "$probdir")
echo "------------------------------------------------------------------"
echo " Problema $code"
echo "------------------------------------------------------------------"
if [ -s "$probdir/enunciado.txt" ]; then
cat "$probdir/enunciado.txt"
else
echo "(Este fichero fuente no trae un enunciado capturable como"
echo " comentario inicial; revisa el .cc original en el repo de"
echo " referencia si necesitas el enunciado completo.)"
fi
type_out() {
local text="$1" c
while IFS= read -r -n1 c; do
printf '%s' "$c"
sleep 0.01
done <<< "$text"
echo
done
}
type_out "Conectando con examshell..."
sleep 0.2
type_out "Autenticando a agarcia2@pro1-simulador..."
sleep 0.2
type_out "Sesion establecida."
echo

# Crea la carpeta de intento con un .cpp vacio por problema.
# --- creacion del intento: un .cpp con cabecera 42 por problema ---
problemas=("$pdir"/*/)
timestamp=$(date +%Y%m%d-%H%M%S)
intento_dir="$INTENTOS_DIR/${timestamp}-${pslug}"
mkdir -p "$intento_dir"
Expand All @@ -67,34 +95,35 @@ for probdir in "${problemas[@]}"; do
# barras, y con '/' como delimitador el sed de BSD (macOS) rompe.
sed -e "s#XXXXXXXXXX#${filename}#" -e "s#YYYY/MM/DD HH:MM:SS#${fecha}#g" \
"$DIR/header_template.cpp" > "$dest"
cat >> "$dest" <<'EOF'
cat >> "$dest" <<'CPPEOF'

#include <iostream>

int main(void)
{
return (0);
}
EOF
CPPEOF
done

echo "=================================================================="
echo " Vas a empezar el parcial: $parcial"
echo " Modo REAL: tendras $EXAM_MINUTES minutos en cuanto pulses una tecla."
echo " Intento creado en: $intento_dir"
echo " Tienes $EXAM_MINUTES minutos. Cronometro en marcha."
echo " Cuando acabes (o se acabe el tiempo): Ctrl+C y luego 'make grade'."
echo "=================================================================="
read -e -n1 -p "Pulsa una tecla para comenzar... " tecla
echo
echo

total_seg=$((EXAM_MINUTES * 60))
inicio=$(date +%s)
while :; do
ahora=$(date +%s)
transcurrido=$((ahora - inicio))
restante=$((total_seg - transcurrido))
if [ "$restante" -le 0 ]; then
printf "\r¡TIEMPO AGOTADO! \n"
break
fi
printf "\rTiempo restante: %02d:%02d " $((restante / 60)) $((restante % 60))
sleep 1
done
# --- arranque real del cronometro: a partir de aqui el tiempo cuenta ---
start_time=$(date +%s)
end_time=$((start_time + EXAM_MINUTES * 60))

cat > "$intento_dir/.estado" <<EOF2
PARCIAL="$parcial"
PARCIAL_SLUG="$pslug"
START_TIME=$start_time
END_TIME=$end_time
EXAM_MINUTES=$EXAM_MINUTES
EOF2

exec bash "$DIR/sesion_examen.sh" "$CACHE_DIR" "$INTENTOS_DIR" "$intento_dir"
Loading
Loading