def testIOOutputFirst(self): self.board.set((0, 2), Wire()) self.board.set((0, 0), Wire()) wire = self.board.get((1, 2)) self.assertEqual(self.nand.inputs, {wire}) self.assertEqual(wire.inputs, {self.nand})
def _surround_bridge_with_simnodes(self): # Attach NAND/Wire horizontally self.board.set((2, 2), Wire()) self.board.set((4, 2), Board.deserialize_simnode('r')) # Attach Switch/Wire vertically self.board.set((3, 1), Wire()) self.board.set((3, 3), Board.deserialize_simnode('o')) self.board.tick() self.board.tick()
def _surround_bridge_with_wires(self): # Place wire around the bridge new_wires = [util.add(self.bridge_coords, nc) for nc in util.neighbour_deltas()] for w in new_wires: self.board.set(w, Wire()) self.board.tick()
def setUp(self): board_str = ("---\n" "d..\n" "---\n") self.board = Board.deserialize(board_str) self.nand = self.board.get((0, 1)) # Dynamically finish the loop self.board.set((2, 1), Wire())
def setUp(self): board_str = ("-R-.-") self.board = Board.deserialize(board_str) self.nand = self.board.get((1, 0)) # Join the two wires self.new_wire = Wire() self.board.set((3, 0), self.new_wire) self.placed_wire = self.board.get((3, 0))
def _grid_local_wire_join(self, coords, new_wire: Wire): """Ensures all wires in the wire group at the given coords consist of the same object, and that inputs/outputs are correct. Parameters ---------- coords : tuple A tuple of grid coordinates to join at. new_wire : Wire The new wire to replace the entire group with. """ neighbouring_wires = [ sn for sn in self.neighbour_objs(coords) if isinstance(sn, Wire) ] new_wire.signal = any([w.output() for w in neighbouring_wires]) old_input_sets = [w.inputs for w in neighbouring_wires] # The new inputs are a union of all the old input sets new_wire.inputs = set().union(*old_input_sets) dirty_simnodes = self._recursive_wire_replace(coords, new_wire) # Update IO for all dirty_simnodes for coord, n in dirty_simnodes.items(): n.recalculate_io(coord, self) # Make sure the new wires immediately show the correct value new_wire.calculate_next_output() new_wire.tick() return new_wire
def _grid_local_wire_break(self, coords, broken_wire: Wire): """Break a wire into multiple bits if required Parameters ---------- coords : (x, y) Coordinates of where the wire group was broken. broken_wire : Wire The wire object which was broken. """ current_obj = self.get(coords) assert (not isinstance(current_obj, Wire)) # Note: This can work even if there are no wires... Just marks all the # neighbours as dirty. new_wires = set() dirty_simnodes = {} # coord -> obj for nd in util.neighbour_deltas(): nc = util.add(coords, nd) # Use coords to avoid mutating what we are iterating over _, nc, n = self.into(nc, nd) if n is broken_wire: new_wire = Wire() dirty_simnodes.update( self._recursive_wire_replace(nc, new_wire)) # Update this later - Can't do here because we need to wait for # dirty simnodes to refresh new_wires.add(new_wire) elif n is not None: dirty_simnodes[nc] = n # Update IO for all dirty_simnodes for coord, n in dirty_simnodes.items(): n.recalculate_io(coord, self) # Make sure the new wires immediately show the correct value for wire in new_wires: wire.calculate_next_output() wire.tick()
def _grid_global_wire_join(self): """Globally reevaluates the grid and performs low-level wire joins. Only used for debugging or in deserialization. Does NOT fix inputs/outputs. Performs connected-component labeling to find groups of wires https://en.wikipedia.org/wiki/Connected-component_labeling#Two-pass """ # Map of tiles to labels tile_lookup = {} # Map of labels to tiles label_lookup = {} # Set of (label, label) connections for later merging connections = set() for y in range(len(self.grid)): for x in range(len(self.grid[y])): me = (x, y) tile = self.grid[y][x] if not isinstance(tile, Wire): continue # Typically we only look directly above/left of the current # tile, but wire bridges throw a spanner in the works, so we # will need to traverse them dx = x - 1 dy = y - 1 while isinstance(self.get((dx, y)), WireBridge): dx -= 1 while isinstance(self.get((x, dy)), WireBridge): dy -= 1 left = tile_lookup.get((dx, y)) top = tile_lookup.get((x, dy)) neighbouring_labels = [x for x in [left, top] if x is not None] try: my_label = min(neighbouring_labels) if len(set(neighbouring_labels)) == 2: connections.add((my_label, max(neighbouring_labels))) except ValueError: # Looks like there are no neighbouring labels, so this is a # new group. my_label = len(label_lookup) # Add myself into the tile_lookup tile_lookup[me] = my_label # Add myself into the label_lookup label_lookup[my_label] = label_lookup.get(my_label, []) + [me] logger.debug('-- PRE-MERGES --') logger.debug(f'The grid before merges: {tile_lookup}') logger.debug(f'Connection list: {connections}') def find(data, i): if i != data[i]: data[i] = find(data, data[i]) return data[i] def union(data, i, j): pi, pj = find(data, i), find(data, j) if pi != pj: data[pi] = pj data = list(range(len(label_lookup))) # Perform all the unions in the connection list for (i, j) in connections: union(data, i, j) for i in range(len(label_lookup)): # Beware that this `find` mutates `data`! # Must `find` each element once first if you want to operate on the # list directly. group = find(data, i) if i != group: label_lookup[group] = label_lookup[group] + label_lookup[i] for t in label_lookup[i]: tile_lookup[t] = group del label_lookup[i] # Component labelling complete! # Now we can do what we came here for - Let's replace all the wires so # that each group is made up of the same Wire object. for label, coords in label_lookup.items(): logger.debug(f'Wire group: {label} is made from coords: {coords}') wire = Wire() for (x, y) in coords: # Low-level wire replace. self.grid[y][x] = wire
def setUp(self): board_str = ("-R-r-") self.board = Board.deserialize(board_str) self.board.set((3, 0), Wire()) self.nand = self.board.get((1, 0))