A street has a row of smart lamps. Every night each lamp looks at its own brightness and its two neighbours' brightness from the previous night and picks a new brightness with a shared rule(left, mine, right). The first and last lamp see 0 for the missing neighbour.
Each lamp's controller is slow (the rule call takes a while), so simulate the street with one thread per lamp, all working in parallel.
Implement simulate(start: list[int], nights: int, rule) -> list[int]:
- Start one thread per lamp. Lamp
i's thread makes all of lampi'srulecalls, one per night. - Night by night, every lamp must use its neighbours' values from the previous night, never a value a faster neighbour already computed for the current night.
- Return the brightness list after
nightsnights.
spread = lambda l, m, r: max(l, m, r)
simulate([0, 0, 5, 0, 0], 1, spread) # [0, 5, 5, 5, 0]
simulate([0, 0, 5, 0, 0], 2, spread) # [5, 5, 5, 5, 5]
simulate([3, 1], 0, spread) # [3, 1] no nights, no rule calls
With the fast lamps racing ahead, night 1 of the first example could wrongly become [0, 5, 5, 5, 5]: lamp 3 already shows 5 when lamp 4 looks at it. That is the bug the tests look for.
Constraints: 0 <= len(start) <= 20, 0 <= nights <= 50. rule may take different amounts of time on different calls. Don't sleep or busy-wait.
Show hint
Keep the current values and a separate "next night" list. Each thread writes only its own slot of the next list, then meets the others at a threading.Barrier(len(start), action=...): the action runs once, after all have written and before any is released, which is the moment to make "next" the new "current".