def _wire_ends_from_rail_loc(self, loc, proto_board): """ Returns a list of the locations that can be connected by a wire with the given |loc| on the given |proto_board|, assuming that |loc| is a rail location. """ assert is_rail_loc(loc) r, c = loc # can draw vertical wires to every other location in the same column wire_ends = [(new_r, c) for new_r in ROWS] return filter(self._valid_not_occupied_filter(proto_board), wire_ends)
def get_children(self): children = [] proto_board, loc_pairs = self.state for i, (loc_1, loc_2, resistor, node) in enumerate(loc_pairs): # can extend a wire from |loc_1| towards |loc_2| from any of the # the locations |loc_1| is internally connected to for neighbor_loc in filter(proto_board.free, proto_board.locs_connected_to(loc_1)): # get candidate end locations for the new wire to draw wire_ends = (self._wire_ends_from_rail_loc(neighbor_loc, proto_board) if is_rail_loc(neighbor_loc) else self._wire_ends_from_body_loc(neighbor_loc, proto_board, span=dist(loc_1, loc_2))) for wire_end in wire_ends: # make sure that there is a wire to draw if wire_end == neighbor_loc: continue new_wire = Wire(neighbor_loc, wire_end, node) # track number of pieces this wire crosses num_piece_crossings = sum(piece.crossed_by(new_wire) for piece in proto_board.get_pieces()) if not ALLOW_PIECE_CROSSINGS and num_piece_crossings: continue # track number of other wires this new wire crosses any_same_orientation_crossings = False num_wire_crossings = 0 for wire in proto_board.get_wires(): if wire.crosses(new_wire): if wire.vertical() == new_wire.vertical(): any_same_orientation_crossings = True break else: num_wire_crossings += 1 # don't allow any wire crossings where the two wires have the same # orientation if any_same_orientation_crossings: continue # continue if we do not want to allow any crossing wires at all, even # ones of different orientation if not ALLOW_WIRE_CROSSINGS and num_wire_crossings: continue # construct a proto board with this new wire wire_proto_board = proto_board.with_wire(new_wire) # check that adding the wire is reasonable wire_proto_board_valid = (wire_proto_board and wire_proto_board is not proto_board) # potentially create another proto board with a resistor in place of # the new wire add_resistor = (resistor is not None and not num_piece_crossings and not num_wire_crossings and new_wire.length() == 3) if add_resistor: n1, n2, r, label = (resistor.n1, resistor.n2, resistor.r, resistor.label) # find representatives for the two nodes, they better be there n1_group = proto_board.rep_for(n1) assert n1_group n2_group = proto_board.rep_for(n2) assert n2_group # check that wire loc 1 is in the same group as node n1 assert proto_board.rep_for(new_wire.loc_1) == n1_group # find representative for wire loc 2, might not be there wire_loc_2_group = proto_board.rep_for(new_wire.loc_2) # if it is there, it better be the same as n2_group, or we can't put # a resistor here if (not wire_loc_2_group) or wire_loc_2_group == n2_group: # figure out correct orientation of Resistor_Piece n1 and n2 vertical = new_wire.vertical() if new_wire.c_1 < new_wire.c_2 or new_wire.r_1 < new_wire.r_2: resistor_piece = Resistor_Piece(n1, n2, r, vertical, label) resistor_piece.top_left_loc = new_wire.loc_1 else: resistor_piece = Resistor_Piece(n2, n1, r, vertical, label) resistor_piece.top_left_loc = new_wire.loc_2 # create proto board with resistor new_proto_board = proto_board.with_piece(resistor_piece) if not wire_loc_2_group: # ensure to mark the oposite location with its apporpriate node new_proto_board = new_proto_board.with_loc_repped(n2_group, new_wire.loc_2) # would no longer need a resistor for this pair of locations new_resistor = None # and the node for the rest of the wires will be the node for the # other end of the resistor new_node = n2 else: # placing the resistor would create a violating connection add_resistor = False if not add_resistor: if wire_proto_board_valid: # can still put a wire down if allowed new_proto_board = wire_proto_board new_resistor = resistor new_node = node else: continue # we have a candidate proto board, compute state and cost new_loc_pairs = list(loc_pairs) new_cost = self.cost + new_wire.length() if proto_board.connected(wire_end, loc_2): new_loc_pairs.pop(i) # favor connectedness a lot new_cost -= 100 else: new_loc_pairs[i] = (wire_end, loc_2, new_resistor, new_node) # penalize long wires new_cost += new_wire.length() # penalize many wires new_cost += 10 # penalize wires crossing pieces (if allowed at all) new_cost += 1000 * num_piece_crossings # penalize crossing wires of opposite orientation (if allowed at all) new_cost += 1000 * num_wire_crossings # favor wires that get us close to the goal, and penalize wires # that get us farther away new_cost += dist(wire_end, loc_2) - dist(loc_1, loc_2) children.append(Proto_Board_Search_Node(new_proto_board, frozenset(new_loc_pairs), self, new_cost, self.filter_wire_lengths)) # if added a resistor, also create a proto board where we use a wire # instead of the resistor if add_resistor and wire_proto_board_valid: wire_loc_pairs = list(loc_pairs) wire_loc_pairs[i] = (wire_end, loc_2, resistor, node) children.append(Proto_Board_Search_Node(wire_proto_board, frozenset(wire_loc_pairs), self, new_cost, self.filter_wire_lengths)) return children
def get_children(self): children = [] proto_board, loc_pairs = self.state for i, (loc_1, loc_2, resistor, node) in enumerate(loc_pairs): # can extend a wire from |loc_1| towards |loc_2| from any of the # the locations |loc_1| is internally connected to for neighbor_loc in filter(proto_board.free, proto_board.locs_connected_to(loc_1)): # get candidate end locations for the new wire to draw wire_ends = ( self._wire_ends_from_rail_loc(neighbor_loc, proto_board) if is_rail_loc(neighbor_loc) else self._wire_ends_from_body_loc( neighbor_loc, proto_board, span=dist(loc_1, loc_2))) for wire_end in wire_ends: # make sure that there is a wire to draw if wire_end == neighbor_loc: continue new_wire = Wire(neighbor_loc, wire_end, node) # track number of pieces this wire crosses num_piece_crossings = sum( piece.crossed_by(new_wire) for piece in proto_board.get_pieces()) if not ALLOW_PIECE_CROSSINGS and num_piece_crossings: continue # track number of other wires this new wire crosses any_same_orientation_crossings = False num_wire_crossings = 0 for wire in proto_board.get_wires(): if wire.crosses(new_wire): if wire.vertical() == new_wire.vertical(): any_same_orientation_crossings = True break else: num_wire_crossings += 1 # don't allow any wire crossings where the two wires have the same # orientation if any_same_orientation_crossings: continue # continue if we do not want to allow any crossing wires at all, even # ones of different orientation if not ALLOW_WIRE_CROSSINGS and num_wire_crossings: continue # construct a proto board with this new wire wire_proto_board = proto_board.with_wire(new_wire) # check that adding the wire is reasonable wire_proto_board_valid = (wire_proto_board and wire_proto_board is not proto_board) # potentially create another proto board with a resistor in place of # the new wire add_resistor = (resistor is not None and not num_piece_crossings and not num_wire_crossings and new_wire.length() == 3) if add_resistor: n1, n2, r, label = (resistor.n1, resistor.n2, resistor.r, resistor.label) # find representatives for the two nodes, they better be there n1_group = proto_board.rep_for(n1) assert n1_group n2_group = proto_board.rep_for(n2) assert n2_group # check that wire loc 1 is in the same group as node n1 assert proto_board.rep_for(new_wire.loc_1) == n1_group # find representative for wire loc 2, might not be there wire_loc_2_group = proto_board.rep_for(new_wire.loc_2) # if it is there, it better be the same as n2_group, or we can't put # a resistor here if (not wire_loc_2_group ) or wire_loc_2_group == n2_group: # figure out correct orientation of Resistor_Piece n1 and n2 vertical = new_wire.vertical() if new_wire.c_1 < new_wire.c_2 or new_wire.r_1 < new_wire.r_2: resistor_piece = Resistor_Piece( n1, n2, r, vertical, label) resistor_piece.top_left_loc = new_wire.loc_1 else: resistor_piece = Resistor_Piece( n2, n1, r, vertical, label) resistor_piece.top_left_loc = new_wire.loc_2 # create proto board with resistor new_proto_board = proto_board.with_piece( resistor_piece) if not wire_loc_2_group: # ensure to mark the oposite location with its apporpriate node new_proto_board = new_proto_board.with_loc_repped( n2_group, new_wire.loc_2) # would no longer need a resistor for this pair of locations new_resistor = None # and the node for the rest of the wires will be the node for the # other end of the resistor new_node = n2 else: # placing the resistor would create a violating connection add_resistor = False if not add_resistor: if wire_proto_board_valid: # can still put a wire down if allowed new_proto_board = wire_proto_board new_resistor = resistor new_node = node else: continue # we have a candidate proto board, compute state and cost new_loc_pairs = list(loc_pairs) new_cost = self.cost + new_wire.length() if proto_board.connected(wire_end, loc_2): new_loc_pairs.pop(i) # favor connectedness a lot new_cost -= 100 else: new_loc_pairs[i] = (wire_end, loc_2, new_resistor, new_node) # penalize long wires new_cost += new_wire.length() # penalize many wires new_cost += 10 # penalize wires crossing pieces (if allowed at all) new_cost += 1000 * num_piece_crossings # penalize crossing wires of opposite orientation (if allowed at all) new_cost += 1000 * num_wire_crossings # favor wires that get us close to the goal, and penalize wires # that get us farther away new_cost += dist(wire_end, loc_2) - dist(loc_1, loc_2) children.append( Proto_Board_Search_Node(new_proto_board, frozenset(new_loc_pairs), self, new_cost, self.filter_wire_lengths)) # if added a resistor, also create a proto board where we use a wire # instead of the resistor if add_resistor and wire_proto_board_valid: wire_loc_pairs = list(loc_pairs) wire_loc_pairs[i] = (wire_end, loc_2, resistor, node) children.append( Proto_Board_Search_Node(wire_proto_board, frozenset(wire_loc_pairs), self, new_cost, self.filter_wire_lengths)) return children