Dequeue Python
Dequeue Python
def parse_input(input_str):
"""
Parses the input string into the initial state and the search algorithm.
Parameters:
input_str (str): The input string in the format '#C#C#C#C-X'.
Returns:
tuple: A tuple containing the initial state string and the search algorithm
character.
"""
try:
state_part, algo = input_str.strip().split('-')
# Validate the algorithm choice
if algo not in ['b', 'a']:
raise ValueError("Algorithm must be 'b' for BFS or 'a' for A*.")
# Validate the state length
if len(state_part) != 8:
raise ValueError("State must be 8 characters long (e.g., '1w2b3w4b').")
# Validate each pancake part
for i in range(0, 8, 2):
if not state_part[i].isdigit() or state_part[i+1] not in ['w', 'b']:
raise ValueError("Each pancake must have an ID followed by 'w' or
'b'.")
return state_part, algo
except ValueError as ve:
print(f"Invalid input format: {ve}")
sys.exit(1)
Parameters:
state (str): The current state string.
position (int): The position to flip at (1-based index).
Returns:
str: The new state after the flip.
"""
# Extract pancakes as list of tuples (id, side)
pancakes = [state[i:i+2] for i in range(0, len(state), 2)]
# Flip the top 'position' pancakes
flipped = pancakes[:position]
flipped = flipped[::-1] # Reverse the order
# Flip the side of each pancake
flipped = [pancake[0] + ('w' if pancake[1] == 'b' else 'b') for pancake in
flipped]
new_pancakes = flipped + pancakes[position:]
return ''.join(new_pancakes)
def heuristic(state):
"""
Heuristic function for A* search: the ID of the largest pancake that is still
out of order.
Parameters:
state (str): The current state string.
Returns:
int: The heuristic value.
"""
# Extract the pancake IDs by ignoring orientation
ids = [int(state[i*2]) for i in range(4)]
# Iterate from largest to smallest pancake
for i in range(3, -1, -1):
if ids[i] != i+1:
return i+1 # ID of the largest out-of-order pancake
return 0 # All pancakes are in correct order
def tie_breaker(state):
"""
Converts the state string to an 8-digit number for tie-breaking.
'w' is replaced with '1' and 'b' with '0'.
Parameters:
state (str): The state string.
Returns:
int: The numerical representation for tie-breaking.
"""
num = ''
for i in range(4):
num += state[i*2]
num += '1' if state[i*2+1] == 'w' else '0'
return int(num)
def bfs(initial_state):
"""
Performs Breadth-First Search to find the solution.
Parameters:
initial_state (str): The initial state string.
Returns:
list: A list of states representing the solution path.
"""
# Initialize the fringe as a queue
fringe = deque()
fringe.append(initial_state)
# Initialize the closed set to keep track of visited states
closed_set = set()
# Dictionary to keep track of parent states for path reconstruction
parent = {initial_state: None}
while fringe:
current = fringe.popleft()
if current in closed_set:
continue
closed_set.add(current)
if current == GOAL_STATE:
# Reconstruct the path from goal to start
path = []
while current:
path.append(current)
current = parent[current]
return path[::-1] # Reverse to get path from start to goal
def a_star(initial_state):
"""
Performs A* Search to find the solution.
Parameters:
initial_state (str): The initial state string.
Returns:
list: A list of tuples representing the solution path with g and h values.
"""
# Initialize the fringe as a priority queue (min-heap)
fringe = []
# Heap elements are tuples: (f, tie_breaker, g, state)
initial_h = heuristic(initial_state)
heapq.heappush(fringe, (initial_h, tie_breaker(initial_state), 0,
initial_state))
# Dictionary to keep track of the lowest g cost for each state
g_costs = {initial_state: 0}
# Dictionary to keep track of parent states and the flip that led to them
parent = {initial_state: (None, None)} # state: (parent_state, flip_position)
while fringe:
f, tb, g, current = heapq.heappop(fringe)
if current == GOAL_STATE:
# Reconstruct the path with g and h values
path = []
state = current
while state:
prev, flip_pos = parent[state]
current_g = g_costs[state]
current_h = heuristic(state)
path.append((state, current_g, current_h))
state = prev
return path[::-1] # Reverse to get path from start to goal
def reconstruct_a_star_path(path):
"""
Constructs the output path with flip positions, g, and h values for A*.
Parameters:
path (list): The list of tuples containing state, g, and h.
Returns:
list: Formatted output strings showing the flip positions with g and h.
"""
output = []
for i in range(len(path)-1):
current, g, h = path[i]
next_state, _, _ = path[i+1]
flip_pos = determine_flip_position(current, next_state)
formatted_state = insert_flip_marker(current, flip_pos)
output.append(f"{formatted_state} g:{g}, h:{h}")
# Add the goal state with its g and h
goal_state, final_g, final_h = path[-1]
output.append(f"{goal_state} g:{final_g}, h:{final_h}")
return output
def reconstruct_bfs_path(path):
"""
Constructs the output path with flip positions for BFS.
Parameters:
path (list): The list of states from BFS.
Returns:
list: Formatted output strings showing the flip positions.
"""
output = []
for i in range(len(path)-1):
current = path[i]
next_state = path[i+1]
flip_pos = determine_flip_position(current, next_state)
formatted_state = insert_flip_marker(current, flip_pos)
output.append(formatted_state)
# Add the goal state
output.append(path[-1])
return output
Parameters:
current (str): The current state string.
next_state (str): The next state string.
Returns:
int: The flip position where the state changes.
"""
for i in range(4):
current_pancake = current[i*2:(i+1)*2]
next_pancake = next_state[i*2:(i+1)*2]
if current_pancake != next_pancake:
return i + 1 # Positions are 1-based
return 4 # Default to flipping all if no difference found
Parameters:
state (str): The state string.
flip_position (int): The flip position (1-based index).
Returns:
str: The formatted state string with the flip marker.
"""
pos = flip_position * 2
return state[:pos] + '|' + state[pos:]
def main():
"""
The main function to execute the Burnt Pancake problem solution.
"""
# Read input from the user
input_str = input().strip()
initial_state, algo = parse_input(input_str)
if algo == 'b':
# Perform BFS
solution_path = bfs(initial_state)
if not solution_path:
print("No solution found using BFS.")
sys.exit(1)
formatted_output = reconstruct_bfs_path(solution_path)
elif algo == 'a':
# Perform A* Search
solution_path = a_star(initial_state)
if not solution_path:
print("No solution found using A* Search.")
sys.exit(1)
formatted_output = reconstruct_a_star_path(solution_path)
else:
print("Invalid algorithm choice. Use 'b' for BFS or 'a' for A*.")
sys.exit(1)
if __name__ == "__main__":
main()