Lab 07 Adversarial Search
Lab 07 Adversarial Search
Lab 07
Intelligence
Adversarial Search
AI-2002
National University of Computer & Emerging Sciences –
NUCES – Karachi
National University of Computer & Emerging Sciences -
NUCES - Karachi
FAST School of Computing
Contents
1. Objectives 3
Player(s): It defines which player is having the current turn to make a move
in the state.
Actions(s): It defines the set of legal moves to be used in a state.
Result(s, a): It is a transition model which defines the result of a move.
Terminal-Test(s): It defines that the game has ended and returns true.
Utility(s, p): It defines the final value with which the game has ended. This
function is also known as the Objective function or Payoff function.
The price which the winner will get i.e.
● -1: If the Player loses.
● +1: If the Player wins.
● 0: If there is a draw between the Players.
For example, in chess, tic-tac-toe, we have two or three possible outcomes. Either to win,
to lose, or to draw the match with values +1,-1, or 0.
Let’s understand the working of the elements with the help of a game tree designed for
tic-tac-toe.
Here, the node represents the game state and the edges represent the moves taken by
the players.
Initial State (So): The top node in the game tree represents the initial state in the
tree and shows all the possible choices to pick out one.
Player(s): There are two players, Max and Min. Max begins the game by
picking one best move and placing X in the empty square box.
Actions(s): Both the players can make moves in the empty boxes chance by
chance.
Results(s, a): The moves made by Min and Max will decide the outcome of the
game.
Terminal-Test(s): When all the empty boxes will be filled, it will be the terminating
state of the game.
Utility: At the end, we will get to know who wins: Max or Min, and
accordingly, the price will be given to them.
In the minimax strategy, the result of the game or the utility value is generated by a
heuristic function, which propagates from the leaf nodes back to the root node using
backtracking. At each node, the algorithm selects the maximum or minimum value
depending on whether it is the Max or Min player's turn.
The Minimax strategy follows the Depth-First Search (DFS) concept. In this
approach, there are two players, Min and Max, who take turns alternately during the
game. For instance, when Max makes a move, the next turn belongs to Min. Once Max
makes a move, it is fixed and cannot be changed.
The Minimax strategy follows the Depth-First Search (DFS) concept to traverse
the game tree. In this approach, two players, Min and Max, alternate turns during the
game. For example, when Max makes a move, the next turn belongs to Min. During the
evaluation of a specific path, the move made by Max is treated as fixed within that path.
However, the algorithm backtracks to explore other paths and determine the optimal
move. The DFS approach ensures each path is explored fully before backtracking,
making it more suitable than Breadth-First Search (BFS) for this algorithm.
Max's Turn:
● Max starts the game by selecting a path and exploring (propagating) all the nodes
along that path.
● After exploring the nodes, Max backtracks to the initial node.
● Max then selects the best path, i.e., the one where his utility value is maximized.
Min's Turn:
● If the current level is a minimizing level (Min's turn), the node accepts the
minimum value from its successor nodes.
● If the current level is a maximizing level (Max's turn), the node accepts the
maximum value from its successor nodes.
Key Concept:
import math
class Node:
def __init__(self, value=None):
self.value = value
self.children = []
self.minmax_value = None
class MinimaxAgent:
def __init__(self, depth):
self.depth = depth
class Environment:
def __init__(self, tree):
self.tree = tree
self.computed_nodes = []
if maximizing_player:
value = -math.inf
for child in node.children:
child_value = self.compute_minimax(child, depth - 1, False)
value = max(value, child_value)
node.minmax_value = value
self.computed_nodes.append(node.value)
return value
else:
value = math.inf
for child in node.children:
child_value = self.compute_minimax(child, depth - 1, True)
value = min(value, child_value)
node.minmax_value = value
self.computed_nodes.append(node.value)
return value
# sample tree
root = Node('A')
n1 = Node('B')
n2 = Node('C')
root.children = [n1, n2]
n3 = Node('D')
n4 = Node('E')
n5 = Node('F')
n6 = Node('G')
n1.children = [n3, n4]
n2.children = [n5, n6]
n7 = Node(2)
n8 = Node(3)
n9 = Node(5)
n10 = Node(9)
n3.children = [n7, n8]
n4.children = [n9, n10]
n11 = Node(0)
n12 = Node(1)
n13 = Node(7)
n14 = Node(5)
n5.children = [n11, n12]
n6.children = [n13, n14]
agent = MinimaxAgent(depth)
environment = Environment(root)
print("Minimax values:")
print("A:", root.minmax_value)
print("B:", n1.minmax_value)
print("C:", n2.minmax_value)
print("D:", n3.minmax_value)
print("E:", n4.minmax_value)
print("F:", n5.minmax_value)
print("G:", n6.minmax_value)
The key idea behind alpha-beta pruning is to "prune" or eliminate branches of the tree
that do not affect the final decision. This allows the algorithm to focus only on relevant
paths while still making the same decisions as the Minimax algorithm. By using this
technique, unnecessary calculations are avoided, improving efficiency.
1. Alpha (-∞): The best value that the Max player can guarantee at any point. It acts
as the lower bound (negative infinity initially).
2. Beta (+∞): The best value that the Min player can guarantee at any point. It acts
as the upper bound (positive infinity initially).
This pruning mechanism ensures that branches that cannot influence the outcome are
skipped, making the algorithm faster without compromising accuracy.
Final Tree
● Nodes computed: A, B, D, E, C, F
● Nodes pruned: Right child of E, Right child of C (Node G)
● The final tree shows the computed nodes and the pruned nodes. The optimal
value for the Max player is 3.
import math
class Node:
def __init__(self, value=None):
self.value = value
self.children = []
self.minmax_value = None
class MinimaxAgent:
def __init__(self, depth):
self.depth = depth
class Environment:
def __init__(self, tree):
self.tree = tree
self.computed_nodes = []
if maximizing_player:
value = -math.inf
for child in node.children:
value = max(value, self.alpha_beta_search(child, depth - 1,
alpha, beta, False))
alpha = max(alpha, value)
if beta <= alpha:
print("Pruned node:", child.value)
break
node.minmax_value = value
return value
else:
value = math.inf
for child in node.children:
value = min(value, self.alpha_beta_search(child, depth - 1,
alpha, beta, True))
beta = min(beta, value)
if beta <= alpha:
print("Pruned node:", child.value)
break
node.minmax_value = value
return value
n3 = Node('D')
n4 = Node('E')
n5 = Node('F')
n7 = Node(2)
n8 = Node(3)
n9 = Node(5)
n10 = Node(9)
n3.children = [n7, n8]
n4.children = [n9, n10]
n11 = Node(0)
n12 = Node(1)
n13 = Node(7)
n14 = Node(5)
n5.children = [n11, n12]
n6.children = [n13, n14]
agent = MinimaxAgent(depth)
environment = Environment(root)
Pruned node: 5
Output Pruned node: F
Nodes computed: ['A', 'B', 'D', 2, 3, 'E', 5, 'C', 'F', 0,
3. Lab Tasks
3.1. Task 01
Tic-Tac-Toe is a classic two-player game played on a 3×3 grid. The players take turns
marking a space with their symbol (X or O). The goal is to form a straight line of three
symbols, either horizontally, vertically, or diagonally. If the grid is full and no player
has won, the game ends in a draw.
In this task, you will implement an AI player using the Minimax algorithm. The AI will
analyze the game board and always make the optimal move, ensuring that it never
loses.
X = AI’s Symbol
O = Player Symbol
Positions:
0 0 | 0 1 | 0 2
1 0 | 1 1 | 1 2
2 0 | 2 1 | 2 2
Sample Output
| |
| |
| |
O | |
| X |
| |
O | X |
| X |
| | O
O | X |
| X |
X | O | O
O | X | O
| X | X
X | O | O
O | X | O
O | X | X
X | O | O
Imagine a game where two players, Max and Min, take turns picking coins from a row
of numbers. Each player can only pick either the leftmost or rightmost coin from the
remaining sequence.
Since Max moves first, he wants to find the best sequence of picks that gives him the
highest possible sum. This task uses the Alpha-Beta Pruning algorithm to optimize
Max’s decision-making.
Sample Output
Winner: Max
Implement the Minimax algorithm to control an agent navigating a maze. The AI should
find the shortest path to a goal while avoiding obstacles and enemies, planning ahead
using the Minimax strategy.
Tasks:
● Create a maze grid with walls, obstacles, and enemies that the AI needs to
navigate.
● Use the Minimax algorithm to simulate different moves the agent can make and
predict the consequences of those moves, such as avoiding enemies or finding
paths around obstacles.
● Incorporate Alpha-Beta pruning to optimize the search for the shortest path and
avoid unnecessary computations.
● Develop an evaluation function that assesses the desirability of a given state
based on proximity to the goal and safety (i.e., avoiding enemies).
Implement the Minimax algorithm for a Connect Four game to decide the best move for
the AI player. The AI should analyze potential moves, simulate future states, and select
the move that maximizes its chances of winning.
Tasks: