Files
relay/backend/relay.go
2024-07-13 21:48:03 +03:00

169 lines
3.7 KiB
Go

package main
import (
"math"
)
// Event type represents different events
type Event string
type Solver string
type SolverTimes map[Solver]map[Event]float64
type Assignment map[Solver][]Event
type PermWithTime struct {
Perm []Solver
Time float64
}
type AssignmentWithTime struct {
Assignment Assignment `json:"assignment"`
Time float64 `json:"time"`
}
type Config struct {
SolverTimes SolverTimes `yaml:"solvers"`
Events []Event `yaml:"events"`
Participants []Solver `yaml:"participants"`
}
//
// Generic Functions
//
func Combinations[T comparable](lst []T, n int) func() ([]T, bool) {
length := len(lst)
current := make([]T, n)
index := make([]int, n)
// Initialize the first combination: ["lst[0]", "lst[0]", ..., "lst[0]"]
for i := 0; i < n; i++ {
current[i] = lst[0]
index[i] = 0
}
return func() ([]T, bool) {
if current == nil {
return nil, false
}
// Copy current combination to return
result := make([]T, n)
copy(result, current)
// Find the next combination
j := n - 1
for j >= 0 && index[j] == length-1 {
j--
}
if j < 0 {
current = nil
} else {
index[j]++
current[j] = lst[index[j]]
for k := j + 1; k < n; k++ {
index[k] = 0
current[k] = lst[0]
}
}
return result, current != nil
}
}
func CombinationsWithStart[T comparable](start []T, lst []T, n int) func() ([]T, bool) {
hasNext := true
iter := Combinations(lst, n-len(start))
var end []T
return func() ([]T, bool) {
end, hasNext = iter()
return append(start, end...), hasNext
}
}
func countCombinations[T comparable](lst []T, n int) int {
return int(math.Pow(float64(len(lst)), float64(n)))
}
//
// Relay Functions
//
func CalculateRelayTime(perm []Solver, events []Event, solverTimes SolverTimes) float64 {
solversTotalTimes := make(map[Solver]float64, len(events))
for i, solver := range perm {
solversTotalTimes[solver] += solverTimes[solver][events[i]]
}
var relayTime float64
for _, time := range solversTotalTimes {
if time > relayTime {
relayTime = time
}
}
return relayTime
}
//
// Worker Functions
//
func GenerateSolverCombinationsWorker(solverCombs chan<- func() ([]Solver, bool), solvers []Solver, events []Event) {
hasNext := true
iter := Combinations(solvers, len(events))
for hasNext {
var start []Solver
start, hasNext = iter()
solverCombs <- CombinationsWithStart(start, solvers, len(events))
}
}
func CalculateRelayTimesWorker(solverCombs <-chan func() ([]Solver, bool), assignments chan<- PermWithTime, events []Event, solverTimes SolverTimes) {
for solverComb := range solverCombs {
hasNext := true
for hasNext {
var comb []Solver
comb, hasNext = solverComb()
relayTime := CalculateRelayTime(comb, events, solverTimes)
assignments <- PermWithTime{comb, relayTime}
}
}
}
func Assign(solvers []Solver, solverTimes SolverTimes, events []Event) AssignmentWithTime {
solverCombs := make(chan func() ([]Solver, bool))
permsWithTime := make(chan PermWithTime)
go GenerateSolverCombinationsWorker(solverCombs, solvers, events)
bestAssignment := AssignmentWithTime{
Assignment: Assignment{},
Time: math.Inf(1),
}
// var bestAssignmentMu sync.Mutex
for i := 0; i < 64; i++ {
go CalculateRelayTimesWorker(solverCombs, permsWithTime, events, solverTimes)
}
totalPerms := countCombinations(solvers, len(events))
for i := 0; i < totalPerms; i++ {
// fmt.Println("checking assignment")
p := <-permsWithTime
if p.Time < bestAssignment.Time {
assignment := make(Assignment, len(p.Perm))
for i, solver := range p.Perm {
assignment[solver] = append(assignment[solver], events[i])
}
bestAssignment = AssignmentWithTime{assignment, p.Time}
}
}
return bestAssignment
}