Esempio n. 1
0
    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})
Esempio n. 2
0
    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()
Esempio n. 3
0
    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()
Esempio n. 4
0
    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())
Esempio n. 5
0
    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))
Esempio n. 6
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
Esempio n. 7
0
    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()
Esempio n. 8
0
    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
Esempio n. 9
0
    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))