0% found this document useful (0 votes)
64 views36 pages

Ai Case T4

The document presents a case study on the Tower of Hanoi problem, focusing on its application in artificial intelligence. It outlines the rules of the puzzle, various AI techniques for solving it, and includes algorithm implementations such as Depth-First Search, Greedy Algorithm, and Hill Climbing. The study emphasizes the educational value of the Tower of Hanoi in teaching fundamental and advanced AI concepts through creative problem-solving tasks.

Uploaded by

dhanush dsouza
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
64 views36 pages

Ai Case T4

The document presents a case study on the Tower of Hanoi problem, focusing on its application in artificial intelligence. It outlines the rules of the puzzle, various AI techniques for solving it, and includes algorithm implementations such as Depth-First Search, Greedy Algorithm, and Hill Climbing. The study emphasizes the educational value of the Tower of Hanoi in teaching fundamental and advanced AI concepts through creative problem-solving tasks.

Uploaded by

dhanush dsouza
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

TOWER OF HANOI

CASE STUDY ON
ARTIFICIAL INTELLIGENCE
CODE : 2031

BY
TEAM-4

MRIDUL DAS VU22CSEN0102221


SATVIK KATYAYAN VU22CSEN0101577
M.L.V.N.S MANJUNATH VU22CSEN0100581
K.KARTHIKEYA VARMA VU22CSEN0100G27
S. Kishore VU22CSEN0400292

UNDER THE GUIDANCE OF:


Dr. D. Chandrika
Mr. V.S.V.S Murthy
Introduction:
The Tower of Hanoi (also called the problem of Benares Temple, Tower of
Brahma or Lucas Tower, and sometimes pluralized as Towers, or simply
pyramid puzzle), is a mathematical game or puzzle consisting of three rods
and a number of disks of various diameters, which can slide onto any rod.
The puzzle begins with the disks stacked on one rod in order of decreasing
size, the smallest at the top, thus approximating a conical shape. The
objective of the puzzle is to move the entire stack to one of the other rods,
obeying the following rules:

Only one disk may be moved at a time.


Each move consists of taking the upper disk from one of the stacks and
placing it on top of another stack or on an empty rod.
No disk may be placed on top of a disk that is smaller than it.
With three disks, the puzzle can be solved in seven moves. The minimal
number of moves required to solve a Tower of Hanoi puzzle is 2n − 1, where
n is the number of disks.

Problem Description:
The puzzle consists of:
• Three pegs (rods): These are labeled as Source (A),
Auxiliary (B), and Destination (C).
• N disks: These are of varying sizes, stacked in descending
order (largest at the bottom and smallest at the top) on the
source peg.
The objective is to move all the disks from the Source peg to the
Destination peg while adhering to the following rules:
1. Only one disk can be moved at a time.
2. A larger disk cannot be placed on top of a smaller disk.
3. All disks must be moved using the auxiliary peg.

Here’s a Towers of Hanoi-themed case study with 14 creative


subtasks aligned with AI concepts. These tasks explore problem-
solving techniques while maintaining the constraints of moving
disks between three pegs. Each subtask includes innovative
extensions to enhance engagement and learning.
The Towers of Hanoi-themed AI case study is a classic yet
innovative choice for BTech students, offering a blend of
foundational learning and advanced AI applications. Here's why it
is an excellent case study:
1. Timeless and Engaging Problem:
o The Towers of Hanoi is a well-known and engaging
puzzle, making it approachable for students while still
being intellectually challenging.
o The problem’s simple rules and clear objectives
provide an excellent starting point for exploring
complex AI concepts.
2. Comprehensive Use of AI Techniques:
o The case study explores a wide range of AI
methodologies, including uninformed search (BFS,
DFS, UCS), informed search (A*, Greedy), local search
(hill climbing, simulated annealing), and optimization
(genetic algorithms).
o Advanced topics like adversarial search, constraint
satisfaction problems (CSP), and first-order logic are
incorporated, broadening the scope of learning.
3. Dynamic and Realistic Scenarios:
o Introduces extensions such as unavailable pegs, time
constraints, and competitive settings, adding depth
and making the scenarios dynamic and realistic.
o Encourages adaptability and creativity through evolving
constraints and adversarial scenarios.
4. Scalability and Flexibility:
o Tasks range from beginner-friendly implementations
(e.g., BFS for shortest moves) to advanced challenges
(e.g., genetic algorithms for solution evolution),
catering to various expertise levels.
o The inclusion of dynamic extensions allows for
increased complexity and further exploration.
5. Interdisciplinary Learning:
o Combines AI with logic, optimization, and game theory,
showcasing interdisciplinary problem-solving
applications.
o Encourages students to apply theoretical knowledge in
practical, creative ways.

PEAS (Performance Measure, Environment, Actuators,


Sensors) for Tower of Hanoi:
Elements Description
Performance Minimum number of moves to transfer all
disks while following the rules

Environment Three rods (A, B, C) and a set of disks in


increasing order on the initial rod

Actuators The ability to pick up a disk and place it on


another rod

Sensors Ability to detect the current arrangement of


disks on the rods

Task Environment Properties:


Observable: Fully observable since the agent has complete
knowledge of the state.
Deterministic: Yes, the result of each action is known.
Episodic or Sequential: Sequential, as each action affects future
actions.
Static or Dynamic: Static; no changes occur in the environment
except those caused by the agent.
Discrete or Continuous: Discrete; there are a limited number of
states and moves.
Single-Agent or Multi-Agent: Single-agent.

Problem Definition:
Initial State: All disks on Rod A in increasing order, Rods B and C
empty.
Action: Move the top disk from one rod to another valid rod.
Transition Model: Describes the effect of an action on the state.
A disk moves from source rod to destination rod, updating the
state.
Goal State: All disks are moved to the target rod in the same
order.
Path Cost: The cost is the number of moves taken to reach the
goal. The number of moves required, optimally 2^n−1.

3. Algorithm Implementation:
A. Depth-First Search (DFS) Approach
DFS explores the solution tree by going as deep as possible into
one path before backtracking. It is implemented using recursion
or a stack.
Steps for DFS in Tower of Hanoi:
1. Move (n-1) disks from source to auxiliary rod.
2. Move the nth (largest) disk to the destination rod.
3. Move (n-1) disks from the auxiliary rod to the destination
rod.
Complexity:
• DFS is not optimal since it may explore unnecessary paths.
• Time Complexity: O(2n)O(2^n)O(2n) (Exponential)
• Space Complexity: O(n)O(n)O(n) (Recursion depth)

Code Implementation:
def tower_of_hanoi(n, source, auxiliary, target, moves):
if n == 1:
moves.append((source, target))
return
tower_of_hanoi(n - 1, source, target, auxiliary, moves)
moves.append((source, target))
tower_of_hanoi(n - 1, auxiliary, source, target, moves)

def solve_hanoi(n):
moves = []
tower_of_hanoi(n, 'A', 'B', 'C', moves)
return moves

n=3
result = solve_hanoi(n)

for move in result:


print(f"Move disk from {move[0]} to {move[1]}")
B. Greedy Algorithm Approach
A greedy algorithm makes the locally optimal choice at each step
with the hope of reaching the global solution.
Greedy Approach for Tower of Hanoi:
• Always move the smallest available disk to the next best
position.
• Move alternately between the smallest disk and another
valid move.
Limitations of Greedy Approach in Tower of Hanoi:
• It may not always be optimal, as it does not backtrack
when needed.
• Can lead to unnecessary moves compared to DFS.

Code Implementation:
def iterative_hanoi(n):
source, auxiliary, target = 'A', 'B', 'C'
moves = []
total_moves = 2 ** n - 1
rods = {source: list(range(n, 0, -1)), auxiliary: [], target: []}
if n % 2 == 0:
target, auxiliary = auxiliary, target
for move in range(1, total_moves + 1):
from_rod, to_rod = None, None
if move % 3 == 1:
from_rod, to_rod = source, target
elif move % 3 == 2:
from_rod, to_rod = source, auxiliary
else:
from_rod, to_rod = auxiliary, target
if rods[from_rod] and (not rods[to_rod] or rods[from_rod][-1] < rods[to_rod][-1]):
rods[to_rod].append(rods[from_rod].pop())
moves.append((from_rod, to_rod))
else:
rods[from_rod].append(rods[to_rod].pop())
moves.append((to_rod, from_rod))
return moves

n=3
result = iterative_hanoi(n)

for move in result:


print(f"Move disk from {move[0]} to {move[1]}")
C. Depth-Limited Search

import matplotlib.pyplot as plt


import random

def draw_towers(state, title):


plt.clf()
plt.title(title)

pegs = [0, 1, 2]
peg_width = 0.2
peg_height = max(len(state[0]), len(state[1]), len(state[2])) + 2

for peg in pegs:


plt.bar(peg, peg_height, peg_width, color='black')

for peg_index, peg in enumerate(state):


for disk_index, disk in enumerate(peg):
plt.bar(peg_index, 1, 0.1 + disk * 0.1, bottom=disk_index + 1, color="blue")

plt.xticks([0, 1, 2], ["A", "B", "C"])


plt.show()
plt.pause(1)
def get_user_input():
while True:
try:
n = int(input("Enter number of disks: "))
if n > 0:
return n
else:
print("Please enter a positive integer.")
except ValueError:
print("Invalid input! Please enter a number.")

def generate_random_state(n):
state = [[], [], []]
for disk in range(n, 0, -1):
random.choice(state).append(disk)
return state

def get_possible_moves(state):
moves = []

for from_peg in range(3):


if state[from_peg]:
disk = state[from_peg][-1]

for to_peg in range(3):


if from_peg != to_peg and (not state[to_peg] or state[to_peg][-1] > disk):
new_state = [list(peg) for peg in state]
new_state[from_peg].pop()
new_state[to_peg].append(disk)
moves.append(new_state)
return moves
def depth_limited_search(state, goal, depth_limit):
if state == goal:
return True, state

if depth_limit == 0:
return False, None

for move in get_possible_moves(state):


found, final_state = depth_limited_search(move, goal, depth_limit - 1)
if found:
return True, final_state

return False, None

def solve_with_dls(start, goal, depth_limit):


draw_towers(start, "Initial State")

found, final_state = depth_limited_search(start, goal, depth_limit)


if found:
draw_towers(final_state, "Final State")
return final_state
else:
return "Solution not found within depth limit."

n = get_user_input()
start_state = generate_random_state(n)
goal_state = [[], [], list(range(n, 0, -1))]

print("Initial State:", start_state)


final_state = solve_with_dls(start_state, goal_state, depth_limit=2 ** n)
print("Final State:", final_state)

D. Hill Climbing Algorithm


import matplotlib.pyplot as plt
import random

def draw_towers(state, title):


plt.clf()
plt.title(title)

pegs = [0, 1, 2]
peg_width = 0.2
peg_height = max(len(state[0]), len(state[1]), len(state[2])) + 2

for peg in pegs:


plt.bar(peg, peg_height, peg_width, color='black')

for peg_index, peg in enumerate(state):


for disk_index, disk in enumerate(peg):
plt.bar(peg_index, 1, 0.1 + disk * 0.1, bottom=disk_index + 1, color="blue")

plt.xticks([0, 1, 2], ["A", "B", "C"])


plt.show()

plt.pause(1)

def get_user_input():
while True:
try:

n = int(input("Enter number of disks: "))


if n > 0:
return n

else:
print("Please enter a positive integer.")
except ValueError:
print("Invalid input! Please enter a number.")

def generate_random_state(n):
state = [[], [], []]
for disk in range(n, 0, -1):
random.choice(state).append(disk)
return state

def get_possible_moves(state):
moves = []
for from_peg in range(3):
if state[from_peg]:
disk = state[from_peg][-1]
for to_peg in range(3):
if from_peg != to_peg and (not state[to_peg] or state[to_peg][-1] > disk):
new_state = [list(peg) for peg in state]
new_state[from_peg].pop()
new_state[to_peg].append(disk)

moves.append(new_state)
return moves

def evaluate(state, goal):


score = 0

for peg_index in range(3):


for i, disk in enumerate(state[peg_index]):
if peg_index == 2 and goal[peg_index] and disk == goal[peg_index][i]:
score += 1
return score
def hill_climb(state, goal, max_iterations=100):
draw_towers(state, "Initial State")

for _ in range(max_iterations):
if state == goal:
draw_towers(state, "Final State")
return state

next_moves = get_possible_moves(state)
best_move = max(next_moves, key=lambda move: evaluate(move, goal),
default=None)

if best_move and evaluate(best_move, goal) > evaluate(state, goal):


state = best_move
draw_towers(state, f"Current State - Score: {evaluate(state, goal)}")
else:
break

draw_towers(state, "Final State")


return state

n = get_user_input()

start_state = generate_random_state(n)
goal_state = [[], [], list(range(n, 0, -1))]

print("Initial State:", start_state)


final_state = hill_climb(start_state, goal_state)
print("Final State:", final_state)
E. Uniform Cost Search
import heapq

# Function to represent the state of the Tower of Hanoi


def get_state_key(pegs):
return tuple(tuple(peg) for peg in pegs)

# Check if the current state is the goal state


def is_goal_state(pegs, num_disks):
return len(pegs[2]) == num_disks

# Get the valid moves from the current state


def get_valid_moves(pegs):
moves = []

# Try moving from each peg to every other peg


for i in range(3): # source peg
if pegs[i]:
for j in range(3): # target peg
if i != j: # can't move to the same peg
# Check if the move is valid (either target peg is empty or larger disk)
if not pegs[j] or pegs[i][-1] < pegs[j][-1]:
# Generate new state by moving the disk
new_pegs = [peg[:] for peg in pegs] # Copy of the pegs
disk = new_pegs[i].pop() # Remove disk from source peg
new_pegs[j].append(disk) # Add disk to target peg
moves.append((new_pegs, i, j)) # Append new state and source and target
pegs
return moves

# Uniform Cost Search (UCS)


def uniform_cost_search(initial_pegs, num_disks):
start_state = get_state_key(initial_pegs)
goal_state = get_state_key([[], [], list(range(num_disks, 0, -1))])

# Priority queue (min-heap) for UCS. Stores (cost, state, move_sequence)


frontier = []
heapq.heappush(frontier, (0, start_state, initial_pegs, [])) # Start with initial state

# Set to track visited states


visited = set()
visited.add(start_state)
# Perform UCS
while frontier:
cost, state_key, pegs, move_sequence = heapq.heappop(frontier)

# Check if we reached the goal


if is_goal_state(pegs, num_disks):
return move_sequence, cost

# Get all valid moves from the current state


for next_pegs, source, target in get_valid_moves(pegs):
next_state_key = get_state_key(next_pegs)

# If the new state hasn't been visited, add it to the frontier


if next_state_key not in visited:
visited.add(next_state_key)
new_cost = cost + 1
new_move_sequence = move_sequence + [(source, target)] # Append the move
to sequence
heapq.heappush(frontier, (new_cost, next_state_key, next_pegs,
new_move_sequence))

return None # If no solution is found

# Function to print the solution


def print_solution(move_sequence, total_cost):

print(f"Total cost: {total_cost}")


rods = ['A', 'B', 'C']
print("Solution:")

for move in move_sequence:


source, target = move
print(f"Move disk from rod {rods[source]} to rod {rods[target]}.")

# Main driver
if name == " main ":
# Define the number of disks and initialize the pegs
num_disks = int(input("Enter the number of disks: "))
pegs = [[i for i in range(num_disks, 0, -1)], [], []] # Initial state: All disks on peg A

# Perform Uniform Cost Search to solve the Tower of Hanoi


move_sequence, total_cost = uniform_cost_search(pegs, num_disks)

# Print the solution


if move_sequence:
print_solution(move_sequence, total_cost)
else:
print("No solution found!")

F. Breadth First Search


from collections import deque

# Function to print the current state of the pegs


def print_state(pegs):
for i, peg in enumerate(pegs):
print(f"Peg {i+1}: {peg}")

# Function to check if the current state is the goal state (all disks on the destination peg)
def is_goal_state(pegs, num_disks):
return len(pegs[2]) == num_disks

# Function to get possible valid moves from the current state


def get_valid_moves(pegs):
valid_moves = []

for i in range(3):
if len(pegs[i]) == 0:
continue

# Try moving the top disk of peg[i] to the other two pegs
for j in range(3):
if i != j:
if len(pegs[j]) == 0 or pegs[i][-1] < pegs[j][-1]: # Move is valid if the top disk of
peg[i] is smaller
new_pegs = [peg.copy() for peg in pegs]

disk = new_pegs[i].pop()
new_pegs[j].append(disk)
valid_moves.append((new_pegs, (i, j))) # Store the new pegs and the move
return valid_moves

# Function to find the shortest sequence of moves using BFS


def bfs_solution(num_disks):
# Initial state of the pegs (source peg with all disks, destination peg is empty)
initial_state = [[i for i in range(num_disks, 0, -1)], [], []]

# BFS queue stores tuples (pegs, path)


queue = deque([(initial_state, [])])

# Set to keep track of visited states


visited = set()
visited.add(tuple(tuple(peg) for peg in initial_state))

while queue:
current_pegs, path = queue.popleft()

# Check if we reached the goal state


if is_goal_state(current_pegs, num_disks):
return path

# Get all valid moves from the current state


for new_pegs, move in get_valid_moves(current_pegs):

# Convert the new pegs into a tuple of tuples for immutability


state_tuple = tuple(tuple(peg) for peg in new_pegs)

# If the new state hasn't been visited, add it to the queue


if state_tuple not in visited:

visited.add(state_tuple)
queue.append((new_pegs, path + [move])) # Store the path with the new move

return None # If no solution found


# Function to print the sequence of moves
def print_solution(path):
for i, (start, end) in enumerate(path):
print(f"Move disk from Peg {start+1} to Peg {end+1}")

# Main function
def solve_towers_of_hanoi(num_disks):
print(f"Solving Towers of Hanoi with {num_disks} disks using BFS...\n")

# Get the solution path


solution_path = bfs_solution(num_disks)

if solution_path is None:
print("No solution found.")
else:
print("Solution steps:")
print_solution(solution_path)
print("\nSolved!")

# Driver code to test the BFS solution for Towers of Hanoi


if name == " main ":
num_disks = int(input("Enter the number of disks: "))
solve_towers_of_hanoi(num_disks)
G. Depth First Search
class TowerOfHanoi:
def init (self, num_disks):
self.num_disks = num_disks
self.start_state = (tuple(range(num_disks, 0, -1)), (), ()) # Pegs as tuples
self.visited = set()
self.solutions = []

def is_goal(self, state):


return len(state[0]) == 0 and len(state[1]) == 0 # All disks moved to peg 3

def get_neighbors(self, state):


neighbors = []

for i in range(3):
if state[i]:
for j in range(3):
if i != j and (not state[j] or state[i][-1] < state[j][-1]):
new_state = list(map(list, state))
new_state[j].append(new_state[i].pop())
neighbors.append(tuple(map(tuple, new_state)))
return neighbors

def dfs(self, state, path):


if state in self.visited:
return
self.visited.add(state)
path.append(state)
if self.is_goal(state):
self.solutions.append(path[:]) # Store the solution path
else:
for neighbor in self.get_neighbors(state):
self.dfs(neighbor, path)
path.pop()

def solve(self):
self.dfs(self.start_state, [])
return self.solutions

# Example usage
num_disks = 3
toh = TowerOfHanoi(num_disks)
solutions = toh.solve()
for idx, solution in enumerate(solutions):

print(f"Solution {idx + 1}:")


for state in solution:
print(state)
print("-" * 30)
H. Greedy Best First Search
import heapq

class TowerOfHanoiGBFS:
def init (self, num_disks):
self.num_disks = num_disks
self.start_state = (tuple(range(num_disks, 0, -1)), (), ())
self.goal_state = ((), (), tuple(range(num_disks, 0, -1)))
self.visited = set()

def heuristic(self, state):


return sum(1 for i, disk in enumerate(state[2]) if disk == self.num_disks - i)

def get_neighbors(self, state):


neighbors = []
for i in range(3):
if state[i]:
for j in range(3):
if i != j and (not state[j] or state[i][-1] < state[j][-1]):
new_state = list(map(list, state))
new_state[j].append(new_state[i].pop())
neighbors.append(tuple(map(tuple, new_state)))
return neighbors

def gbfs(self):
pq = []
heapq.heappush(pq, (self.heuristic(self.start_state), self.start_state, []))

while pq:
_, current_state, path = heapq.heappop(pq)

if current_state == self.goal_state:
return path

if current_state in self.visited:
continue

self.visited.add(current_state)

for neighbor in self.get_neighbors(current_state):


heapq.heappush(pq, (self.heuristic(neighbor), neighbor, path + [neighbor]))

return None

# Example usage
num_disks = 3
toh_gbfs = TowerOfHanoiGBFS(num_disks)

solution = toh_gbfs.gbfs()
if solution:
print("Solution found:")
for state in solution:
print(state)
else:
print("No solution found.")

Visualizing the output:


Step-by-step movement
Move Disk 1 from A → C
Move Disk 2 from A → B
Move Disk 1 from C → B
Move Disk 3 from A → C
Move Disk 1 from B → A
Move Disk 2 from B → C
Move Disk 1 from A → C

Iterative Solution:
Recursive solution:

Task Interaction
Each action directly affects the future available moves.
Smaller disks need to be freed first before larger disks can move.
DFS ensures that all possible moves are explored, while Greedy
may take an inefficient path due to local optimizations.
The number of disks affects complexity exponentially (moves
double for every extra disk).
Graphical Visualization:
A graphical representation can make understanding the
problem easier. The steps of execution can be animated as disks
moving from one rod to another.
Ways to visualize graphically:
• Using Python s Matplotlib
o Draw the rods and disks using bars.
o Animate the movement of disks between rods.
o Example libraries: Matplotlib, Pygame, Tkinter
• Using Online Simulators
o Websites like
https://www.mathsisfun.com/games/towerofhanoi.
html
o Animated solutions for different numbers of disks.

• Using Blocks in a Grid (ASCII Animation)


o Represent rods as columns and disks as different-
sized symbols.
Recursive Best-First Search (RBFS):
import math
import heapq

class State:
def __init__(self, pegs, moves=0, h=0, parent=None):
self.pegs = pegs # Pegs represented as a list of stacks
self.moves = moves # Number of moves made
self.h = h # Heuristic value (estimated cost)
self.parent = parent # Parent state for backtracking

def __lt__(self, other):


return (self.moves + self.h) < (other.moves + other.h)

def __eq__(self, other):


return self.pegs == other.pegs

def __hash__(self):
return hash(tuple(tuple(peg) for peg in self.pegs))

def heuristic(state, goal_state):


"""
A simple heuristic: Count the number of disks not in the goal position.
"""
misplaced_disks = sum(1 for i in range(len(state.pegs)) if state.pegs[i] != goal_state[i])
return misplaced_disks

def generate_successors(state, constraints):


"""
Generate all possible valid moves while considering constraints.
"""
successors = []
num_pegs = len(state.pegs)

for i in range(num_pegs):
if state.pegs[i]: # Ensure peg is not empty
disk = state.pegs[i][-1] # Take the top disk

for j in range(num_pegs):
if i != j and (not state.pegs[j] or state.pegs[j][-1] > disk): # Valid move
if disk in constraints and constraints[disk] != j:
continue # Enforce constraint (some disks must stay on certain pegs)

new_pegs = [list(peg) for peg in state.pegs] # Deep copy


new_pegs[j].append(new_pegs[i].pop()) # Move disk
new_state = State(new_pegs, state.moves + 1, 0, state)
new_state.h = heuristic(new_state, constraints['goal'])
successors.append(new_state)

return successors

def rbfs(state, goal_state, constraints, f_limit):


"""
Recursive Best-First Search (RBFS) for dynamically constrained Towers of Hanoi.
"""
if state.pegs == goal_state:
return state, 0 # Solution found

successors = generate_successors(state, constraints)

if not successors:
return None, math.inf # No feasible moves

for s in successors:
s.h = max(s.h, state.h) # Ensure non-decreasing f-values

heapq.heapify(successors) # Priority queue (best move first)

while successors:
best = heapq.heappop(successors) # Best move
if best.h > f_limit:
return None, best.h # Prune if exceeding limit

# Second-best move (alternative path)


alternative = successors[0].h if successors else math.inf

result, best.h = rbfs(best, goal_state, constraints, min(f_limit, alternative))

if result:
return result, best.h # Solution found

heapq.heappush(successors, best) # Restore if needed

return None, math.inf

def solve_towers_of_hanoi(start_state, goal_state, constraints):


"""
Wrapper function to solve Towers of Hanoi using RBFS.
"""
initial_h = heuristic(start_state, constraints['goal'])
result, _ = rbfs(start_state, goal_state, constraints, initial_h)

# Reconstruct the path if a solution was found


if result:
path = []
while result:
path.append(result.pegs)
result = result.parent
return path[::-1] # Return the path from start to goal
return None

# Example usage:
initial_pegs = [[3, 2, 1], [], []] # Start state: All disks on first peg
goal_pegs = [[], [], [3, 2, 1]] # Goal state: All disks on the third peg

# Constraints (e.g., disk 2 must stay on peg 1 for some time)


constraints = {
2: 1, # Disk 2 must stay on peg 1
'goal': goal_pegs # The final goal state
}

start_state = State(initial_pegs)
solution_path = solve_towers_of_hanoi(start_state, goal_pegs, constraints)

if solution_path:
print("\nSolution Found:")
for step, state in enumerate(solution_path):
print(f"Step {step}:", state)
else:
print("No solution found.")
Out put:
Solution Found:
Step 0: [[3, 2, 1], [], []]
Step 1: [[3, 2], [], [1]]
Step 2: [[3], [2], [1]]
Step 3: [[3], [2, 1], []]
Step 4: [[], [2, 1], [3]]
Step 5: [[], [2], [3, 1]]
Step 6: [[], [], [3, 2, 1]]

Constraint Satisfaction Problems (CSP)

class TowersOfHanoiCSP:
def __init__(self, num_disks, constraints):
self.num_disks = num_disks
self.pegs = {i: [] for i in range(3)} # Pegs as dictionary {0: [], 1: [], 2: []}
self.constraints = constraints # Constraints dict {disk: peg}
self.solution = []
for disk in range(num_disks, 0, -1):
self.pegs[0].append(disk) # Start with all disks on peg 0

def is_valid_move(self, from_peg, to_peg):


""" Check if a move is valid under CSP constraints """
if not self.pegs[from_peg]: # No disk to move
return False

disk = self.pegs[from_peg][-1]

# Constraint check: Disk must stay on a particular peg


if disk in self.constraints and self.constraints[disk] != to_peg:
return False

# Standard Hanoi rules: A larger disk cannot be placed on a smaller one


if self.pegs[to_peg] and self.pegs[to_peg][-1] < disk:
return False

return True

def move_disk(self, from_peg, to_peg):


""" Move the top disk from one peg to another """
disk = self.pegs[from_peg].pop()
self.pegs[to_peg].append(disk)
self.solution.append((disk, from_peg, to_peg)) # Log the move

def backtrack(self, num_moves=0):


""" Recursive backtracking search with forward checking """
if self.pegs[2] == list(range(self.num_disks, 0, -1)): # Goal state check
return True

for from_peg in range(3):


if self.pegs[from_peg]: # If peg is not empty
for to_peg in range(3):
if from_peg != to_peg and self.is_valid_move(from_peg, to_peg):
self.move_disk(from_peg, to_peg)

if self.backtrack(num_moves + 1): # Recursive call


return True # Solution found

# Backtrack (undo move)


self.move_disk(to_peg, from_peg)

return False # No valid moves found

def solve(self):
""" Solve the CSP-based Towers of Hanoi problem """
if self.backtrack():
return self.solution
else:
return None # No solution found

# Example Usage:
num_disks = 3
constraints = {2: 1} # Example constraint: Disk 2 must stay on Peg 1 temporarily

hanoi_csp = TowersOfHanoiCSP(num_disks, constraints)


solution = hanoi_csp.solve()

if solution:
print("\nSolution Found:")
for move in solution:
print(f"Move Disk {move[0]} from Peg {move[1]} to Peg {move[2]}")
else:
print("No solution found.")

Output

Move Disk 1 from Peg 0 to Peg 2


Move Disk 2 from Peg 0 to Peg 1
Move Disk 1 from Peg 2 to Peg 1
Move Disk 3 from Peg 0 to Peg 2
Move Disk 1 from Peg 1 to Peg 0
Move Disk 2 from Peg 1 to Peg 2
Move Disk 1 from Peg 0 to Peg 2

You might also like