diff --git a/ts/tsism-lib/src/algorithm.ts b/ts/tsism-lib/src/algorithm.ts index ea93cf6..711d73a 100644 --- a/ts/tsism-lib/src/algorithm.ts +++ b/ts/tsism-lib/src/algorithm.ts @@ -1,5 +1,6 @@ -import {NumberToNumberMapInterface, Student} from './student' +import {Student} from './student' import {Seminar} from './seminar' +import {Iteration} from "./iteration"; export enum Points { FIRST_SELECTION = 0, @@ -8,29 +9,6 @@ export enum Points { NO_SELECTION = 30 } -interface AssignmentMapInterface { - get: (s: Student) => { wSeminar?: Seminar, pSeminar?: Seminar } | undefined - set: (s: Student, value: { wSeminar?: Seminar, pSeminar?: Seminar }) => void -} - -class AssignmentMapArray implements AssignmentMapInterface { - readonly a: Array<{ wSeminar?: Seminar; pSeminar?: Seminar }> = [] - - get(s: Student): { wSeminar?: Seminar; pSeminar?: Seminar } | undefined { - return this.a[s.id]; - } - - set(s: Student, value: { wSeminar?: Seminar; pSeminar?: Seminar }): void { - this.a[s.id] = value; - } -} - -export type Iteration = { - id: number, - points: number, - assignments: AssignmentMapInterface -} - function shuffle(a: Array) { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); @@ -46,14 +24,13 @@ export async function runAlgorithm(iterations: number, students: Student[]): Pro } function tryAssignment(seminar: Seminar, student: Student, currentIteration: Iteration, seminarType: 'W' | 'P', points: Points) { - let cap = seminar.remainingCapacityPerRun.get(currentIteration.id) + let cap = currentIteration.seminarCapacity.get(seminar.id) if (cap === undefined) { cap = seminar.capacity } if (cap > 0) { - seminar.remainingCapacityPerRun.set(currentIteration.id, cap - 1) - currentIteration.points += points + currentIteration.seminarCapacity.set(seminar.id, cap - 1); const previousAssignment = currentIteration.assignments.get(student) || {} if (seminarType === 'W') { @@ -64,7 +41,7 @@ function tryAssignment(seminar: Seminar, student: Student, currentIteration: Ite currentIteration.assignments.set(student, {...previousAssignment, pSeminar: seminar}) } - student.pointsPerRun.set(currentIteration.id, (student.pointsPerRun.get(currentIteration.id) || 0) + points) + currentIteration.pointsPerStudent.set(student.id, (currentIteration.pointsPerStudent.get(student.id) || 0) + points) return true } @@ -75,7 +52,8 @@ function tryAssignment(seminar: Seminar, student: Student, currentIteration: Ite function execute(iterations: number, students: Student[]): Iteration { let best: Iteration | null = null for (let i = 0; i < iterations; i++) { - const currentIteration: Iteration = {id: i, points: 0, assignments: new AssignmentMapArray()} + const algRun = new Iteration(); + const currentIteration = new Iteration(); const studentsCopy = shuffle([...students]) for (const student of studentsCopy) { if ( @@ -83,13 +61,13 @@ function execute(iterations: number, students: Student[]): Iteration { !tryAssignment(student.wWishes[1], student, currentIteration, 'W', Points.SECOND_SELECTION) && !tryAssignment(student.wWishes[2], student, currentIteration, 'W', Points.THIRD_SELECTION) ) { - student.pointsPerRun.set(i, Points.NO_SELECTION) - currentIteration.points += Points.NO_SELECTION + currentIteration.pointsPerStudent.set(student.id, Points.NO_SELECTION) + algRun.pointsPerStudent.set(student.id, Points.NO_SELECTION) } } studentsCopy.sort((a, b) => { - return (b.pointsPerRun.get(i) || 0) - (a.pointsPerRun.get(i) || 0) + return (currentIteration.pointsPerStudent.get(b.id) || 0) - (currentIteration.pointsPerStudent.get(a.id) || 0) }) for (const student of studentsCopy) { @@ -98,12 +76,11 @@ function execute(iterations: number, students: Student[]): Iteration { !tryAssignment(student.pWishes[1], student, currentIteration, 'P', Points.SECOND_SELECTION) && !tryAssignment(student.pWishes[2], student, currentIteration, 'P', Points.THIRD_SELECTION) ) { - student.pointsPerRun.set(i, Points.NO_SELECTION + (student.pointsPerRun.get(i) || 0)) - currentIteration.points += Points.NO_SELECTION + currentIteration.pointsPerStudent.set(student.id, Points.NO_SELECTION + (currentIteration.pointsPerStudent.get(student.id) || 0)) } } - if (best === null || best.points > currentIteration.points) { + if (best === null || best.totalPoints() > currentIteration.totalPoints()) { best = currentIteration } } diff --git a/ts/tsism-lib/src/io.ts b/ts/tsism-lib/src/io.ts index d49a92d..217ef67 100644 --- a/ts/tsism-lib/src/io.ts +++ b/ts/tsism-lib/src/io.ts @@ -2,7 +2,7 @@ import {Seminar, SeminarType} from './seminar' import * as fs from 'node:fs' import {parse} from 'csv-parse' import {Student} from './student' -import {Iteration} from './algorithm' +import {Iteration} from './iteration' export type SeminarData = { wSeminars: Seminar[], pSeminars: Seminar[] } @@ -17,6 +17,7 @@ export function importSeminars(file: string): Promise { wSeminars: [], pSeminars: [], } + let currID = 0; for (const result of results) { if (!Array.isArray(result) || result.length < 3) { console.warn(`Skip invalid line: "${result}"`) @@ -25,14 +26,15 @@ export function importSeminars(file: string): Promise { switch (result[2]) { case 'W': - ret.wSeminars.push(new Seminar(result[0], Number.parseInt(result[1], 10), SeminarType.W_SEMINAR)) + ret.wSeminars.push(new Seminar(currID, result[0], Number.parseInt(result[1], 10), SeminarType.W_SEMINAR)) break case 'P': - ret.pSeminars.push(new Seminar(result[0], Number.parseInt(result[1], 10), SeminarType.P_SEMINAR)) + ret.pSeminars.push(new Seminar(currID, result[0], Number.parseInt(result[1], 10), SeminarType.P_SEMINAR)) break default: throw new Error('Unknown Seminar Type ' + result[3]) } + currID++; } resolve(ret) @@ -72,8 +74,8 @@ export function importStudents(file: string, seminars: SeminarData): Promise a + b, 0); + } + return this.points; + } + +} \ No newline at end of file diff --git a/ts/tsism-lib/src/maps.ts b/ts/tsism-lib/src/maps.ts new file mode 100644 index 0000000..f8b4155 --- /dev/null +++ b/ts/tsism-lib/src/maps.ts @@ -0,0 +1,40 @@ +import {Student} from "./student"; +import {Seminar} from "./seminar"; + +export interface AssignmentMapInterface { + get: (s: Student) => { wSeminar?: Seminar, pSeminar?: Seminar } | undefined + set: (s: Student, value: { wSeminar?: Seminar, pSeminar?: Seminar }) => void +} + +export class AssignmentMapArray implements AssignmentMapInterface { + readonly a: Array<{ wSeminar?: Seminar; pSeminar?: Seminar }> = [] + + get(s: Student): { wSeminar?: Seminar; pSeminar?: Seminar } | undefined { + return this.a[s.id]; + } + + set(s: Student, value: { wSeminar?: Seminar; pSeminar?: Seminar }): void { + this.a[s.id] = value; + } +} + +export interface NumberToNumberMapInterface { + get: (id: number) => number | undefined + set: (id: number, value: number) => void + getAllValues: () => number[] +} + +export class NumberToNumberArray implements NumberToNumberMapInterface { + readonly a: number[] = [] + get(id: number): number | undefined { + return (this.a)[id]; + } + + set(id: number, value: number): void { + this.a[id] = value; + } + + getAllValues(): number[] { + return this.a; + } +} \ No newline at end of file diff --git a/ts/tsism-lib/src/seminar.ts b/ts/tsism-lib/src/seminar.ts index 8503f46..846074f 100644 --- a/ts/tsism-lib/src/seminar.ts +++ b/ts/tsism-lib/src/seminar.ts @@ -1,17 +1,16 @@ -import {NumberToNumberMapInterface, NumberToNumberArray} from "./student"; - export enum SeminarType { W_SEMINAR, P_SEMINAR, } export class Seminar { + readonly id: number readonly name: string readonly capacity: number readonly type: SeminarType - readonly remainingCapacityPerRun: NumberToNumberMapInterface = new NumberToNumberArray() - constructor(name: string, capacity: number, type: SeminarType) { + constructor(id: number, name: string, capacity: number, type: SeminarType) { + this.id = id this.name = name this.capacity = capacity this.type = type diff --git a/ts/tsism-lib/src/student.ts b/ts/tsism-lib/src/student.ts index b5b7493..34e487c 100644 --- a/ts/tsism-lib/src/student.ts +++ b/ts/tsism-lib/src/student.ts @@ -1,27 +1,12 @@ import {Seminar} from './seminar' -export interface NumberToNumberMapInterface { - get: (id: number) => number | undefined - set: (id: number, value: number) => void -} - -export class NumberToNumberArray implements NumberToNumberMapInterface { - readonly a: number[] = [] - get(id: number): number | undefined { - return (this.a)[id]; - } - set(id: number, value: number): void { - this.a[id] = value; - } -} export class Student { readonly name: string readonly id: number readonly wWishes: [Seminar, Seminar, Seminar] readonly pWishes: [Seminar, Seminar, Seminar] - readonly pointsPerRun: NumberToNumberMapInterface = new NumberToNumberArray() constructor(id: number, name: string, wWishes: [Seminar, Seminar, Seminar], pWishes: [Seminar, Seminar, Seminar]) { this.id = id