Convertir errores en una ronda de práctica

PythonBeginner
Practicar Ahora

Introducción

Guarda respuestas estables y convierte únicamente los errores de esta ronda en una práctica independiente. Necesitas los conceptos del cuestionario anterior y funciones, listas, diccionarios, condiciones y bucles de Python. Esta unidad prepara independientemente las tres reglas anteriores: no requiere archivos ni procesos de otra unidad.

Usa WebIDE Terminal → New Terminal (More en ventanas estrechas). Edita solo logic.py; conserva engine.py, quiz.py y bank.json. Tiempo estimado de lectura y práctica: 35 minutos, no medido con principiantes.

Guardar respuestas enviadas como JSON

En este paso guardarás una ronda real sin vincular las respuestas a posiciones de pantalla. El motor preparado devuelve una lista que solo contiene pares question_id/option_id enviados. Controla los comentarios y la puntuación; el registro no declara por sí mismo aciertos ni números de pantalla.

Empieza en la terminal de WebIDE:

cd /home/labex/project

json.dump escribe listas y diccionarios Python como JSON. with open(..., "w", encoding="utf-8") abre un archivo de texto para escribir y lo cierra después. UTF-8 hace portable el texto; ensure_ascii=False mantiene legible Unicode e indent=2 facilita la lectura. stream.write("\n") añade un salto de línea final. La envoltura {"answers": answers} identifica la lista.

El here-document completo conserva las tres reglas preparadas y añade la función de guardado. > sustituye logic.py; <<'PY', entrecomillado, escribe texto literal hasta el último PY. La función de práctica se añade en el próximo paso.

cat > logic.py <<'PY'
import json


def choose_questions(questions, count, rng):
    return rng.sample(questions, min(count, len(questions)))


def move_options(question, rng):
    options = question["options"].copy()
    rng.shuffle(options)
    return options


def read_answer(options, ask, tell):
    while True:
        text = ask("Answer (number or q): ").strip()
        if text.lower() == "q":
            return None
        for position, option in enumerate(options, start=1):
            if text == str(position):
                return option["option_id"]
        tell("Enter one displayed number, or q to stop.")


def save_answers(path, answers):
    with open(path, "w", encoding="utf-8") as stream:
        json.dump({"answers": answers}, stream, ensure_ascii=False, indent=2)
        stream.write("\n")
PY

--ordered mantiene el orden conocido de preguntas y opciones; --output nombra un registro nuevo. Las rutas de salida existentes se rechazan antes de jugar para proteger los registros. Responde dos preguntas: 1 para sky (correcto) y después 1 para tide (incorrecto).

python3 quiz.py bank.json --ordered --count 2 --output round.json
Score: 1/2 answered
Saved round.json

cat muestra el registro real:

cat round.json

Contiene sky/sun y tide/sun. El mismo ID de opción significa algo distinto en cada pregunta. Conserva esta ronda original. Si repites por accidente, el error de salida existente es esperado: utiliza otro nombre para experimentos adicionales, sin sobrescribirla. Si escribes mal el archivo de origen, consulta ls y repite con la ruta correcta; Quiz error: ... no significa que se haya guardado una ronda.

Practicar solo los errores de esta ronda

En este paso recalcularás los errores a partir del ID correcto de cada pregunta. Un diccionario asocia los ID de preguntas respondidas con las opciones elegidas. Incluye una pregunta solo si se respondió y su respuesta difiere de su propio correct_option_id. Excluye tanto las correctas como las no respondidas. La lista conserva objetos de pregunta completos. En {answer["question_id"]: answer["option_id"] for answer in answers}, la parte tras for recorre las respuestas y la parte clave/valor anterior construye cada entrada: es una comprensión de diccionario. En [question for question in questions if ...], for recorre preguntas e if conserva las coincidentes: es una comprensión de lista. and exige ambas condiciones: existe una respuesta y es incorrecta. Así, una pregunta no respondida no se cuenta como error. >> añade sin reemplazar las reglas existentes:

cat >> logic.py <<'PY'
def wrong_questions(questions, answers):
    selected = {answer["question_id"]: answer["option_id"] for answer in answers}
    return [question for question in questions
            if question["question_id"] in selected
            and selected[question["question_id"]] != question["correct_option_id"]]
PY

--practice lee la ronda original; --output escribe un registro nuevo separado. Repite todos y solo los errores de esa ronda, no un historial acumulado. Ejecuta:

python3 quiz.py bank.json --practice round.json --output retry.json

Solo aparece tide. Selecciona el número visible de Moon, cuya posición puede cambiar. El resultado es Score: 1/1 answered. Inspecciona ambos registros independientes:

cat round.json retry.json

El original sigue teniendo tide/sun; la práctica tiene tide/moon. El banco no cambia. Ahora crea una ronda totalmente correcta. La cantidad es positiva y se limita mediante min(count, len(questions)); 100 pregunta las tres una sola vez.

python3 quiz.py bank.json --ordered --count 100 --output perfect.json

Introduce 1, 2, 2 para sky, tide y roots. Después practica esa ronda:

python3 quiz.py bank.json --practice perfect.json --output empty.json
No mistakes to practice.
Score: 0/0 answered
Saved empty.json

Un conjunto vacío de errores es válido. Por último, inicia otra ronda e introduce q antes de responder:

python3 quiz.py bank.json --output stopped.json

La puntuación es 0/0 y stopped.json tiene una lista answers vacía. No debe guardar como error la pregunta abandonada. Conserva los cinco registros. Si la edición causó un error de sintaxis, repite el bloque completo del paso; como el paso 1 reemplaza el archivo, vuelve a ejecutar después el bloque de adición del paso 2. Conserva los registros existentes y utiliza nombres nuevos para más partidas.

Resumen

Has guardado pares de ID realmente enviados, conservado la ronda original y creado práctica independiente de errores, incluidas rondas vacías y detenidas válidas.