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 }