Unity University: Department of Computer Science
Unity University: Department of Computer Science
Unity University: Department of Computer Science
Nahom Lulseged
03396/13
Adversarial search strategies are used in competitive environments where agents with opposing goals
compete, such as in games like chess, checkers, or tic-tac-toe. Two common algorithms used in
adversarial search are the Minimax algorithm and the Alpha-Beta Pruning algorithm.
Minimax Algorithm
The Minimax algorithm is a recursive search algorithm used to choose an optimal move for a player
assuming that the opponent is also playing optimally. It works by simulating all possible moves and
counter-moves, and then choosing the move that maximizes the player's minimum gain (hence the name
"minimax").
Chess: In a chess game, the Minimax algorithm can be used to decide the next move. The algorithm
simulates all possible moves (for both players) from the current position to a certain depth, and
evaluates the board position at the leaves of the search tree. The move that maximizes the minimum
possible gain (considering the opponent's best possible responses) is chosen.
The Alpha-Beta Pruning algorithm is an optimization of the Minimax algorithm. It reduces the number of
nodes that need to be evaluated in the search tree by pruning branches that cannot possibly influence
the final decision. This is done by maintaining two values, alpha and beta, which represent the minimum
score that the maximizing player is assured of and the maximum score that the minimizing player is
assured of, respectively.
Tic-Tac-Toe: In a tic-tac-toe game, Alpha-Beta pruning can be applied to the Minimax algorithm to
efficiently decide the next move. By pruning branches that do not need to be explored (because they
cannot affect the outcome), the algorithm runs faster and can search deeper within the same time
constraints.
A Constraint Satisfaction Problem (CSP) involves finding a solution to a problem that satisfies a number
of constraints. CSPs can be solved using search algorithms that systematically explore the possible
assignments of values to variables while checking against constraints.
Use Case Example:
Sudoku: In a Sudoku puzzle, the goal is to fill a 9x9 grid with digits so that each column, each row, and
each of the nine 3x3 subgrids contain all the digits from 1 to 9. This can be modeled as a CSP where each
cell in the grid is a variable, the values are the digits from 1 to 9, and the constraints are the rules of
Sudoku. A CSP solver would explore different assignments of digits to cells while ensuring the constraints
are not violated.
An inference system is used in artificial intelligence to deduce new information from known facts using
logical rules. There are two main types of inference: forward chaining and backward chaining.
Forward Chaining
Forward chaining is a data-driven inference technique that starts with known facts and applies inference
rules to extract more data until a goal is reached.
Example:
Expert Systems: Consider an expert system for diagnosing diseases. The system starts with known
symptoms (facts) and applies medical rules to deduce possible diseases (conclusions). If the system
knows that a patient has a fever and a cough, it can infer (using forward chaining) that the patient might
have the flu.
Backward Chaining
Backward chaining is a goal-driven inference technique that starts with a goal and works backward to
determine which facts must be true to achieve that goal.
Example:
Problem Solving: Consider a troubleshooting system for fixing a car. The system starts with the goal (e.g.,
"The car will not start") and works backward to identify possible causes (e.g., "The battery is dead", "The
fuel tank is empty"). It then checks these hypotheses against known facts to find the cause of the
problem.
Exercise 1.1
f: Valid atom.
(b) Which of the following are valid names for Prolog variables?
(c) What would a Prolog interpreter reply given the following query?
X = a,
Y = b.
(d) Would the following query succeed?
No, the query would not succeed because mary and Mary are different terms in Prolog (one is an atom,
and the other is a variable).
a(B, B).
has been consulted by Prolog. How will the system react to the following query?
Exercise 1.2
(a)
First, X is unified with myFunctor(1, 2). Then, X is unified with myFunctor(Y, Y). This unification will fail
because myFunctor(1, 2) cannot match myFunctor(Y, Y) (1 = 2).
(b)
a matches a.
_ matches X.
c matches Y.
d matches _.
X = c,
Y = d.
(c)
Prolog will execute write('One ') first, printing "One ". Then X is unified with write('Two '), but this does
not immediately execute the write('Two '). To execute it, you need another goal that uses X, for example:
Exercise 1.3
Let's draw the family tree and define the new predicates.
Family Tree:
| | |
| |
Dick Sandra
Predicates:
father(X, Y) :-
parent(X, Y),
male(X).
sister(X, Y) :-
parent(Z, X),
parent(Z, Y),
female(X),
X \= Y.
grandmother(X, Y) :-
parent(X, Z),
parent(Z, Y),
female(X).
cousin(X, Y) :-
parent(A, X),
parent(B, Y),
parent(C, A),
parent(C, B),
X \= Y.
Exercise 1.4
Original rules:
is_bigger(elephant, horse).
is_bigger(horse, donkey).
Query:
?- is_bigger(A, donkey).
The query will first look for A such that is_bigger(A, X) and is_bigger(X, donkey) both hold. This may
cause an infinite loop or repeated solutions, because Prolog will keep looking for larger elements
without terminating.
Exercise 1.5
Reviewing and practicing the syntax, matching, and goal execution in Prolog after gaining some
experience will help solidify the concepts and improve understanding.
The predicate analyse_list/1 should analyze the given list and print its head and tail, or report if the list is
empty or not a list.
analyse_list([]) :-
nl.
analyse_list([Head|Tail]) :-
write(Head),
nl,
write(Tail),
nl.
analyse_list(_) :-
fail.
membership(X, [X|_]).
membership(X, [_|Tail]) :-
membership(X, Tail).
remove_duplicates([], []).
remove_duplicates([Head|Tail], Result) :-
member(Head, Tail),
remove_duplicates(Tail, Result).
remove_duplicates([Head|Tail], [Head|Result]) :-
\+ member(Head, Tail),
remove_duplicates(Tail, Result).
reverse_list(List, Reversed) :-
The predicate whoami/1 succeeds if the list contains an even number of elements.
whoami([]).
whoami([_, _ | Rest]) :-
whoami(Rest).
A goal of the form whoami(X) will succeed if X is a list with an even number of elements.
The predicates last1/2 and last2/2 return the last element of a list.
(a) Using recursion:
last1([X], X).
last1([_|Tail], X) :-
last1(Tail, X).
last2(List, X) :-
The predicate replace/4 replaces all occurrences of an element in a list with another element.
replace([], _, _, []).
Head \= Old,
power([], [[]]).
power([Head|Tail], PowerSet) :-
power(Tail, TailPowerSet),
The predicate longer/2 succeeds if the second list is longer than the first.
longer([], [_|_]).
longer([_|Tail1], [_|Tail2]) :-
longer(Tail1, Tail2).
(a) successor/2
The successor of a number is the number we obtain if we add one to it. In unary notation, adding one
means appending one more x to the list.
successor(Unary, [x|Unary]).
Examples:
Result = [x].
(b) plus/3
The predicate plus/3 computes the sum of two given unary numbers. In unary notation, the sum of two
numbers is simply their concatenation.
plus([], Y, Y).
plus([x|X], Y, [x|Z]) :-
plus(X, Y, Z).
Examples:
(c) times/3
The predicate times/3 multiplies two given unary numbers. In unary notation, the product of two
numbers can be obtained by repeatedly concatenating the first number to itself, the number of times
equal to the second number.
times([], _, []).
times([x|X], Y, Result) :-
times(X, Y, PartialResult),
Examples: