adventofcode/2018/day07/day07.go

228 lines
4.2 KiB
Go

package main
import (
"bufio"
"errors"
"fmt"
"os"
"strings"
"time"
)
const ClearScreen = "\033[H\033[2J"
var manager *StepManager
var TotalWorkers int
func main() {
inp := StdinToStringSlice()
signTheContract(inp)
part1()
manager.resetJobs()
part2()
}
func signTheContract(inp []string) {
TotalWorkers = 5
manager = &StepManager{workers: TotalWorkers}
for _, v := range inp {
manager.addOrUpdateStep(v[36], v[5])
}
}
func part1() {
fmt.Println("= Part 1 =")
for !manager.done() {
manager.part1Act()
}
fmt.Println("")
}
func part2() {
var seconds int
realtime := true
for !manager.done() {
seconds++
manager.part2Act()
fmt.Print(ClearScreen)
fmt.Println(manager.string())
fmt.Println("Total Seconds: ", seconds)
if realtime {
time.Sleep(time.Millisecond * 125)
}
}
}
type StepManager struct {
steps []*Step
workers int
}
// In part 1, all steps complete immediately
func (m *StepManager) part1Act() {
// Find the next step that should run
var nextStep *Step
for i := range m.steps {
if !m.steps[i].done() && m.steps[i].ready() && (nextStep == nil || nextStep.id > m.steps[i].id) {
nextStep = m.steps[i]
}
}
nextStep.timeSpent = int(nextStep.id) + 5
fmt.Print(string(nextStep.id))
}
func (m *StepManager) part2Act() {
// Clear all done jobs
for i := range m.steps {
if m.steps[i].active && m.steps[i].done() {
m.steps[i].active = false
m.workers++
fmt.Println(string(m.steps[i].id))
}
}
// Find all steps that are ready for work
var waitingSteps []*Step
for i := range m.steps {
if m.steps[i].ready() && !m.steps[i].done() && !m.steps[i].active {
waitingSteps = append(waitingSteps, m.steps[i])
}
}
// If we have any available workers, get them working
for m.workers > 0 {
var nextStep *Step
for i := range waitingSteps {
if !waitingSteps[i].active && (nextStep == nil || waitingSteps[i].id < nextStep.id) {
nextStep = waitingSteps[i]
}
}
if nextStep != nil {
nextStep.active = true
m.workers--
} else {
break
}
}
// Increment all active steps
for i := range m.steps {
if m.steps[i].active {
m.steps[i].timeSpent++
}
}
}
func (m *StepManager) getStep(id byte) (*Step, error) {
for i := range m.steps {
if m.steps[i].id == id {
return m.steps[i], nil
}
}
return nil, errors.New("No step with id " + string(id) + " found.")
}
func (m *StepManager) addOrUpdateStep(id, reqId byte) {
s, err := m.getStep(id)
if err != nil {
s = &Step{id: id}
}
r, reqErr := m.getStep(reqId)
if reqErr != nil {
r = &Step{id: reqId}
m.steps = append(m.steps, r)
}
s.addRequirement(r)
if err != nil {
m.steps = append(m.steps, s)
}
}
func (m *StepManager) resetJobs() {
for i := range manager.steps {
manager.steps[i].timeSpent = 0
}
}
func (m *StepManager) string() string {
var ret string
var stepsDone int
for i := range manager.steps {
ret += (manager.steps[i].string() + "\n")
if manager.steps[i].done() {
stepsDone++
}
}
pct := 100 * (float64(stepsDone) / float64(len(manager.steps)))
filled := int(pct / 10)
progBar := strings.Repeat("\u2588", filled)
progBar += strings.Repeat(" ", (10 - filled))
ret += fmt.Sprintf("Progress: [%s] %d\n", progBar, int(pct))
ret += fmt.Sprintf("Idle Workers %d/%d", m.workers, TotalWorkers)
return ret
}
func (m *StepManager) done() bool {
for i := range m.steps {
if !m.steps[i].done() {
return false
}
}
return true
}
type Step struct {
id byte
req []*Step
timeSpent int
active bool
}
func (s *Step) done() bool {
return s.timeSpent >= int(s.id)-4
}
func (s *Step) addRequirement(r *Step) {
for i := range s.req {
if s.req[i] == r {
return
}
}
s.req = append(s.req, r)
}
func (s *Step) ready() bool {
for i := range s.req {
if !s.req[i].done() {
return false
}
}
return true
}
func (s *Step) string() string {
var ret string
if s.done() {
ret += "[✔️ ] "
} else {
ret += "[✖️ ] "
}
ret += fmt.Sprintf("%s ( %d ): [", string(s.id), s.id)
for i := range s.req {
ret += string(s.req[i].id) + " "
}
ret += "] "
if s.active {
ret += "▶️ "
}
return ret
}
func StdinToStringSlice() []string {
var input []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
input = append(input, scanner.Text())
}
return input
}