Experiment-02 Ai
Experiment-02 Ai
(Abhit Kumar-2K22/IT/11)
Aim:- a)Write down two intelligent programs for
the TIC-TAC-TOE problem.
Theory:-
Tic-Tac-Toe is a classic game played on a 3x3 grid, where two
players alternately place their respective markers (X and O).
The objective is to place three markers in a row horizontally,
vertically, or diagonally.
Intelligent Programs for Tic-Tac-Toe
1. Minimax Algorithm: A decision-making algorithm used in
two-player games. It explores all possible moves and
selects the move that maximizes the player's chances of
winning while minimizing the opponent's chances.
2. Alpha-Beta Pruning: An optimization of Minimax that
reduces the number of nodes evaluated in the decision
tree, making it computationally efficient.
Code:-
import math
# Initialize board
def print_board(board):
for row in board:
print(" | ".join(row))
print()
def is_winner(board, player):
for row in board:
if all(s == player for s in row):
return True
for col in range(3):
if all(board[row][col] == player for row in range(3)):
return True
if all(board[i][i] == player for i in range(3)) or all(board[i][2 -
i] == player for i in range(3)):
return True
return False
def is_draw(board):
return all(cell != ' ' for row in board for cell in row)
def get_empty_positions(board):
return [(r, c) for r in range(3) for c in range(3) if board[r][c]
== ' ']
def minimax(board, depth, is_maximizing):
if is_winner(board, 'X'):
return -10 + depth
if is_winner(board, 'O'):
return 10 - depth
if is_draw(board):
return 0
if is_maximizing:
max_eval = -math.inf
for r, c in get_empty_positions(board):
board[r][c] = 'O'
score = minimax(board, depth + 1, False)
board[r][c] = ' '
max_eval = max(max_eval, score)
return max_eval
else:
min_eval = math.inf
for r, c in get_empty_positions(board):
board[r][c] = 'X'
score = minimax(board, depth + 1, True)
board[r][c] = ' '
min_eval = min(min_eval, score)
return min_eval
def best_move(board):
best_score = -math.inf
move = None
for r, c in get_empty_positions(board):
board[r][c] = 'O'
score = minimax(board, 0, False)
board[r][c] = ' '
if score > best_score:
best_score = score
move = (r, c)
return move
# Game loop
board = [[' ' for _ in range(3)] for _ in range(3)]
print("Tic-Tac-Toe: You are X and the computer is O")
while True:
print_board(board)
user_move = tuple(map(int, input("Enter your move (row and
column): ").split()))
if board[user_move[0]][user_move[1]] == ' ':
board[user_move[0]][user_move[1]] = 'X'
else:
print("Invalid move, try again.")
continue
if is_winner(board, 'X'):
print_board(board)
print("You win!")
break
if is_draw(board):
print_board(board)
print("It's a draw!")
break
ai_move = best_move(board)
board[ai_move[0]][ai_move[1]] = 'O'
if is_winner(board, 'O'):
print_board(board)
print("Computer wins!")
break
if is_draw(board):
print_board(board)
print("It's a draw!")
break
OUTPUT:-
b) Write down a program to implement Breadth-
first search and depth-first search for the water
jug problem
Theory:-
The Water Jug Problem is a common AI problem where you have
two jugs of fixed capacities and must measure an exact amount
of water using operations like filling, emptying, and transferring
water between jugs.
State Representation:
A state is represented as (x, y), where x and y are the current
water levels in jug1 and jug2, respectively.
Goal:
Reach a target water level in one of the jugs.
Algorithms:
1. Breadth-First Search (BFS): Explores all states level by
level, ensuring the shortest path to the goal. It uses a
queue.
2. Depth-First Search (DFS): Explores as far as possible along
each branch before backtracking. It uses a stack or
recursion.
Complexity:
BFS guarantees the shortest path to the solution but may
consume more memory as it stores all nodes at the
current level in the queue. Its time complexity is
O(V+E)O(V + E)O(V+E), where VVV is the number of
states (vertices) and EEE is the number of possible
transitions (edges).
DFS is memory-efficient as it explores one branch at a
time but does not guarantee the shortest path. Its time
complexity is similar to BFS, O(V+E)O(V + E)O(V+E), but
with a smaller memory footprint.
Applicability to Real-world Problems:
The water jug problem represents real-world challenges in
resource management. BFS is preferred when the goal is to find
the minimum steps to achieve a solution, while DFS is suitable
for exploring all possible outcomes or when memory is a
constraint.
CODE:-
from collections import deque
def water_jug_bfs(jug1_capacity, jug2_capacity, target):
visited = set()
queue = deque([(0, 0)]) # Start with both jugs empty
parent = {} # To track the path
visited.add((0, 0)) while queue:
x, y = queue.popleft()
# Possible states
states = [
(jug1_capacity, y), # Fill jug1
(x, jug2_capacity), # Fill jug2
(0, y), # Empty jug1
(x, 0), # Empty jug2
(max(x - (jug2_capacity - y), 0), min(jug2_capacity, x +
y)), # Pour jug1 -> jug2
(min(jug1_capacity, x + y), max(y - (jug1_capacity - x),
0)) # Pour jug2 -> jug1
]
# Possible states
states = [
(jug1_capacity, y), # Fill jug1
(x, jug2_capacity), # Fill jug2
(0, y), # Empty jug1
(x, 0), # Empty jug2
(max(x - (jug2_capacity - y), 0), min(jug2_capacity, x +
y)), # Pour jug1 -> jug2
(min(jug1_capacity, x + y), max(y - (jug1_capacity - x),
0)) # Pour jug2 -> jug1
]
for state in states:
if dfs(state[0], state[1]):
return True
path.pop()
return False
if dfs(0, 0):
return path
return None # No solution
# Test BFS and DFS
jug1_capacity = 4
jug2_capacity = 3
target = 2
print("BFS Solution:")
bfs_result = water_jug_bfs(jug1_capacity, jug2_capacity, target)
if bfs_result:
for step in bfs_result:
print(step)
else:
print("No solution")
print("\nDFS Solution:")
dfs_result = water_jug_dfs(jug1_capacity, jug2_capacity, target)
if dfs_result:
for step in dfs_result:
print(step)
else:
print("No solution")
OUTPUT:-