Generating must never block the screen or record a plan that breaks a rule. The controller refuses before launching, in a fixed order: an out-of-range setting named by the engine's own check, the session's refusals, then what makes the run useless. It runs the search in a worker or in the thread, records only propositions whose invariants hold, skips plans already present, and keeps the last duration for the session only. The evaluation table ranks propositions on the minimum of encounters and names the reservations a proposition does not honour. Checked: 2112 node and 60 node-long tests; coverage floors kept. --- FR --- [ADD] application : génération, travailleur, table d'évaluation Générer ne doit jamais bloquer l'écran ni enregistrer un plan qui enfreint une règle. Le contrôleur refuse avant tout lancement, dans un ordre fixe : un réglage hors de son domaine, nommé par la règle même du moteur, les refus de la séance, puis ce qui rend le calcul vain. Il lance la recherche dans un travailleur ou dans le fil, n'enregistre que les propositions dont les invariants tiennent, écarte les plans déjà présents, et garde la dernière durée pour la seule séance. La table d'évaluation classe les propositions sur le minimum de rencontres et nomme les réservations qu'une proposition ne tient pas. Vérifié : 2112 node et 60 node-long ; seuils de couverture tenus. Assisted-by: Claude Opus 5.5
154 lines
7.8 KiB
JavaScript
154 lines
7.8 KiB
JavaScript
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
// Les textes des codes du moteur qui n'ont pas leur table ailleurs, et les
|
|
// libellés de ses critères (§ 5, § 14.6) : les codes d'une
|
|
// ErreurConfiguration, ceux des violations de verifierInvariants et de
|
|
// verifierIndicateurs, et moteur.critere.<nom> pour chaque nom de CRITERES.
|
|
// Un code garde un seul texte : CAPACITE et TOURS, que le moteur lève sous
|
|
// deux formes de détails — ErreurConfiguration et violation —, se
|
|
// distinguent par les détails qu'ils portent. Une réservation que le moteur
|
|
// désigne sans identifiant l'est par son rang à partir de 0, que le texte
|
|
// affiche à partir de 1 ; une personne qu'une violation d'indicateur désigne
|
|
// par son index se nomme par participant quand l'appelant l'a traduit, par
|
|
// son rang sinon. INCONNU, aux mêmes détails que la faute de placement du
|
|
// stockage, a son texte dans libelles/stockage.js.
|
|
import { capitale, compte, nomDans } from './aides.js';
|
|
|
|
// Les cas d'une table que le plan ne rattache pas à l'instance, par detail.
|
|
const CAS_DE_TABLE = new Map([
|
|
['inconnue', (table) => `Le plan déclare la table d'identifiant ${table}, que l'événement n'a pas.`],
|
|
['doublon', (table) => `Le plan déclare plus d'une fois la table d'identifiant ${table}.`],
|
|
['absente', (table) => `Le plan ne déclare pas la table d'identifiant ${table}.`],
|
|
]);
|
|
|
|
// La personne d'une violation d'indicateur : son numéro, ou son rang dans
|
|
// la population quand l'appelant ne l'a pas traduit.
|
|
const personneDe = ({ index, participant }) =>
|
|
participant === undefined || participant === null ? `la personne de rang ${index + 1}` : `la personne n° ${participant}`;
|
|
|
|
// Un rang de réservation, compté à partir de 0 par le moteur.
|
|
const reservationDe = (rang) => `la réservation n° ${rang + 1}`;
|
|
|
|
export const TABLE = new Map([
|
|
// Une salle trop petite : avertissement d'un geste qui fait croître le
|
|
// manque (A2), refus de la génération (A6) ; un seul texte pour les deux.
|
|
[
|
|
'PLACES_MANQUANTES',
|
|
({ placesManquantes }) =>
|
|
`Il manque ${compte(placesManquantes, 'place', 'places')} à chaque tour pour asseoir toutes les personnes : ` +
|
|
'ajouter des tables ou des sièges, ou exclure des personnes, rend la génération possible.',
|
|
],
|
|
[
|
|
'SURRESERVATION',
|
|
({ table, tour, reservees, capacite }) =>
|
|
`La table d'identifiant ${table} reçoit ${compte(reservees, 'personne réservée', 'personnes réservées')} ` +
|
|
`au tour ${tour}, pour ${compte(capacite, 'place', 'places')}.`,
|
|
],
|
|
|
|
// --- La configuration (normaliser, § 4, § 5.2) ------------------------------
|
|
// CAPACITE {table, capacite} : une capacité refusée ; {table, tour} : une
|
|
// liste du plan plus longue que sa table.
|
|
[
|
|
'CAPACITE',
|
|
({ table, capacite, tour }) =>
|
|
capacite === undefined
|
|
? `Au tour ${tour}, la table d'identifiant ${table} reçoit plus de personnes qu'elle n'a de places.`
|
|
: `La table d'identifiant ${table} a une capacité de ${capacite} : une table compte au moins 2 places.`,
|
|
],
|
|
// TOURS {tours} : un nombre de tours refusé ; {} : un plan qui n'a pas un
|
|
// tour par tour de l'événement.
|
|
[
|
|
'TOURS',
|
|
({ tours }) =>
|
|
tours === undefined
|
|
? "Le plan ne porte pas autant de tours que l'événement."
|
|
: `Le nombre de tours, ${tours}, n'est pas un entier d'au moins 1.`,
|
|
],
|
|
['PARTICIPANT_DOUBLON', ({ participant }) => `La personne n° ${participant} figure plus d'une fois dans la liste.`],
|
|
['TABLE_DOUBLON', ({ table }) => `La table d'identifiant ${table} figure plus d'une fois.`],
|
|
[
|
|
'RESERVATION_INCONNUE',
|
|
({ reservation, participant, table }) =>
|
|
participant === undefined
|
|
? `${capitale(reservationDe(reservation))} désigne la table d'identifiant ${table}, que l'événement n'a pas.`
|
|
: `${capitale(reservationDe(reservation))} désigne la personne n° ${participant}, que la liste n'a pas.`,
|
|
],
|
|
[
|
|
'RESERVATION_TOUR',
|
|
({ reservation, tour }) => `${capitale(reservationDe(reservation))} désigne le tour ${tour}, que l'événement n'a pas.`,
|
|
],
|
|
[
|
|
'RESERVATION_CONFLIT',
|
|
({ participant, tour, tables }) =>
|
|
`La personne n° ${participant} est réservée au tour ${tour} à deux tables, ` +
|
|
`d'identifiants ${tables[0]} et ${tables[1]}.`,
|
|
],
|
|
[
|
|
'ENUMERATION_TROP_GRANDE',
|
|
({ participant, limite }) =>
|
|
`Le plafond a priori de la personne n° ${participant} demanderait d'énumérer plus de ${limite} itinéraires.`,
|
|
],
|
|
|
|
// --- Le plan confronté à la configuration (indexerPlan) -------------------------
|
|
[
|
|
'PLAN_TOURS',
|
|
({ attendu, tours, reserves }) =>
|
|
`Le plan porte ${compte(tours, 'tour', 'tours')} et ${compte(reserves, 'réserve', 'réserves')}, ` +
|
|
`pour ${compte(attendu, 'tour', 'tours')} dans l'événement.`,
|
|
],
|
|
['PLAN_TABLE_INCONNUE', ({ table }) => `Le plan déclare la table d'identifiant ${table}, que l'événement n'a pas.`],
|
|
['PLAN_TABLE_DOUBLON', ({ table }) => `Le plan déclare plus d'une fois la table d'identifiant ${table}.`],
|
|
['PLAN_TABLE_ABSENTE', ({ table }) => `Le plan ne déclare pas la table d'identifiant ${table}.`],
|
|
[
|
|
'PLAN_LISTES',
|
|
({ tour, listes, tables }) =>
|
|
`Au tour ${tour}, le plan porte ${compte(listes, 'liste', 'listes')} pour ${compte(tables, 'table', 'tables')}.`,
|
|
],
|
|
['PLAN_INCONNU', ({ participant, tour }) => `Au tour ${tour}, le plan place la personne n° ${participant}, que la liste n'a pas.`],
|
|
['PLAN_EXCLU_PLACE', ({ participant, tour }) => `Au tour ${tour}, le plan place la personne n° ${participant}, qui est exclue.`],
|
|
['PLAN_DOUBLE_PLACE', ({ participant, tour }) => `Au tour ${tour}, le plan place plus d'une fois la personne n° ${participant}.`],
|
|
['PLAN_NON_ASSIS', ({ participant, tour }) => `Au tour ${tour}, le plan ne place nulle part la personne n° ${participant}.`],
|
|
|
|
// --- Les violations de verifierInvariants (§ 14.12) -----------------------------
|
|
[
|
|
'TABLE_INCONNUE',
|
|
({ table, tour, detail }) =>
|
|
detail === 'listes'
|
|
? `Au tour ${tour}, le plan n'a pas une liste par table déclarée.`
|
|
: nomDans(CAS_DE_TABLE, detail, 'cas de table')(table),
|
|
],
|
|
['DOUBLE_PLACE', ({ participant, tour }) => `Au tour ${tour}, la personne n° ${participant} est placée plus d'une fois.`],
|
|
['EXCLU_PLACE', ({ participant, tour }) => `Au tour ${tour}, la personne n° ${participant} est placée alors qu'elle est exclue.`],
|
|
['NON_ASSIS', ({ participant, tour }) => `Au tour ${tour}, la personne n° ${participant} n'est assise à aucune table.`],
|
|
[
|
|
'RESERVATION',
|
|
({ participant, table, tour }) =>
|
|
`Au tour ${tour}, la personne n° ${participant}, réservée à la table d'identifiant ${table}, est placée ailleurs.`,
|
|
],
|
|
|
|
// --- Les violations de verifierIndicateurs (§ 12.3) -----------------------------
|
|
[
|
|
'PLAFOND_A_PRIORI',
|
|
(details) => `Le plafond a priori de ${personneDe(details)} dépasse le nombre des autres personnes : le calcul est faux.`,
|
|
],
|
|
[
|
|
'ORDRE_PLAFONDS',
|
|
(details) => `Le plafond réalisé de ${personneDe(details)} dépasse son plafond a priori : le calcul est faux.`,
|
|
],
|
|
[
|
|
'PLAFOND_REALISE',
|
|
(details) => `Le plafond réalisé de ${personneDe(details)} dépasse le nombre des autres personnes : le calcul est faux.`,
|
|
],
|
|
[
|
|
'DEPASSEMENT',
|
|
(details) => `Les rencontres de ${personneDe(details)} dépassent son plafond réalisé : le calcul est faux.`,
|
|
],
|
|
|
|
// --- Les critères du classement (§ 5.7) -----------------------------------------
|
|
['moteur.critere.ecartAuPlafondAPrioriMax', 'écart au plafond a priori'],
|
|
['moteur.critere.excedentCollisions', 'excédent de collisions'],
|
|
['moteur.critere.collisionsCumulees', 'collisions cumulées'],
|
|
['moteur.critere.rencontresRepetees', 'rencontres répétées'],
|
|
['moteur.critere.redondance', "redondance d'appartenance"],
|
|
]);
|