import copy

"""
--- Day 11: Radioisotope Thermoelectric Generators ---

You come upon a column of four floors that have been entirely sealed off from the rest of the building except for a
small dedicated lobby. There are some radiation warnings and a big sign which reads "Radioisotope Testing Facility".

According to the project status board, this facility is currently being used to experiment with Radioisotope
Thermoelectric Generators (RTGs, or simply "generators") that are designed to be paired with specially-constructed
microchips. Basically, an RTG is a highly radioactive rock that generates electricity through heat.

The experimental RTGs have poor radiation containment, so they're dangerously radioactive. The chips are prototypes and
don't have normal radiation shielding, but they do have the ability to generate an electromagnetic radiation shield when
powered. Unfortunately, they can only be powered by their corresponding RTG. An RTG powering a microchip is still
dangerous to other microchips.

In other words, if a chip is ever left in the same area as another RTG, and it's not connected to its own RTG, the chip
will be fried. Therefore, it is assumed that you will follow procedure and keep chips connected to their corresponding
RTG when they're in the same room, and away from other RTGs otherwise.

These microchips sound very interesting and useful to your current activities, and you'd like to try to retrieve them.
The fourth floor of the facility has an assembling machine which can make a self-contained, shielded computer for you to
take with you - that is, if you can bring it all of the RTGs and microchips.

Within the radiation-shielded part of the facility (in which it's safe to have these pre-assembly RTGs), there is an
elevator that can move between the four floors. Its capacity rating means it can carry at most yourself and two RTGs or
microchips in any combination. (They're rigged to some heavy diagnostic equipment - the assembling machine will detach
it for you.) As a security measure, the elevator will only function if it contains at least one RTG or microchip. The
elevator always stops on each floor to recharge, and this takes long enough that the items within it and the items on
that floor can irradiate each other. (You can prevent this if a Microchip and its Generator end up on the same floor in
this way, as they can be connected while the elevator is recharging.)

You make some notes of the locations of each component of interest (your puzzle input). Before you don a hazmat suit and
start moving things around, you'd like to have an idea of what you need to do.

When you enter the containment area, you and the elevator will start on the first floor.

For example, suppose the isolated area has the following arrangement:

The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.
The second floor contains a hydrogen generator.
The third floor contains a lithium generator.
The fourth floor contains nothing relevant.

As a diagram (F# for a Floor number, E for Elevator, H for Hydrogen, L for Lithium, M for Microchip, and G for
Generator), the initial state looks like this:

F4 .  .  .  .  .  
F3 .  .  .  LG .  
F2 .  HG .  .  .  
F1 E  .  HM .  LM 

Then, to get everything up to the assembling machine on the fourth floor, the following steps could be taken:

Bring the Hydrogen-compatible Microchip to the second floor, which is safe because it can get power from the Hydrogen
Generator:

F4 .  .  .  .  .  
F3 .  .  .  LG .  
F2 E  HG HM .  .  
F1 .  .  .  .  LM 

Bring both Hydrogen-related items to the third floor, which is safe because the Hydrogen-compatible microchip is getting
power from its generator:

F4 .  .  .  .  .  
F3 E  HG HM LG .  
F2 .  .  .  .  .  
F1 .  .  .  .  LM 

Leave the Hydrogen Generator on floor three, but bring the Hydrogen-compatible Microchip back down with you so you can
still use the elevator:

F4 .  .  .  .  .  
F3 .  HG .  LG .  
F2 E  .  HM .  .  
F1 .  .  .  .  LM 

At the first floor, grab the Lithium-compatible Microchip, which is safe because Microchips don't affect each other:

F4 .  .  .  .  .  
F3 .  HG .  LG .  
F2 .  .  .  .  .  
F1 E  .  HM .  LM 

Bring both Microchips up one floor, where there is nothing to fry them:

F4 .  .  .  .  .  
F3 .  HG .  LG .  
F2 E  .  HM .  LM 
F1 .  .  .  .  .  

Bring both Microchips up again to floor three, where they can be temporarily connected to their corresponding generators
while the elevator recharges, preventing either of them from being fried:

F4 .  .  .  .  .  
F3 E  HG HM LG LM 
F2 .  .  .  .  .  
F1 .  .  .  .  .  

Bring both Microchips to the fourth floor:

F4 E  .  HM .  LM 
F3 .  HG .  LG .  
F2 .  .  .  .  .  
F1 .  .  .  .  .  

Leave the Lithium-compatible microchip on the fourth floor, but bring the Hydrogen-compatible one so you can still use
the elevator; this is safe because although the Lithium Generator is on the destination floor, you can connect
Hydrogen-compatible microchip to the Hydrogen Generator there:

F4 .  .  .  .  LM 
F3 E  HG HM LG .  
F2 .  .  .  .  .  
F1 .  .  .  .  .  

Bring both Generators up to the fourth floor, which is safe because you can connect the Lithium-compatible Microchip to
the Lithium Generator upon arrival:

F4 E  HG .  LG LM 
F3 .  .  HM .  .  
F2 .  .  .  .  .  
F1 .  .  .  .  .  

Bring the Lithium Microchip with you to the third floor so you can use the elevator:

F4 .  HG .  LG .  
F3 E  .  HM .  LM 
F2 .  .  .  .  .  
F1 .  .  .  .  .  

Bring both Microchips to the fourth floor:

F4 E  HG HM LG LM 
F3 .  .  .  .  .  
F2 .  .  .  .  .  
F1 .  .  .  .  .  

In this arrangement, it takes 11 steps to collect all of the objects at the fourth floor for assembly. (Each elevator
stop counts as one step, even if nothing is added to or removed from it.)

In your situation, what is the minimum number of steps required to bring all of the objects to the fourth floor?

Your puzzle answer was 37.

--- Part Two ---

You step into the cleanroom separating the lobby from the isolated area and put on the hazmat suit.

Upon entering the isolated containment area, however, you notice some extra parts on the first floor that weren't listed
on the record outside:

An elerium generator.
An elerium-compatible microchip.
A dilithium generator.
A dilithium-compatible microchip.

These work just like the other generators and microchips. You'll have to get them up to assembly as well.

What is the minimum number of steps required to bring all of the objects, including these four new ones, to the fourth
floor?

Your puzzle answer was 61.

Both parts of this puzzle are complete! They provide two gold stars: **
"""


def is_valid(floor):
    has_generator = sum(x < 0 for x in floor)
    is_unpaired = sum(x > 0 and -x not in floor for x in floor)
    return not (has_generator and is_unpaired)


def single_floor_hash(floor):
    return str(len(floor)) + ''.join(str(sum(x < 0 for x in floor)))


def get_hash(elevator, floors):
    return str(elevator) + ''.join((single_floor_hash(floor) for floor in floors))


def move(elevator, floors, direction, from_idx, to_idx):
    if from_idx is not None and to_idx is not None:
        floors[elevator + direction].insert(to_idx, floors[elevator].pop(from_idx))


def explore_state(states, queue, req, elevator, floors, direction, moves, index1, index2=None):
    if direction * elevator < req:
        move(elevator, floors, direction, index2, 0)
        move(elevator, floors, direction, index1, 0)

        next_hash = get_hash(elevator + direction, floors)

        is_next_state_valid = is_valid(floors[elevator + direction]) and is_valid(floors[elevator])
        if next_hash not in states and floors[elevator + direction] and is_next_state_valid:
            queue.append([elevator + direction, copy.deepcopy(floors), moves + 1, next_hash])

        move(elevator + direction, floors, -direction, 0, index1)
        move(elevator + direction, floors, -direction, 0, index2)


def find_moves(start):
    states = set()
    queue = [[0, start, 0, get_hash(0, start)]]
    end_hash = get_hash(3, [[], [], [], sum(start, [])])

    while True:
        elevator, floors, moves, current_hash = queue.pop(0)

        if current_hash not in states:
            states.add(current_hash)

            if current_hash == end_hash:
                return moves

            for index1 in range(len(floors[elevator])):
                for index2 in range(index1 + 1, len(floors[elevator])):
                    explore_state(states, queue, 3, elevator, floors, 1, moves, index1, index2)
                    explore_state(states, queue, 0, elevator, floors, -1, moves, index1, index2)

                explore_state(states, queue, 3, elevator, floors, 1, moves, index1)
                explore_state(states, queue, 0, elevator, floors, -1, moves, index1)


if __name__ == "__main__":
    """
    The first floor contains a strontium generator, a strontium-compatible microchip, a plutonium generator, and a plutonium-compatible microchip.
    The second floor contains a thulium generator, a ruthenium generator, a ruthenium-compatible microchip, a curium generator, and a curium-compatible microchip.
    The third floor contains a thulium-compatible microchip.
    The fourth floor contains nothing relevant.
    """
    part1 = [
        ["SG", "SM", "PG", "PM"],
        ["TG", "RG", "RM", "CG", "CM"],
        ["TM"],
        []
    ]
    part1 = [
        [-1, 1, -2, 2],
        [-3, -4, 4, -5, 5],
        [3],
        []
    ]

    part2 = [
        ["SG", "SM", "PG", "PM", "EG", "EM", "DG", "DM"],
        ["TG", "RG", "RM", "CG", "CM"],
        ["TM"],
        []
    ]
    part2 = [
        [-1, 1, -2, 2, -6, 6, 7, -7],
        [-3, -4, 4, -5, 5],
        [3],
        []
    ]

    print(f"moves {find_moves(part1)}")
    print(f"moves {find_moves(part2)}")
