Den 7. byl opět jeden z drsnějších, ale nakonec se podařilo 🙂 .

import com.svetylkovo.rojo.Rojo
import java.io.File
import java.lang.StringBuilder

data class Step(val name: String) : Comparable<Step> {
    val children = mutableListOf<Step>()
    var completed = false
    var remainingSeconds = (name.first().toInt() - 'A'.toInt() + 1) + 60

    fun doWork() {
        if (--remainingSeconds < 1) completed = true
    }

    fun completeAndPrint() {
        completed = true
        print(name)
    }

    override fun compareTo(other: Step) = name.compareTo(other.name)
}

data class Worker(val name: String) {
    var stepInProgress: Step? = null

    fun doWork() = stepInProgress?.doWork()
}

fun main() {

    val stepsMap = mutableMapOf<String, Step>()

    val input = File("input7.txt").readText().trim()

    Rojo.forEach("Step (\\w+).+(\\w+) can", input) { name, subsequentName ->
        val step = stepsMap.computeIfAbsent(name) { Step(name) }
        step.children += stepsMap.computeIfAbsent(subsequentName) { Step(subsequentName) }
    }

    val allSteps = stepsMap.values.toList()

    //A
    processSteps(allSteps.findAvailableSteps(), allSteps)
    println()

    //B
    val workers = (1..5).map { Worker("Worker $it") }

    allSteps.forEach { it.completed = false }
    processStepsParallel(allSteps.findAvailableSteps(), allSteps, workers)
}

tailrec fun processStepsParallel(
    availableSteps: List<Step>,
    allSteps: List<Step>,
    workers: List<Worker>,
    doneSteps: StringBuilder = StringBuilder(),
    seconds: Int = 0
) {
    if (availableSteps.isNotEmpty()) {
        workers.forEach {
            it.run {
                doWork()
                if (stepInProgress?.completed == true) {
                    doneSteps.append(stepInProgress?.name)
                    stepInProgress = null
                }
            }
        }

        assignWork(workers, allSteps)

        println("Second $seconds, ${workers.joinToString(" ") {
            "${it.name}: ${it.stepInProgress?.name ?: "."}"
        }}, Done: $doneSteps")

        return processStepsParallel(allSteps.findAvailableSteps(), allSteps, workers, doneSteps, seconds + 1)
    }

    val realSeconds = seconds - 1

    println("Elapsed seconds: $realSeconds")
}

private fun assignWork(
    workers: List<Worker>,
    allSteps: List<Step>
) {
    val unassignedSteps = allSteps.findAvailableSteps()
        .filterNot { step -> workers.any { it.stepInProgress == step } }

    workers.filter { it.stepInProgress == null }
        .zip(unassignedSteps) { worker, step ->
            worker.stepInProgress = step
        }
}

tailrec fun processSteps(availableSteps: List<Step>, allSteps: List<Step>) {
    if (availableSteps.isNotEmpty()) {
        availableSteps.first().completeAndPrint()
        processSteps(allSteps.findAvailableSteps(), allSteps)
    }
}

private fun List<Step>.findAvailableSteps() = filter { !it.completed }
    .filter { findPendingDependenciesFor(it).isEmpty() }
    .sorted()

private fun List<Step>.findPendingDependenciesFor(step: Step) = filter { !it.completed }
    .filter { it.children.any { it.name == step.name } }