// Algoritmo di assegnazione persone -> stanze. // Vincolo: ogni stanza è mono-genere (M con M, F con F). // Strategia: 1) partiziona le stanze tra i due generi in modo che ciascun // genere ci stia (capienza sufficiente); 2) distribuisce casualmente le // persone nelle stanze del proprio genere, cercando di non lasciare stanze // completamente vuote. (function (global) { function shuffle(arr, rng) { const a = arr.slice(); for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(rng() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } // Sceglie quali stanze vanno alle femmine e quali ai maschi. // Preferisce partizioni in cui la capienza femminile combacia esattamente // col numero di femmine (zero letti sprecati da quel lato => niente stanze // vuote lato femmine). Fallback: qualsiasi partizione valida. function chooseGenderPartition(rooms, nF, nM, rng) { const n = rooms.length; const total = rooms.reduce((s, r) => s + r.posti, 0); const valid = []; const exact = []; // esclude mask 0 e mask "tutte" così ogni genere ha almeno una stanza for (let mask = 1; mask < (1 << n) - 1; mask++) { let capF = 0; for (let i = 0; i < n; i++) if (mask & (1 << i)) capF += rooms[i].posti; const capM = total - capF; if (capF >= nF && capM >= nM) { valid.push(mask); if (capF === nF) exact.push(mask); } } const pool = exact.length ? exact : valid; if (!pool.length) { throw new Error( "Nessuna partizione valida: capienza insufficiente per il vincolo di genere." ); } return pool[Math.floor(rng() * pool.length)]; } // Distribuisce le persone nelle stanze del proprio genere. // Prima assegna 1 persona a stanza (copertura => niente stanze vuote se // le persone sono >= delle stanze), poi riempie i letti restanti a caso. function fillRooms(roomsG, peopleG, rng) { const rooms = shuffle(roomsG, rng); const people = shuffle(peopleG, rng); const assign = {}; rooms.forEach((r) => (assign[r.id] = [])); let idx = 0; if (people.length >= rooms.length) { for (const r of rooms) assign[r.id].push(people[idx++]); } const slots = []; for (const r of rooms) { const free = r.posti - assign[r.id].length; for (let k = 0; k < free; k++) slots.push(r.id); } const freeSlots = shuffle(slots, rng); while (idx < people.length) { const roomId = freeSlots.shift(); assign[roomId].push(people[idx++]); } return assign; } function assign(rooms, participants, rng) { rng = rng || Math.random; const females = participants.filter((p) => p.genere === "F"); const males = participants.filter((p) => p.genere === "M"); const mask = chooseGenderPartition(rooms, females.length, males.length, rng); const fRooms = rooms.filter((r, i) => mask & (1 << i)); const mRooms = rooms.filter((r, i) => !(mask & (1 << i))); const fAssign = fillRooms(fRooms, females.map((p) => p.nome), rng); const mAssign = fillRooms(mRooms, males.map((p) => p.nome), rng); const byPerson = {}; const roomGender = {}; fRooms.forEach((r) => (roomGender[r.id] = "F")); mRooms.forEach((r) => (roomGender[r.id] = "M")); for (const rid in fAssign) fAssign[rid].forEach((nome) => (byPerson[nome] = rid)); for (const rid in mAssign) mAssign[rid].forEach((nome) => (byPerson[nome] = rid)); return { byPerson, // nome -> roomId roomGender, // roomId -> "F" | "M" fRooms, mRooms, }; } const Matching = { assign, shuffle }; if (typeof module !== "undefined" && module.exports) module.exports = Matching; global.Matching = Matching; })(typeof window !== "undefined" ? window : globalThis);