150 lines
3.5 KiB
C
150 lines
3.5 KiB
C
/*
|
|
* Copyright (c) Rhizome <rhizome@wip.sh>
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
#include "rotagen.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/time.h>
|
|
|
|
#define POOL_SIZE 0x1000
|
|
#define MAX_PEOPLE 32
|
|
|
|
static struct config conf;
|
|
static uint8_t pool[POOL_SIZE];
|
|
static uint8_t *free_ptr;
|
|
|
|
static void *pool_alloc(int size)
|
|
{
|
|
if (free_ptr + size > pool + POOL_SIZE)
|
|
return NULL;
|
|
void *ptr = free_ptr;
|
|
free_ptr += size;
|
|
return ptr;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
free_ptr = &pool[0];
|
|
|
|
unsigned seed;
|
|
struct timeval now;
|
|
if (gettimeofday(&now, NULL) == 0) {
|
|
seed = now.tv_usec;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Warning: failed to get time of day to seed random number "
|
|
"generation; falling back to hard-coded seed.\n");
|
|
seed = 0xdeadbeef;
|
|
}
|
|
srand(seed);
|
|
|
|
const char *config_path = argc > 1 ? argv[1] : "rotagen.conf";
|
|
memset(&conf, 0, sizeof(conf));
|
|
read_config(config_path, &conf);
|
|
|
|
struct slot_result *rota
|
|
= pool_alloc(conf.num_slots * sizeof(struct slot_result));
|
|
if (rota == NULL) {
|
|
fprintf(stderr,
|
|
"Fatal error: memory pool exhausted in %s().\n",
|
|
__func__);
|
|
exit(1);
|
|
}
|
|
|
|
generate_rota(rota);
|
|
print_rota(rota);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void generate_rota(struct slot_result *rota_out)
|
|
{
|
|
int prev[MAX_JOBS];
|
|
for (int job = 0; job < conf.num_jobs; ++job)
|
|
prev[job] = conf.num_people;
|
|
|
|
for (int slot = 0; slot < conf.num_slots; ++slot) {
|
|
do {
|
|
for (int job = 0; job < conf.num_jobs; ++job) {
|
|
struct assignment *assignment
|
|
= &rota_out[slot].assignments[job];
|
|
do {
|
|
assignment->slot = slot;
|
|
assignment->job = job;
|
|
assignment->person = rand() % conf.num_people;
|
|
} while (!satisfies_assignment_constraints(assignment)
|
|
|| previously_allocated(prev, assignment->person));
|
|
}
|
|
} while (!satisfies_slot_constraints(&rota_out[slot]));
|
|
|
|
for (int job = 0; job < conf.num_jobs; ++job)
|
|
prev[job] = rota_out[slot].assignments[job].person;
|
|
}
|
|
}
|
|
|
|
bool satisfies_assignment_constraints(const struct assignment *assignment)
|
|
{
|
|
for (int i = 0; i < conf.num_constraints; ++i) {
|
|
if (assignment->person != conf.constraints[i].person)
|
|
continue;
|
|
switch (conf.constraints[i].type) {
|
|
case JOB_EXEMPTION_CONSTRAINT:
|
|
if (assignment->job == conf.constraints[i].object.job)
|
|
return false;
|
|
break;
|
|
case SLOT_EXEMPTION_CONSTRAINT:
|
|
if (assignment->slot == conf.constraints[i].object.slot)
|
|
return false;
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"Warning: unhandled constraint type %d in %s()",
|
|
conf.constraints[i].type,
|
|
__func__);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool previously_allocated(int prev[MAX_JOBS], int person)
|
|
{
|
|
for (int job = 0; job < conf.num_jobs; ++job) {
|
|
if (prev[job] == person)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool satisfies_slot_constraints(const struct slot_result *result)
|
|
{
|
|
for (int i = 0; i < conf.num_jobs; ++i) {
|
|
for (int j = i + 1; j < conf.num_jobs; ++j) {
|
|
if (result->assignments[i].person == result->assignments[j].person)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void print_rota(const struct slot_result *rota)
|
|
{
|
|
for (int slot = 0; slot < conf.num_slots; ++slot) {
|
|
printf("----------------------------------------\n");
|
|
for (int job = 0; job < conf.num_jobs; ++job) {
|
|
printf("%s\t%s\t\t%s\n",
|
|
job == 0 ? conf.slots[slot] : " ",
|
|
conf.jobs[job],
|
|
conf.people[rota[slot].assignments[job].person]);
|
|
}
|
|
}
|
|
printf("----------------------------------------\n");
|
|
}
|