Advertisement
eliax1996

Untitled

Dec 20th, 2023
817
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.75 KB | None | 0 0
  1. import time
  2. from enum import Enum
  3. from dataclasses import dataclass
  4. from functools import cache
  5. from typing import Any
  6.  
  7.  
  8. class Signal(Enum):
  9.     LOW = "LOW"
  10.     HIGH = "HIGH"
  11.  
  12.     def __repr__(self):
  13.         return self.value.lower()
  14.  
  15.     def __str__(self):
  16.         return self.value.lower()
  17.  
  18.  
  19. class Component:
  20.     def apply(self, signal: Signal, index: int) -> Signal | None:
  21.         raise NotImplementedError()
  22.  
  23.     def state(self) -> Any:
  24.         raise NotImplementedError()
  25.  
  26.     def set_state(self, state: Any):
  27.         raise NotImplementedError()
  28.  
  29.  
  30. @dataclass
  31. class FlipFlop(Component):
  32.     name: str
  33.     active: bool = False
  34.  
  35.     def apply(self, signal: Signal, index: int) -> Signal | None:
  36.         if index != 0:
  37.             raise Exception(f"FlipFlop can only receive one signal, not {index}")
  38.  
  39.         match signal:
  40.             case Signal.LOW:
  41.                 self.active = not self.active
  42.                 if self.active:
  43.                     return Signal.HIGH
  44.                 else:
  45.                     return Signal.LOW
  46.             case _:
  47.                 return None
  48.  
  49.     def __hash__(self):
  50.         return hash(self.name)
  51.  
  52.     def state(self):
  53.         return self.active
  54.  
  55.     def set_state(self, state: Any):
  56.         self.active = state
  57.  
  58.     def __repr__(self):
  59.         return f"FlipFlop({self.name})"
  60.  
  61.  
  62. @dataclass
  63. class TestComponent(Component):
  64.     name: str
  65.  
  66.     def apply(self, signal: Signal, index: int) -> Signal | None:
  67.         return None
  68.  
  69.     def state(self):
  70.         return None
  71.  
  72.     def set_state(self, state: Any):
  73.         pass
  74.  
  75.     def __hash__(self):
  76.         return hash(self.name)
  77.  
  78.  
  79. @dataclass
  80. class Conjunction(Component):
  81.     name: str
  82.     memory: list[Signal]
  83.  
  84.     def apply(self, signal: Signal, index: int) -> Signal:
  85.         print(index)
  86.         self.memory[index] = signal
  87.         if all([signal == Signal.HIGH for signal in self.memory]):
  88.             return Signal.LOW
  89.         else:
  90.             return Signal.HIGH
  91.  
  92.     def __hash__(self):
  93.         return hash(self.name)
  94.  
  95.     def state(self):
  96.         return tuple(self.memory)
  97.  
  98.     def set_state(self, state: Any):
  99.         self.memory = list(state)
  100.  
  101.     def __repr__(self):
  102.         return f"Conjunction({self.name}: {self.memory})"
  103.  
  104.  
  105. @dataclass
  106. class Broadcaster(Component):
  107.     name: str = "broadcaster"
  108.  
  109.     def apply(self, signal: Signal, index: int) -> Signal:
  110.         if index != 0:
  111.             raise Exception("Broadcaster can only receive one signal")
  112.  
  113.         return signal
  114.  
  115.     def __hash__(self):
  116.         return hash("constant")
  117.  
  118.     def state(self):
  119.         return None
  120.  
  121.     def set_state(self, state: Any):
  122.         pass
  123.  
  124.     def __repr__(self):
  125.         return f"Broadcaster()"
  126.  
  127.  
  128. text = """
  129. %jr -> mq, xn
  130. %zl -> tz, cm
  131. &lh -> nr
  132. %hx -> jx, tz
  133. %cm -> tz, ls
  134. &fk -> nr
  135. broadcaster -> sj, pf, kh, cn
  136. %gz -> mq, lq
  137. %gb -> xf, kr
  138. %zc -> rq
  139. %ln -> qj, xf
  140. %gq -> pp
  141. %fb -> xf
  142. %pf -> tg, nv
  143. %bc -> cf
  144. &tz -> cn, fk, ls
  145. %cq -> fb, xf
  146. %rq -> tg, dx
  147. %km -> gq
  148. &mq -> gq, xn, fv, km, lh, xv, sj
  149. %zp -> mq, xv
  150. %jx -> tz, np
  151. &tg -> mm, rp, zc, pf, bc
  152. %cv -> sq, xf
  153. %nv -> ht, tg
  154. %sq -> gb
  155. %kr -> ln
  156. %dk -> cv
  157. %xn -> zp
  158. %sx -> xf, cq
  159. %zt -> tz, fq
  160. %dx -> tg, qn
  161. &ff -> nr
  162. %bn -> hx, tz
  163. %fj -> zt, tz
  164. %ht -> rr, tg
  165. %fq -> tz, bn
  166. %kh -> dk, xf
  167. %sj -> mq, fv
  168. %vm -> zl, tz
  169. &mm -> nr
  170. %rp -> bc
  171. %fh -> sx
  172. %ls -> fj
  173. %xz -> mq, gz
  174. %fv -> km
  175. &nr -> rx
  176. %lq -> mq
  177. %xv -> xz
  178. %cn -> tz, vm
  179. %pp -> jr, mq
  180. %hn -> tg
  181. %qn -> hn, tg
  182. %rr -> rp, tg
  183. %cf -> tg, zc
  184. %qj -> fh, xf
  185. &xf -> sq, dk, fh, ff, kh, kr
  186. %np -> tz
  187. """
  188.  
  189.  
  190. def parse_elem(elem: str):
  191.     if elem == "broadcaster":
  192.         return Broadcaster()
  193.     if elem.startswith("%"):
  194.         return FlipFlop(name=elem.strip("%"))
  195.     if elem.startswith("&"):
  196.         return Conjunction(name=elem.strip("&"), memory=[])
  197.     return TestComponent(name=elem)
  198.  
  199.  
  200. game = text.strip().split("\n")
  201.  
  202. topology = {}
  203. name_to_component = {}
  204.  
  205. for line in game:
  206.     source_, destinations_raw = line.split("->")
  207.     father = parse_elem(source_.strip())
  208.     children = [child.strip() for child in destinations_raw.split(",")]
  209.     topology[father.name] = children
  210.     name_to_component[father.name] = father
  211.  
  212. for children in topology.values():
  213.     for child in children:
  214.         if child not in name_to_component:
  215.             name_to_component[child] = parse_elem(child)
  216.  
  217. reversed_topology = {}
  218. for father, children in topology.items():
  219.     for child in children:
  220.         reversed_topology.setdefault(child, []).append(father)
  221.  
  222. for component_name, component in name_to_component.items():
  223.     if isinstance(component, Conjunction):
  224.         component.memory = list(Signal.LOW for child in reversed_topology[component_name])
  225.  
  226. from collections import deque
  227.  
  228.  
  229. class HDict(dict):
  230.     def __hash__(self):
  231.         return hash(frozenset(self.items()))
  232.  
  233.  
  234. @cache
  235. def compute_high_and_low_signals(
  236.         topology: HDict[str, list[str]],
  237.         name_to_component: HDict[str, Component],
  238.         components_state: HDict[str, Any],
  239.         next_signal: Signal,
  240. ) -> tuple[HDict[str, Any], tuple[int, int]]:
  241.     """
  242.    Given a signal, the topology and the current state of the components, compute the next state of the components, the unprocessed signals
  243.    and returns the number of low and high signals.
  244.    """
  245.  
  246.     #for component_name, component_state in components_state.items():
  247.     #    name_to_component[component_name].state = component_state
  248.  
  249.     to_forward = topology["broadcaster"]
  250.     print("computing new state")
  251.  
  252.     signal_queue = deque()
  253.  
  254.     high_signals = 0 if next_signal == Signal.LOW else 1
  255.     low_signals = 0 if next_signal == Signal.HIGH else 1
  256.  
  257.     for component_name in to_forward:
  258.         signal_queue.append((("broadcaster", component_name), next_signal))
  259.  
  260.     while signal_queue:
  261.         (source, destination), signal = signal_queue.popleft()
  262.  
  263.  
  264.         match signal:
  265.             case Signal.HIGH:
  266.                 high_signals += 1
  267.             case Signal.LOW:
  268.                 low_signals += 1
  269.             case _:
  270.                 pass
  271.  
  272.         print(f"{source} -{signal}-> {destination}")
  273.         component_state = name_to_component[destination]
  274.         index = reversed_topology[destination].index(source) if isinstance(component_state, Conjunction) else 0
  275.         emitted_signal = component_state.apply(signal, index)
  276.  
  277.         if emitted_signal is None:
  278.             continue
  279.  
  280.         for component_child in topology[destination]:
  281.             signal_queue.append(((destination, component_child), emitted_signal))
  282.  
  283.     return (
  284.         HDict(
  285.             dict((component, name_to_component[component].state()) for component in sorted(name_to_component.keys()))),
  286.         (high_signals, low_signals),
  287.     )
  288.  
  289.  
  290. def click_button(times: int = 1000):
  291.     frozen_topology = HDict((k, tuple(v)) for k, v in topology.items())
  292.     frozen_name_to_component = HDict(name_to_component)
  293.  
  294.     def compute_frozen_components_state():
  295.         return HDict(
  296.             (component, name_to_component[component].state()) for component in sorted(name_to_component.keys()))
  297.  
  298.     total_high_signals = 0
  299.     total_low_signals = 0
  300.     component_state_map = None
  301.     for i in range(times):
  302.         component_state_map, (high, low) = compute_high_and_low_signals(
  303.             frozen_topology,
  304.             frozen_name_to_component,
  305.             compute_frozen_components_state() if component_state_map is None else component_state_map,
  306.             Signal.LOW
  307.         )
  308.         total_high_signals += high
  309.         total_low_signals += low
  310.  
  311.     return total_high_signals, total_low_signals
  312.  
  313.  
  314. print(f"source to destination: {topology}")
  315. print(f"destination to source: {reversed_topology}")
  316.  
  317. high, low = click_button()
  318. print(high*low)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement