Advertisement
Guest User

Untitled

a guest
Dec 25th, 2021
637
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.03 KB | None | 0 0
  1. from collections import defaultdict
  2. import heapq
  3.  
  4. # ENUMERATORS
  5. E = 0
  6. A = 1
  7. B = 10
  8. C = 100
  9. D = 1000
  10. enum2char = {0: "E", 1: "A", 10: "B", 100: "C", 1000: "D"}
  11. enum2room = {1: 1, 10: 3, 100: 5, 1000: 7}
  12.  
  13. class Room:
  14.     def __init__(self):
  15.         # Type of room:
  16.         #  A, B, C, D are the rooms
  17.         #  E = hallway
  18.         self.type = E # A B C D E
  19.         self.pos = [E, E]
  20.         self.s = len(self.pos)
  21.  
  22.     def is_complete(self):
  23.         # Hallways shouldn't be checked if they are complete anyway
  24.         if self.type == E:
  25.             return True
  26.  
  27.         for crab in self.pos:
  28.             if crab != self.type:
  29.                 return False
  30.  
  31.         return True
  32.  
  33.     def has_space(self):
  34.         """Used on halways
  35.        """
  36.         if self.pos[0]:
  37.             return False
  38.  
  39.         return True
  40.  
  41.     def is_empty(self):
  42.         """Returns true if it is empty or if it is filled with correct type
  43.        crabs. If it is a hallway only the first element is checked.
  44.        """
  45.         for i in range(self.s):
  46.             # In case that the room is filled with the correct crabs,
  47.             # we ignore them
  48.             if self.pos[i] == self.type:
  49.                 continue
  50.  
  51.             if self.pos[i]:
  52.                 return False
  53.         return True
  54.  
  55.     def get_next(self):
  56.         # Return the next crab to move out. This should be called after
  57.         # checking if it is complete or in case of hallway, get the next crab.
  58.         # The i acts also as an offset for computing the number of steps.
  59.         for i in range(self.s):
  60.             if self.pos[i]:
  61.                 return self.pos[i], i
  62.  
  63.     def get_position(self):
  64.         """Should be called after if it empty to get the correct index of the
  65.        position.
  66.  
  67.        Returns the deepest level of move for current room. Can be used also
  68.        for halways.
  69.        """
  70.         for i in range(self.s - 1, -1, -1):
  71.             if not self.pos[i]:
  72.                 return i + 1
  73.  
  74.     def __eq__(self, other: 'Room'):
  75.         for j in range(self.s):
  76.             if self.pos[j] != other.pos[j]:
  77.                 return False
  78.         return True
  79.  
  80. class State:
  81.     def __init__(self, part_one=True):
  82.         self.rooms = []
  83.         if part_one:
  84.             self.room_depths = 2
  85.         else:
  86.             self.room_depths = 4
  87.         self.rooms_complete = 0
  88.  
  89.     def count_completed_rooms(self):
  90.         """Not only do we count completed rooms, we favor those that have the
  91.        expensive crabs already sorted in their correct room.
  92.        """
  93.         self.rooms_complete = 0
  94.         for i in [1, 3, 5, 7]:
  95.             if self.rooms[i].is_complete():
  96.                 self.rooms_complete += self.rooms[i].type
  97.  
  98.     def __lt__(self, other: 'State'):
  99.         # For heapq
  100.         # See who has more rooms complete
  101.         # Favor rooms that have more expensive crabs already tucked.
  102.  
  103.  
  104.         if self.rooms_complete < other.rooms_complete:
  105.             return True
  106.  
  107.         return False
  108.  
  109.     def create_rooms(self):
  110.         identifier = {1: A, 3:B, 5:C, 7:D}
  111.         for i in range(9):
  112.             room = Room()
  113.             if i % 2 == 0:
  114.                 room.type = E
  115.                 if i == 0 or i == 8:
  116.                     room.pos = [E, E]
  117.                     room.s = 2
  118.                 else:
  119.                     room.pos = [E]
  120.                     room.s = 1
  121.             else:
  122.                 room.type = identifier[i]
  123.                 room.pos = [identifier[i] for j in range(self.room_depths)]
  124.                 room.s = self.room_depths
  125.             self.rooms.append(room)
  126.  
  127.     def is_complete(self):
  128.         eq = True
  129.         for i in [1, 3, 5, 7]:
  130.             eq &= self.rooms[i].is_complete()
  131.  
  132.         return eq
  133.  
  134.     def __eq__(self, other: 'State'):
  135.         """Easier is to call complete, but to compare states this can be used
  136.        """
  137.  
  138.         eq = True
  139.         for i in [1, 3, 5, 7]:
  140.             eq &= self.rooms[i] == other.rooms[i]
  141.  
  142.         return eq
  143.  
  144.     def __hash__(self):
  145.         """Create a hash in the following way:
  146.  
  147.        HALL - ROOM - HALL - ROOM -...
  148.        """
  149.         string = ""
  150.         for room in self.rooms:
  151.             for pos in room.pos:
  152.                 string += enum2char[pos]
  153.         return hash(string)
  154.  
  155.     def __repr__(self):
  156.         vis = "\n"
  157.         vis += 13 * "#" + "\n"
  158.         # Collect the hallways
  159.         vis += "#"
  160.         vis += enum2char[self.rooms[0].pos[1]]
  161.         vis += enum2char[self.rooms[0].pos[0]]
  162.         vis += " "
  163.         for j in [2, 4, 6]:
  164.             vis += "".join([enum2char[self.rooms[j].pos[_]] for _ in range(len(self.rooms[j].pos))])
  165.             vis += " "
  166.         vis += enum2char[self.rooms[8].pos[0]]
  167.         vis += enum2char[self.rooms[8].pos[1]]
  168.         vis += "#\n"
  169.  
  170.         first = True
  171.         for i in range(self.room_depths):
  172.             if first:
  173.                 vis += "###"
  174.             else:
  175.                 vis += "  #"
  176.             vis += "#".join([enum2char[self.rooms[j].pos[i]] for j in [1, 3, 5, 7]])
  177.  
  178.             if first:
  179.                 vis += "###\n"
  180.                 first = False
  181.             else:
  182.                 vis += "#\n"
  183.         vis += "  " + 9*"#" + "\n"
  184.         return vis
  185.  
  186. def _deepcopy(state_obj: State):
  187.     new_obj = State()
  188.     new_obj.room_depths = state_obj.room_depths
  189.     for i in range(len(state_obj.rooms)):
  190.         room = Room()
  191.         room.type = state_obj.rooms[i].type
  192.         room.pos = state_obj.rooms[i].pos[:]
  193.         room.s = state_obj.rooms[i].s
  194.         new_obj.rooms.append(room)
  195.  
  196.     return new_obj
  197.  
  198. # Create states
  199. part_one = 0
  200.  
  201. target_state = State(part_one=part_one)
  202. target_state.create_rooms()
  203.  
  204. current_state = State(part_one=part_one)
  205. current_state.create_rooms()
  206.  
  207. # Part 1
  208. if part_one:
  209.     current_state.rooms[1].pos = [B, A]
  210.     current_state.rooms[3].pos = [C, D]
  211.     current_state.rooms[5].pos = [B, C]
  212.     current_state.rooms[7].pos = [D, A]
  213. # Part 2
  214. else:
  215.     current_state.rooms[1].pos = [B, D, D, A]
  216.     current_state.rooms[3].pos = [C, C, B, D]
  217.     current_state.rooms[5].pos = [B, B, A, C]
  218.     current_state.rooms[7].pos = [D, A, C, A]
  219.  
  220. HALLWAY_IND = [0, 2, 4, 6, 8]
  221. ROOMS_INDIC = [1, 3, 5, 7]
  222.  
  223. def get_next_states(state: State):
  224.     """Create new states, but prioritize the following:
  225.  
  226.    asdjkgnmweormelfkmw
  227.  
  228.    Prioritize nothing...
  229.  
  230.    """
  231.     out = []
  232.  
  233.     # First we check hallways.
  234.     for i in HALLWAY_IND:
  235.         # Check if the room has any crabs
  236.         hall = state.rooms[i]
  237.         if hall.is_empty():
  238.             continue
  239.  
  240.         # Get the crab
  241.         crab, crab_pos = hall.get_next()
  242.  
  243.         # Get target room
  244.         target_room = enum2room[crab]
  245.         if state.rooms[target_room].is_empty():
  246.             # Wait, first we need to see if we can move it to the room
  247.  
  248.             if i < target_room:
  249.                 # Hallway is on the left of the room
  250.                 left = i
  251.                 right = target_room
  252.             else:
  253.                 left = target_room
  254.                 right = i
  255.  
  256.             but_can_it_move = True
  257.             for j in range(left, right):
  258.                 if j % 2:
  259.                     continue
  260.                 if j == i:
  261.                     continue
  262.                 if state.rooms[j].has_space():
  263.                     continue
  264.                 but_can_it_move = False
  265.                 break
  266.  
  267.             if but_can_it_move:
  268.                 # We can move the crab!
  269.  
  270.                 new_state = _deepcopy(state)
  271.                 # Calculate the new cost
  272.                 # The path is the current position of the crab in the current
  273.                 # hallway, then the position in the target room and finaly
  274.                 # the move between the hallways and rooms
  275.                 target_position = state.rooms[target_room].get_position()
  276.                 move = abs(target_room - i)
  277.                 new_cost = (crab_pos + target_position + move) * crab
  278.  
  279.                 # Apply changes to the state
  280.                 new_state.rooms[i].pos[crab_pos] = E
  281.                 new_state.rooms[target_room].pos[target_position - 1] = crab
  282.                 new_state.count_completed_rooms()
  283.                 out.append((new_cost, new_state))
  284.  
  285.  
  286.     for i in ROOMS_INDIC:
  287.         # Check if room is complete
  288.         room = state.rooms[i]
  289.         if room.is_complete():
  290.             continue
  291.  
  292.         if room.is_empty():
  293.             continue
  294.  
  295.         # The room is not complete so we have to move the topmost crab out.
  296.         crab, crab_pos = room.get_next()
  297.  
  298.         # See where it has to go
  299.         target_room = enum2room[crab]
  300.  
  301.         # See if target room is empty so we can directly move in to the
  302.         # target room
  303.         if state.rooms[target_room].is_empty():
  304.             if i < target_room:
  305.                 left = i
  306.                 right = target_room
  307.             else:
  308.                 left = target_room
  309.                 right = i
  310.             but_can_it_move = True
  311.             for j in range(left, right):
  312.                 if j % 2:
  313.                     # Other rooms
  314.                     continue
  315.                 if j == i:
  316.                     continue
  317.                 if state.rooms[j].has_space():
  318.                     continue
  319.                 but_can_it_move = False
  320.                 break
  321.  
  322.             if but_can_it_move:
  323.                 new_state = _deepcopy(state)
  324.                 target_position = state.rooms[target_room].get_position()
  325.                 # Calculate the new state
  326.                 move = abs(target_room - i) + 1
  327.                 new_cost = (crab_pos + move + target_position) * crab
  328.  
  329.                 # Apply changes
  330.                 new_state.rooms[i].pos[crab_pos] = E
  331.                 new_state.rooms[target_room].pos[target_position - 1] = crab
  332.                 new_state.count_completed_rooms()
  333.                 out.append((new_cost, new_state))
  334.         # Well now let's see if we can move to a halway
  335.         for j in HALLWAY_IND:
  336.             # We fill all the hallways. All of them...
  337.             hall = state.rooms[j]
  338.             if hall.has_space():
  339.                 # We can move it here.
  340.                 but_can_it_move = True
  341.  
  342.                 if i < j:
  343.                     left = i
  344.                     right = j
  345.                 else:
  346.                     left = j
  347.                     right = i
  348.  
  349.                 for l in range(left, right):
  350.                     if l == j: # Ignore target hall
  351.                         continue
  352.                     if l % 2:  # Ignore rooms
  353.                         continue
  354.  
  355.                     if state.rooms[l].is_empty():
  356.                         continue
  357.  
  358.                     but_can_it_move = False
  359.                     break
  360.  
  361.                 if but_can_it_move:
  362.  
  363.                     # Fill all possible positions for this hallway.
  364.                     for k in range(hall.s -1, -1, -1):
  365.                         if hall.pos[k]:
  366.                             continue
  367.  
  368.                         new_state = _deepcopy(state)
  369.                         move = abs(i - j)
  370.  
  371.                         new_cost = (crab_pos + k + 1 + move) * crab
  372.  
  373.                         # Make the change
  374.                         new_state.rooms[i].pos[crab_pos] = E
  375.                         new_state.rooms[j].pos[k] = crab
  376.                         new_state.count_completed_rooms()
  377.                         out.append((new_cost, new_state))
  378.     return out
  379.  
  380. def dict_def_value():
  381.     return 100_000_000
  382.  
  383. risk_levels = defaultdict(dict_def_value)
  384. stack = [(0, current_state)]
  385.  
  386. while stack:
  387.     cost, state = heapq.heappop(stack)
  388.     if state.is_complete():
  389.         # We do not need to search more
  390.         print("Completed!")
  391.         break
  392.  
  393.     next_states = get_next_states(state)
  394.     for new_cost, new_state in next_states:
  395.         updated_cost = cost + new_cost
  396.  
  397.         if cost + new_cost < risk_levels[new_state]:
  398.             risk_levels[new_state] = updated_cost
  399.             heapq.heappush(stack, (updated_cost, new_state))
  400.  
  401. print(state)
  402. msg = "Part 2: "
  403. if part_one:
  404.     msg = "Part 1:"
  405.  
  406. print(msg, risk_levels[target_state])
  407.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement