2018/07.py
def part2() -> int:
    """
    I'm doing Part 2 using a discrete event simulation (DES). I'm assuming there's a way
    to do this using a topological sort as well, but I wanted to implement a DES.

    The basic idea of a DES is that you keep a priority queue of "events". Each event is
    a *discrete event* and mutates the state of the world in some way. In my case, the
    state of the world consists of how many workers are available; the set of jobs that
    are unstarted, started, and satisfied; and the time of the last completed job.
    """
    # Calculate the time it will take for each job to complete. For the full problem,
    # the time is 60 + (numeric letter of the alphabet, starting at 1)
    BASE_TIME = 60 if not test else 0
    WORKERS = 6 if not test else 2
    job_to_time = {c: BASE_TIME + ord(c) - ord("A") + 1 for c in string.ascii_uppercase}

    # World state
    T = 0
    unstarted: Set[str] = set(ans_part1)
    satisfied: Set[str] = set()
    workers_available: int = 0
    started: Set[str] = set()

    # Create the event queue. The queue contains tuples of (time, action, ID) where time
    # is the time at which the event occurs (note that this is the primary sort key in
    # the priority queue), action is the action type, and ID is an optional arbitrary ID
    # (used for the JOB DONE event).

    # We start by populating the queue with an inital set of events: one event for each
    # worker becoming available at time = 0.
    Q: List[Tuple[int, str, str]] = [
        (0, "WORKER AVAILABLE", "") for _ in range(WORKERS)
    ]

    # Loop through all of the events.
    while Q:
        t, action, id_ = heapq.heappop(Q) # extract the next event

        # Handle the different actions
        if action == "JOB DONE":
            started.remove(id_)  # remove from the set of jobs currently being performed
            satisfied.add(id_)  # add to the set of satisfied/completed jobs
            T = max(T, t)  # update the max time
        elif action == "WORKER AVAILABLE":
            # A worker has become available, increase the number of available workers.
            workers_available += 1
        elif action == "WORKER START":
            if id_ in started:
                # Skip if already being worked on.
                continue

            # Start work on the job.
            started.add(id_)
            unstarted.remove(id_)
            workers_available -= 1  # the worker is no longer available

            # Look up how long it will be until the job is completed. At that time, the
            # job will be done and the worker will become available. Add these events to
            # the queue.
            heapq.heappush(Q, (t + job_to_time[id_], "JOB DONE", id_))
            heapq.heappush(Q, (t + job_to_time[id_], "WORKER AVAILABLE", ""))

        # Then, check if we can have a worker start on a job.
        if workers_available > 0 and unstarted:
            for r in unstarted:
                # Can we actually do this job?
                issat = True
                for dep in STEP_DEPENDENCIES[r]:
                    if dep not in satisfied:
                        issat = False
                        break
                if issat:
                    heapq.heappush(Q, (t, "WORKER START", r))
                    break

    return T

    I'm doing Part 2 using a discrete event simulation (DES). I'm assuming there's a way
    to do this using a topological sort as well, but I wanted to implement a DES.

    The basic idea of a DES is that you keep a priority queue of "events". Each event is
    a *discrete event* and mutates the state of the world in some way. In my case, the
    state of the world consists of how many workers are available; the set of jobs that
    are unstarted, started, and satisfied; and the time of the last completed job.
    
I'm doing Part 2 using a discrete event simulation (DES). I'm assuming there's a way
to do this using a topological sort as well, but I wanted to implement a DES.

The basic idea of a DES is that you keep a priority queue of "events". Each event is
a *discrete event* and mutates the state of the world in some way. In my case, the
state of the world consists of how many workers are available; the set of jobs that
are unstarted, started, and satisfied; and the time of the last completed job.
I ' m doing Part 2 using a discrete event simulation ( DES ) . I ' m assuming there ' s a way to do this using a topological sort as well but I wanted to implement a DES . The basic idea of a DES is that you keep a priority queue of " events " . Each event is a * discrete event * and mutates the state of the world in some way . In my case the state of the world consists of how many workers are available ; the set of jobs that are unstarted started and satisfied ; and the time of the last completed job .
def part2() -> int:
    BASE_TIME = 60 if not test else 0
    WORKERS = 6 if not test else 2
    job_to_time = {c: BASE_TIME + ord(c) - ord("A") + 1 for c in string.ascii_uppercase}
    T = 0
    unstarted: Set[str] = set(ans_part1)
    satisfied: Set[str] = set()
    workers_available: int = 0
    started: Set[str] = set()
    Q: List[Tuple[int, str, str]] = [
        (0, "WORKER AVAILABLE", "") for _ in range(WORKERS)
    ]
    while Q:
        t, action, id_ = heapq.heappop(Q) 
        if action == "JOB DONE":
            started.remove(id_)  
            satisfied.add(id_)  
            T = max(T, t)  
        elif action == "WORKER AVAILABLE":
            workers_available += 1
        elif action == "WORKER START":
            if id_ in started:
                continue
            started.add(id_)
            unstarted.remove(id_)
            workers_available -= 1  
            heapq.heappush(Q, (t + job_to_time[id_], "JOB DONE", id_))
            heapq.heappush(Q, (t + job_to_time[id_], "WORKER AVAILABLE", ""))
        if workers_available > 0 and unstarted:
            for r in unstarted:
                issat = True
                for dep in STEP_DEPENDENCIES[r]:
                    if dep not in satisfied:
                        issat = False
                        break
                if issat:
                    heapq.heappush(Q, (t, "WORKER START", r))
                    break
    return T
def part2 ( ) -> int : BASE_TIME = 60 if not test else 0 WORKERS = 6 if not test else 2 job_to_time = { c : BASE_TIME + ord ( c ) - ord ( "A" ) + 1 for c in string . ascii_uppercase } T = 0 unstarted : Set [ str ] = set ( ans_part1 ) satisfied : Set [ str ] = set ( ) workers_available : int = 0 started : Set [ str ] = set ( ) Q : List [ Tuple [ int , str , str ] ] = [ ( 0 , "WORKER AVAILABLE" , "" ) for _ in range ( WORKERS ) ] while Q : t , action , id_ = heapq . heappop ( Q ) if action == "JOB DONE" : started . remove ( id_ ) satisfied . add ( id_ ) T = max ( T , t ) elif action == "WORKER AVAILABLE" : workers_available += 1 elif action == "WORKER START" : if id_ in started : continue started . add ( id_ ) unstarted . remove ( id_ ) workers_available -= 1 heapq . heappush ( Q , ( t + job_to_time [ id_ ] , "JOB DONE" , id_ ) ) heapq . heappush ( Q , ( t + job_to_time [ id_ ] , "WORKER AVAILABLE" , "" ) ) if workers_available > 0 and unstarted : for r in unstarted : issat = True for dep in STEP_DEPENDENCIES [ r ] : if dep not in satisfied : issat = False break if issat : heapq . heappush ( Q , ( t , "WORKER START" , r ) ) break return T
I'm doing Part 2 using a discrete event simulation (DES).
I ' m doing Part 2 using a discrete event simulation ( DES ) .
"""
    I'm doing Part 2 using a discrete event simulation (DES). I'm assuming there's a way
    to do this using a topological sort as well, but I wanted to implement a DES.

    The basic idea of a DES is that you keep a priority queue of "events". Each event is
    a *discrete event* and mutates the state of the world in some way. In my case, the
    state of the world consists of how many workers are available; the set of jobs that
    are unstarted, started, and satisfied; and the time of the last completed job.
    """ # Calculate the time it will take for each job to complete. For the full problem, # the time is 60 + (numeric letter of the alphabet, starting at 1) # World state # Create the event queue. The queue contains tuples of (time, action, ID) where time # is the time at which the event occurs (note that this is the primary sort key in # the priority queue), action is the action type, and ID is an optional arbitrary ID # (used for the JOB DONE event). # We start by populating the queue with an inital set of events: one event for each # worker becoming available at time = 0. # Loop through all of the events. # extract the next event # Handle the different actions # remove from the set of jobs currently being performed # add to the set of satisfied/completed jobs # update the max time # A worker has become available, increase the number of available workers. # Skip if already being worked on. # Start work on the job. # the worker is no longer available # Look up how long it will be until the job is completed. At that time, the # job will be done and the worker will become available. Add these events to # the queue. # Then, check if we can have a worker start on a job. # Can we actually do this job?