Skip to content

Commit

Permalink
Implemented main alg & restructured code
Browse files Browse the repository at this point in the history
* Updated configs.

* Renamed/removed data properties.

* First working backtracking version for distributing.
  • Loading branch information
erik-sth authored Jul 14, 2024
1 parent f38a151 commit 69c166d
Show file tree
Hide file tree
Showing 29 changed files with 306 additions and 317 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "node",
"request": "launch",
"name": "Debug TypeScript",
"program": "${workspaceFolder}/src/index.ts",
"program": "${workspaceFolder}/src/alg/test.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"sourceMaps": true
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion benchmark/timeDistribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const benchmarkResults: BenchmarkResults = {
middleValue,
timestamp: new Date().toISOString(),
};
const resultsFilePath: string = 'results.json';
const resultsFilePath: string = 'benchmark/results.json';
let existingResultsArray: BenchmarkResults[] = [];

if (fs.existsSync(resultsFilePath)) {
Expand Down
10 changes: 0 additions & 10 deletions jest.config.js

This file was deleted.

13 changes: 13 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Config } from '@jest/types';

const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'js'],
testMatch: ['**/test/**/*.test.(ts|js)'],
transform: {
'^.+\\.(ts)$': 'ts-jest',
},
};

export default config;
6 changes: 0 additions & 6 deletions serverConfig.json

This file was deleted.

61 changes: 17 additions & 44 deletions src/alg/StudentDistribution.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,22 @@
import { Group, Groups } from '../Class/Groups';
// tyoes
import PollQuestion from '../types/Polls';

import Student from '../types/Student';
import Item from '../types/Item';
import Project from '../types/Project';

// algorithmen stages
import createGraph from './StudentDistribution/CreateGraph';
import { distributeStudentsToPaths } from './StudentDistribution/DistributeStudents';
import { findPathsForTheGroups } from './StudentDistribution/FindPaths';
import { getVotingIds } from './StudentDistribution/Utils';
import { allocateGroupsToItems } from './StudentDistribution/AllocateGroupsToItems';
import Item from '../types/Item';
import Project from '../types/Project';
import Room from '../types/Room';
import { rooms } from '../data/Rooms';

function buildGroupsByPaths(
polls: PollQuestion[],
students: Student[],
project: Project
): Group[] {
const groups = new Groups();
students.forEach((student) => {
groups.add(getVotingIds(student._id, polls), student._id);
});
if (groups.size() == 0) {
project.failed = true;
project.reasonForFailing = 'Zero Possilbe Groups';
}
return groups.getAll();
}

function validating(
project: Project,
items: Item[],
students: Student[],

rooms: Room[]
): boolean {
if (items.length > 0 && students.length > 0 && rooms.length > 0) {
return true;
} else {
project.failed = true;
project.reasonForFailing = `StudentsAmount: ${students.length}}; RoomsAmount: ${rooms.length}; ItemsAmount: ${items.length}`;
return false;
}
}
//data and utils
import { rooms } from '../data/Rooms';
import {
buildGroupsByPaths,
getVotingIds,
validating,
} from './StudentDistribution/Utils';

function main(
items: Item[],
Expand All @@ -53,27 +26,27 @@ function main(
): boolean {
project.status = 'Validating';
validating(project, items, students, rooms);
if (project.failed) return false;
if (project.failedCalculating) return false;

project.status = 'CreateGroups';
const groups = buildGroupsByPaths(polls, students, project);
if (project.failed) return false;
if (project.failedCalculating) return false;

project.status = 'CreateGraph';
const g = createGraph(items, project);
if (project.failed) return false;
if (project.failedCalculating) return false;

project.status = 'FindPaths';
findPathsForTheGroups(groups, items, g, project);
if (project.failed) return false;
if (project.failedCalculating) return false;

project.status = 'Distributing';
distributeStudentsToPaths(items, groups, project);
if (project.failed) return false;
if (project.failedCalculating) return false;

project.status = 'Allocating';
allocateGroupsToItems(items, groups, project);
if (project.failed) return false;
if (project.failedCalculating) return false;

project.status = 'FinishedCalc';
return true;
Expand Down
6 changes: 3 additions & 3 deletions src/alg/StudentDistribution/AllocateGroupsToItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ function allocateGroupsToItems(
groups.forEach((group) => {
const rooms = findRoommatesInGroup(group, project);
group.paths.forEach((path_config) => {
if (path_config.valueForTestingStudentDistribution !== 0) {
if (path_config.testValueForDistributingStudents !== 0) {
let studentsCount =
path_config.valueForTestingStudentDistribution;
path_config.testValueForDistributingStudents;

while (studentsCount > 0) {
let room = rooms.shift();
Expand All @@ -25,7 +25,7 @@ function allocateGroupsToItems(
rooms.push(secondPart);
}

allocateToItems(path_config.path, items, room);
allocateToItems(path_config.itemsInPath, items, room);
studentsCount -= room.length;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/alg/StudentDistribution/CreateGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function createGraph(items: Item[], project: Project): DirectedGraph<Item> {
});
});
if (G.sizeEdges() === 0) {
project.failed = true;
project.failedCalculating = true;
project.reasonForFailing = 'Graph build failed: ZeroEdges';
}
return G;
Expand Down
193 changes: 124 additions & 69 deletions src/alg/StudentDistribution/DistributeStudents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,93 +6,148 @@ function distributeStudentsToPaths(
items: Item[],
groups: Group[],
project: Project
) {
setGroupsWithOnePath(groups, items);
): void {
setGroupsWithOnePath(groups);
const changableGroups = groups.filter((group) => group.paths.length > 1);
let relevantItems = findRelevantItems(items, changableGroups);

relevantItems = filterOutItemsWithoutCapacityProblems(
relevantItems,
amountRelevantStudents(changableGroups)
);
return distributeWithMinimumCapacity(
changableGroups,
relevantItems,
project
);
}

function distributeWithMinimumCapacity(
changableGroups: Group[],
relevantItems: Item[],
project: Project
) {
let working = true;
changableGroups.forEach((group) => {
let amountStudents = group.studentIds.length;
group.paths.forEach((path) => {
const minCapacity = Math.min(
...path.path.map((pathItem) => pathItem.updatedGroupCapacity),
group.studentIds.length
);
path.valueForTestingStudentDistribution = minCapacity;
amountStudents -= minCapacity;
relevantItems.forEach((item) => {
if (path.path.includes(item)) {
item.updatedGroupCapacity -= minCapacity;
}
});
const groupsDistribution: {
groupId: number;
distributedStudentsPerPath: number[];
validDistributionNumbers: number[][];
amountToDistribute: number;
remainingToDistribute: number;
}[] = [];

changableGroups.forEach((g) => {
groupsDistribution.push({
groupId: g._id,
amountToDistribute: g.studentIds.length,
remainingToDistribute: g.studentIds.length,
distributedStudentsPerPath: Array(g.paths.length).fill(0),
validDistributionNumbers: [],
});
});

if (amountStudents > 0) {
working = false;
project.failed = true;
project.reasonForFailing = 'Students left after distribution done';
}
let validFound = false;

changableGroups.forEach((g, index) => {
distributeItems(
0,
g.studentIds.length,
new Array(g.paths.length).fill(0),
groupsDistribution[index].validDistributionNumbers,
g
);
});
return working;
}

function amountRelevantStudents(changableGroups: Group[]) {
return changableGroups.reduce(
(total, group) => total + group.studentIds.length,
0
);
if (!validFound) {
distributeStudentsToPath(0, 0);
}

// Recursive function to distribute items
function distributeItems(
currentIndex: number,
remainingItems: number,
currentDistribution: number[],
validDistributionNumbers: number[][],
group: Group
): void {
if (validFound) {
return;
}

if (currentIndex === currentDistribution.length - 1) {
currentDistribution[currentIndex] = remainingItems;

group.paths.forEach((p, index) => {
p.testValueForDistributingStudents = currentDistribution[index];
});

if (isValid(groups)) {
validDistributionNumbers.push([...currentDistribution]);
} else {
group.paths.forEach((p) => {
p.testValueForDistributingStudents = 0;
});
}
} else {
for (let i: number = 0; i <= remainingItems; i++) {
currentDistribution[currentIndex] = i;
distributeItems(
currentIndex + 1,
remainingItems - i,
currentDistribution,
validDistributionNumbers,
group
);
}
}
}

// Function to distribute students to paths recursively
function distributeStudentsToPath(
currentIndex: number,
groupIndex: number
): void {
if (validFound || groupIndex >= changableGroups.length) {
project.failedCalculating = true;
return;
}

const group = groupsDistribution[groupIndex];

if (currentIndex >= group.validDistributionNumbers.length) {
distributeStudentsToPath(0, groupIndex + 1);
return;
}

group.validDistributionNumbers[currentIndex].forEach(
(distributionNumber, index) => {
changableGroups[groupIndex].paths[
index
].testValueForDistributingStudents = distributionNumber;
}
);

if (isValid(groups)) {
validFound = true;
return;
}

distributeStudentsToPath(currentIndex + 1, groupIndex);
}
}

function findRelevantItems(items: Item[], changableGroups: Group[]): Item[] {
return items.filter((item) => {
let counter = 0;
changableGroups.forEach((group) => {
group.paths.forEach((path) => {
if (path.path.includes(item)) {
counter++;
function isValid(groups: Group[]): boolean {
const mapOfUsedCapacityPerItem: Map<Item, number> = new Map();
let isValidDistribution = true;

groups.forEach((g) => {
g.paths.forEach((path) => {
path.itemsInPath.forEach((item) => {
const itemMap = mapOfUsedCapacityPerItem.get(item) || 0;
mapOfUsedCapacityPerItem.set(
item,
itemMap + path.testValueForDistributingStudents
);

if (
item.studentCapacity < mapOfUsedCapacityPerItem.get(item)!
) {
isValidDistribution = false;
}
});
});
return counter > 0;
});
}

function filterOutItemsWithoutCapacityProblems(
relevantItems: Item[],
amountRelevantStudents: number
) {
return relevantItems.filter(
(item) => item.updatedGroupCapacity < amountRelevantStudents
);
return isValidDistribution;
}

function setGroupsWithOnePath(groups: Group[], items: Item[]) {
function setGroupsWithOnePath(groups: Group[]): void {
groups.forEach((group) => {
if (group.paths.length === 1) {
group.paths[0].valueForTestingStudentDistribution =
group.paths[0].testValueForDistributingStudents =
group.studentIds.length;
items.forEach((item) => {
if (group.paths[0].path.includes(item)) {
item.updatedGroupCapacity -= group.studentIds.length;
}
});
}
});
}
Expand Down
Loading

0 comments on commit 69c166d

Please sign in to comment.