Separa los juegos adecuados de los similares

PythonBeginner
Practicar Ahora

Introducción

Una pequeña biblioteca de juegos necesita alternativas para esta noche. Un juego puede parecerse a tu favorito y, aun así, no estar disponible o no admitir al grupo. En este laboratorio separarás dos preguntas: qué juegos se pueden usar y cómo comparar sus características numéricas.

Crearás un programa de idoneidad reproducible y un resultado JSON real. Necesitas funciones y diccionarios básicos de Python, arrays introductorios de NumPy y lectura de tablas. Conocer la clasificación por vecinos más próximos ayuda, pero aquí no hay etiquetas que predecir. Python y NumPy están preparados; no necesitas scikit-learn. Los 45 minutos son una estimación del autor que incluye lectura y correcciones habituales.

Decide qué juegos se pueden usar esta noche

En este paso examinarás el catálogo real de la biblioteca e implementarás las reglas de idoneidad de esta noche en una región de una función preparada.

Abre la pestaña Terminal. Todos los comandos se ejecutan allí, incluido el lector de archivos guardados. Abrir o reconectar Terminal no reinstala ni reinicia el espacio de trabajo. cd cambia el directorio actual de este terminal; otro terminal tiene su propio directorio.

cd /home/labex/project/board-games

Empieza por la entrada real. CSV tiene una cabecera seguida de filas separadas por comas. cat imprime el archivo sin modificarlo. Este es el catálogo completo, sin filtrar, no una lista de juegos que ya cumplen las condiciones de esta noche.

cat catalog.csv

Cada fila representa una edición distinta. Su game_id único y no vacío es la clave usada para encontrar y relacionar esa edición. title es texto de presentación: varias ediciones de Harbor tienen ID distintos y deben permanecer separadas. minutes es una estimación positiva de duración; complexity es una valoración sintética de 1 a 5, donde los valores mayores significan mayor complejidad. Los enteros min_players y max_players incluyen ambos extremos. La entrada sintética está completa y tiene una fila por ID; limpiar datos malformados queda fuera del ejercicio.

cat availability.csv

La tabla separada de préstamos tiene una fila por cada ID del catálogo, en otro orden. Su texto CSV true/false se convierte en booleanos Python True/False en el cargador preparado. Relacionar mediante una clave significa encontrar el mismo game_id, no unir la fila 1 con la fila 1. Las claves únicas y una fila de estado de préstamo por ID evitan relaciones ambiguas o ausentes. Implementarás esa búsqueda.

JSON guarda valores con nombre, listas y objetos. Lee la solicitud de esta noche:

cat night.json

reference_id es el ejemplo al que parecerse, players el tamaño del grupo y k la cantidad solicitada de alternativas próximas, que se usa en el siguiente laboratorio. La referencia es una consulta de ejemplo y puede no estar disponible o ser inadecuada; solo las alternativas devueltas necesitan cumplir las condiciones de esta noche. Excluye solo su ID exacto, no todos los juegos con su título o características idénticas. La vista general de las entradas originales no ha aplicado estas reglas ni generado un resultado de idoneidad.

Considera tres juegos disponibles que admiten cuatro jugadores: Harbor dura 60 minutos y tiene complejidad 3, Quay dura 61 con complejidad 5 y Bridge dura 90 con complejidad 3. Las diferencias en unidades originales son candidato menos referencia: Quay dura un minuto más y tiene dos puntos más de complejidad; Bridge dura treinta minutos más y tiene igual complejidad. La opción -c de Python ejecuta el cálculo entre comillas como un solo argumento. El punto y coma separa dos instrucciones Python.

python3 -c "print('Quay minutes:', 61 - 60); print('Bridge minutes:', 90 - 60)"

Obtendrás 1 y 30. La distancia euclídea sin escalar eleva al cuadrado las diferencias de características, las suma y calcula la raíz cuadrada. Treinta minutos pueden dominar dos puntos de complejidad si se mezclan directamente esas unidades. Definirás una escala común a continuación; primero decide quién puede participar.

Puedes editar suitability.py, ranking.py, tus archivos de resultados y tus propias copias de los scripts. Conserva todas las entradas CSV/JSON suministradas, planner.py, coursekit.py, show_results.py y las dos copias de recuperación starter-*.py. planner.py selecciona una función pública y delega la lectura de archivos, conversión de tipos y escritura JSON a coursekit.py; ninguno toma la decisión de negocio. Deja ranking.py sin completar en este laboratorio.

Lee el módulo preparado:

cat suitability.py

choose_games(catalog, availability, request) recibe las listas cargadas de diccionarios y el diccionario de solicitud. Sus dos regiones etiquetadas empiezan por eligible = [] y scale = {}. Son marcadores incompletos, no una afirmación de que ningún juego cumpla las condiciones. El retorno final empaqueta los ID y la escala para el ejecutor. Sustituirás ahora la región de idoneidad y conservarás el marcador de escala para el paso 2. La preparación y la vista general de entradas no crean ningún resultado evaluable.

Haz una copia de este módulo. cp SOURCE DESTINATION copia solo el archivo indicado; .bak es un sufijo de copia de seguridad. No sobrescribas esta copia después de cometer un error.

cp suitability.py suitability.py.bak

nano edita un archivo existente dentro de Terminal. LC_ALL=C.UTF-8, aplicado solo a este comando, selecciona UTF-8 únicamente para este editor, sin cambiar la configuración global del shell. En la franja de atajos de nano, ^ significa mantener Ctrl. Las flechas mueven el cursor; escribir inserta texto. Ctrl+W, una frase de búsqueda y Enter localizan texto. Home va al principio de la línea; Ctrl+K corta toda esa línea. Python usa sangría: las líneas siguientes empiezan con cuatro espacios dentro de la función y más sangría dentro del bucle y la condición.

LC_ALL=C.UTF-8 nano suitability.py

Practica primero descartar una edición no deseada. Busca ## Candidate identities con Ctrl+W y Enter, pulsa End y añade temporary a ese comentario. Pulsa Ctrl+X y después N en la pregunta de guardado. Sales sin cambiar el archivo. Vuelve a abrirlo:

LC_ALL=C.UTF-8 nano suitability.py

Busca eligible = [], pulsa Home y después Ctrl+K una vez para quitar solo esa línea provisional. Conserva la cabecera de la función, la región de escala y el retorno final. Inserta allí las siguientes líneas con sangría. La comprensión de diccionario construye una correspondencia de ID a disponibilidad. El bucle utiliza and para tres requisitos independientes; las comparaciones encadenadas <= incluyen ambos límites de jugadores. append añade el ID coincidente, no el título visible.

    status = {row['game_id']: row['available'] for row in availability}
    eligible = []
    for game in catalog:
        different_id = game['game_id'] != request['reference_id']
        fits_group = game['min_players'] <= request['players'] <= game['max_players']
        if different_id and fits_group and status[game['game_id']]:
            eligible.append(game['game_id'])

Guarda con Ctrl+O, pulsa Enter para confirmar el nombre sin cambios y después Ctrl+X para salir. Guardar no ejecuta la función. Su retorno existente usa sorted(eligible) para producir una lista alfabética de identidades independiente del orden de las filas de entrada y deja scale como objeto vacío.

La opción --mode suitable del ejecutor selecciona choose_games; --output suitable.json, obligatorio, nombra el resultado guardado. Por defecto se usan catalog.csv, availability.csv y night.json de este directorio. --catalog, --availability y --request permiten seleccionar otras rutas. Estas entradas siguen siendo de solo lectura.

python3 planner.py --mode suitable --output suitable.json

El lector suministrado abre solo ese JSON guardado. No genera un archivo ausente ni calcula nada desde el catálogo.

python3 show_results.py suitable.json

Debes ver Scale: {} y los ID left, near, raw, right, same, zero. Tanto same como zero son otras ediciones de Harbor; zero incluso tiene características numéricas idénticas a la referencia. blocked no está disponible, small no admite cuatro jugadores y ref es la referencia exacta. Compara estas identidades con ambas tablas de entrada. La idoneidad ya está completa, pero el escalado aún no se ha implementado deliberadamente.

El siguiente fallo de directorio incorrecto es intencional. Sube un directorio:

cd /home/labex/project
python3 planner.py --mode suitable --output suitable.json

Python informa de can't open file y No such file or directory: no encuentra aquí el ejecutor. No se han reiniciado tu programa ni el resultado existentes. pwd muestra el directorio de este terminal; vuelve al espacio de trabajo y repite el comando real.

pwd
cd /home/labex/project/board-games
python3 planner.py --mode suitable --output suitable.json
python3 show_results.py suitable.json

Para un error de edición, lee el archivo y número de línea indicados por Python, vuelve a abrir el módulo en nano y corrige solo la región afectada. SyntaxError suele indicar que falta un signo de dos puntos o una comilla; IndentationError significa que debes revisar los espacios iniciales. Guarda, sal y vuelve a ejecutar. Ctrl+X, N abandona los cambios sin guardar. Si es necesario, cp suitability.py.bak suitability.py restaura solo esta base incompleta, así que debes volver a implementar después la región de idoneidad. Ni una copia de seguridad ni el starter-suitability.py protegido son tu solución completa. No vuelvas a ejecutar setup ni cambies entradas para recuperarte.

En este paso implementarás la región de escala conservando la lógica de idoneidad que ya completaste.

Continúa en la pestaña Terminal. Indica el directorio explícitamente para que también funcione en un terminal nuevo.

cd /home/labex/project/board-games

Una característica es una propiedad numérica usada en la comparación. Su orden siempre es minutes, después complexity; los límites de jugadores y la disponibilidad determinan idoneidad, no distancia. Para cada característica busca mínimo y máximo en todo el catálogo, incluida la referencia y los juegos no disponibles o inadecuados. El rango es máximo menos mínimo. MinMax transforma x en (x - minimum) / range: minutos y puntos de complejidad pasan a representar fracciones de sus respectivos rangos.

Los mínimos completos son [10, 1] y los rangos [100, 4]. Las diferencias de Quay se convierten en [1/100, 2/4]; las de Bridge, en [30/100, 0/4]. La distancia escalada de Quay ronda 0.5001 y la de Bridge es 0.3. La métrica cambia la clasificación, pero ninguna unidad revela gustos personales. Dar el mismo peso a ambas características escaladas es una decisión explícita del ejercicio.

Este catálogo es la referencia para definir la métrica, no un conjunto de entrenamiento supervisado. No hay etiqueta objetivo ni división entrenamiento/prueba. Cambiar solo la disponibilidad no debe cambiar la escala ni las distancias de juegos que siguen siendo elegibles. Recalcular parámetros tras filtrar movería la regla cada vez que se presta un juego.

Si una característica es constante, su rango es cero. Guarda el rango real 0, pero usa divisor 1 en el cálculo posterior. Como la referencia pertenece al catálogo, todas las diferencias de esa característica son cero y no aportan distancia. Así evitas dividir por cero sin inventar información.

Conserva una copia nueva de la fase de idoneidad que ya funciona, dejando intacta la copia original de la base:

cp suitability.py suitability-ids.py.bak
LC_ALL=C.UTF-8 nano suitability.py

Busca scale = {} con Ctrl+W y Enter, pulsa Home y corta solo esa línea provisional con Ctrl+K. Conserva sin cambios la búsqueda de ID y el bucle completos que están encima. Inserta las cuatro líneas con sangría siguientes. np.array construye una tabla numérica de dos columnas en el orden de características publicado; axis=0 encuentra mínimos y máximos por columna. tolist() convierte arrays en listas normales que JSON puede guardar.

    features = np.array([[game['minutes'], game['complexity']] for game in catalog])
    minimum = features.min(axis=0)
    ranges = features.max(axis=0) - minimum
    scale = {'min': minimum.tolist(), 'range': ranges.tolist()}

El retorno existente incluye ahora este scale calculado y los mismos ID ordenados. Guarda con Ctrl+O, Enter y después Ctrl+X. Has añadido una región en lugar de sustituir el trabajo anterior o añadir otro programa al final.

Se guardan números, no cadenas formateadas. El resultado contiene cada ID elegible una vez y en orden alfabético. scale.min y scale.range tienen dos números en el orden indicado. Se permiten campos explicativos adicionales inocuos. Las tolerancias absoluta y relativa de evaluación son 1e-8: admiten diferencias habituales de coma flotante, no otra métrica.

Regenera el resultado real a partir de las entradas originales, reemplazando solo tu archivo de salida.

python3 planner.py --mode suitable --output suitable.json
python3 show_results.py suitable.json

Debes ver mínimos 10 y 1, rangos 100 y 4 y los mismos seis ID elegibles. El lector puede mostrar 10.0 en lugar de 10; ambos representan el número diez. Examina directamente la estructura guardada:

cat suitable.json

Si una edición daña la región de idoneidad, cp suitability-ids.py.bak suitability.py restaura solo esa fase que ya funciona. Vuelve a abrir nano e implementa otra vez la región de escala. En cambio, cp starter-suitability.py suitability.py restaura la base incompleta y pierde ambas regiones; úsalo solo si quieres empezar de nuevo deliberadamente. Las correcciones habituales deben reparar las líneas relevantes, guardar, salir y volver a ejecutar. Conserva ambos archivos starter suministrados y todas las entradas. Reconectar Terminal nunca ejecuta setup ni restaura una copia por ti.

Autocomprobación: la idoneidad de esta noche puede cambiar sin que cambie esta regla del catálogo completo. En el siguiente laboratorio independiente se suministra la función de idoneidad completa para que puedas centrarte en distancias y selección.

Resumen

Separaste la idoneidad para esta noche de la similitud de contenido, relacionaste registros independientes mediante identificadores estables, conservaste alternativas legítimas con el mismo título o diferencias nulas y calculaste una escala MinMax fija sobre el catálogo completo. suitable.json es un resultado real que puedes regenerar. El siguiente laboratorio usa estos conocimientos para medir y explicar candidatos en su propio entorno independiente.