You are on page 1of 61

Cairo University, Faculty of Computers and AI

CS213 – 2022 / 2023

Programming II

Lecture 19: Backtracking and const

By
Dr. Mohammad El-Ramly
Lecture Objectives
• Understand the importance of const

• Understand backtracking deeper and


apply to some problems

• Understanding minimax
Safe Programming
• It is programming in a way that reduces
crashes and run-time errors.
• C++ is moving more and more to safety
▪ Exceptions
▪ const correctness
▪ Smart pointers
▪ Static analysis tools
1. const
• const is a mechanize for safe
programming that minimizes possible side
effects.

• What does this code mean?

• const char * const MyFunction


(const string& input) const;

4
Examples
• getWidth should probably NOT change
anything
• When passing by reference, probably we
like to specify if the function can change it
or no.

• For these tasks, we can use const.


Values
• const float PI = 3.14;

• This is the easiest use, declaring


constants
Constant Values
• const float PI = 3.14;
• const int LENGTH = 100;

• This is the easiest use, declaring


constants
• It can protect you from errors like
• int length = …
• if (length = 100) …
const and Pointers
const int* data = new int[3]{1,2,3};
data[1] = 0; // WRONG pointer 2 const
delete [] data;
data = new int(4) // OK

int* const data = new int[3]{1,2,3};


data[1] = 0; // OK
data = new int[2] // WRONG const pointer

•To understand it, read it from right to left


const and Pointers
• What is this?
• int const * const data
= new int[3]{1,2,3};
Is this legal ?
• int myInt;
• const int* myPtr = &myInt;
• myInt = 900;
const and Objects
• const string myString =
"This is a constant string!";

• You cannot modify content of the string


• myString = "New Str"; // Wrong
• myString.length(); // OK

• Why?
• size_type length() const;
const and Objects
• const keyword after method name
indicates that it does not modify any of the
class's instance variables.
▪ You are guaranteed that the object's contents
will not change.
• For const objects, you can only call
methods that have been marked const.
▪ If you have a function that doesn't modify the
object but not marked const, you can’t call it.
• getX() and getX() const are
different methods.
const and References
• void doStuff(vector<int>& vec);

• Pass by reference avoid expansive


copying?
• But is this function supposed to change
something in the vector?
• If not pass it by const reference.
• void doStuff(const
vector<int>& vec);
const and Iterators
• What is wrong with this code?

void PrintVector (const


vector<string> &myVector)
{
for (vector<string>::iterator
itr = myVector.begin();
itr != myVector.end();
++itr)
cout << *itr << endl;
}
const and Iterators
• You can only use const_iterator on a
const container.

• C++ define two different begin and end


functions: non-const versions that return
iterators and const versions that
return const_iterators.

• C++ will call the right one depending on


the constness of the container.
const and Iterators
void regularIterator(vector<int>& v) {
vector<int>::iterator i ;
for (i = v.begin(); i < v.end(); i++)
*i += 1;
cout << *i << " ";
}
}
void constIterator(const vector<int>& v1)
{
vector<int>::const_iterator ci;
for (ci = v1.begin(); ci < v1.end(); ci++)
cout << *ci << " ";
}
}
1. const
• const is a mechanize for safe
programming that minimizes possible side
effects.

• What does this code mean?

• const char * const MyFunction


(const string& input) const;

18
const Correctness
• const Correctness is a safety
mechanism to protect data from accidental
changes when they should not be
changed.
const Correctness
1. Objects are never passed by value.
▪ Instead of pass by value, they are passed by
reference to-const or pointer-to-const

2. Member functions which do not


change state are marked const.
▪ Methods not marked as const are almost
guaranteed to make some change to state.

• Variables which are set but never


modified are marked const.
Example – Make this const
correct
template <typename ValueType> class Map {
public:
Map(int sizeHint = 101);
~Map();
int size();
bool isEmpty();
void put(string key, ValueType value);
void remove(string key);
bool containsKey(string key);
ValueType get(string key);
ValueType& operator[](string key);
void clear();
Iterator iterator();
private:
/* ... Implementation specific ... */
};
Member functions which do not
change state are marked const.
template <typename ValueType> class Map {
public:
Map(int sizeHint = 101);
~Map();
int size() const;
bool isEmpty() const;
void put(string key, ValueType value);
void remove(string key);
bool containsKey(string key) const;
ValueType get(string key) const;
ValueType& operator[](string key);
void clear();
Iterator iterator() const;
private:
/* ... Implementation specific ... */
};
Objects are never passed by value.
template <typename ValueType> class Map {
public:
Map(int sizeHint = 101);
~Map();
int size() const;
bool isEmpty() const;
void put(const string& key,
const ValueType& value);
void remove(const string& key);
bool containsKey(const string& key) const;
ValueType get(string key) const;
ValueType& operator[](const string& key);
void clear();
Iterator iterator() const;
private:
/* ... Implementation specific ... */
};
Why const Correctness?
• Code correctness, reducing the possibility
of errors.
• Code documentation, making code
self-documenting and clearly indicating
what can/cannot be done.
• Library integration. The C++ standard
libraries and most third-party libraries are
fully const-correct and expect that any
classes or functions that interface with
them to be the same.
3. Backtracking
• Backtracking is an algorithmic technique
for solving problems recursively by trying
to build a solution incrementally, removing
those solutions that fail to satisfy the
constraints of the problem at any point in
time.
• It searches for a solution to a problem
among all the available options.

27
The 8 Queens Problem

CS 307 Fundamentals
of Computer Science
The 8 Queens Problem
• A classic chess puzzle
▪ Place 8 queen pieces on a chess board so
that none of them can attack one another
The N Queens Problem
• Place N Queens on an N by N chessboard so
that none of them can attack each other
• Number of possible placements?
• In 8 x 8
64 * 63 * 62 * 61 * 60 * 59 * 58 * 57
= 178,462, 987, 637, 760 / 8!
= 4,426,165,368

• n choose k
• How many ways can you choose k things from a
• set of n items?
• In this case there are 64 squares and we want to
choose 8 of them to put queens on
Reducing the Search Space
• The previous calculation includes set ups like
this one
Q
Q
• Includes lots of set ups with Q
multiple queens in the same Q
column Q

• How many queens can there be QQ


in one column? Q
• Number of set ups
8 * 8 * 8 * 8 * 8 * 8 * 8 * 8 = 16,777,216
• We have reduced search space by two orders of
magnitude by applying some logic
31
Iterative Solution to 8 Queens
• If number of queens is fixed and I realize there can't be
more than one queen per column I can iterate through
the rows for each column
for(int c0 = 0; c0 < 8; c0++){
board[c0][0] = 'q';
for(int c1 = 0; c1 < 8; c1++){
board[c1][1] = 'q';
for(int c2 = 0; c2 < 8; c2++){
board[c2][2] = 'q';
// a little later
for(int c7 = 0; c7 < 8; c7++){
board[c7][7] = 'q';
if( queensAreSafe(board) )
printSolution(board);
board[c7][7] = ' '; //pick up queen
}
board[c6][6] = ' '; // pick up queen
32
N Queens

• What is the problem with this solution?


• The problem with N queens is you don't
know how many for loops to write.

• Recursion is better in these cases.

33
Recursive Backtracking
• You must practice!!!
• Learn to recognize problems that fit the
pattern
• All solutions or a solution?
• Reporting results and acting on results

34
Solving N-Queens Pb
1. Start in the leftmost column
2. If all queens are placed return true
3. Try all rows in the current column. For each row
A. If the queen can be placed safely in this row then mark
this [row, column] as part of the solution and recursively
check if placing queen here leads to a solution.
B. If placing the queen in [row, column] leads to a solution
return true.
C. If placing queen doesn't lead to a solution then unmark
this [row, column] (Backtrack) and go to step (a) to try
other rows.
4. If all rows have been tried and nothing worked
return false to trigger backtracking.
• https://www.geeksforgeeks.org/n-queen-problem-backtrack
ing-3/
Another Backtracking Problem
A Simple Maze
Search maze until way
out is found. If no way
out possible report that.

36
The Local View
Which way do
I go to get
out?
North
West
East

Behind me, to the South


is a door leading South
Three Musts of Recursion

1. Your code must have a case for


all valid inputs.

2. You must have a base case.

3. When you make a recursive call it


should be to a simpler instance (forward
progress towards base case)
Backtracking Approach
• Design recursion function to return success /
failure
1. At each call choose one option and go with it
2. Recursively proceed and see what happens
A. If it works out great otherwise unmake choice and try
again
B. If no option worked return fail result which triggers
backtracking (i.e. un-making earlier decisions)

39
Game of Nim
• The game starts with a pile of 13 coins on the
table. (or any number)
• The 1st player removes 1, 2 or 3 coins.
• The 2nd player does the same.
• The player who has to remove the last coin loses.
Game of Nim
• Working from the end:
▪ If you have 1 coin left you are doomed (bad position)
▪ If you have 2, 3 or 4 coins you can win.
▪ If you have 5 coins you may easily lose (bad position)
▪ If you have 6 coins, move one and you can win (good
move)
▪ …….….….
• A move is good
▪ If it puts your opponent in bad position
• A position is bad
▪ If there are no good moves (mutual recursion)
Game of Nim
int FindGoodMove(int nCoins) {
for (each possible move) {
Evaluate the position that results
from making that move. If the
resulting position is bad
return that move.
}
Return a sentinel value indicating
that no good move exists.
}
Computer Turn

Human Turn
Game of Nim
int FindGoodMove(int nCoins) {
for (int n = 1; n <= MAX_MOVE; n++)
if (IsBadPosition(nCoins - n))
return n;
return NO_GOOD_MOVE;
}

bool IsBadPosition(int nCoins) {


if (nCoins == 1) return true;
return FindGoodMove(nCoins) ==
NO_GOOD_MOVE;
}
Design of Nim
• Welcome message
• While (nCoins > 1)
▪ Display state

▪ If human’s turn
• Get user move
▪ If computer’s turn
• Get best move

▪ Update state and decide next turn


• Declare winner
int main() {
int nCoins = N_COINS;
Design of
playerT whoseTurn = Human;
GiveInstructions();
Nim
while (nCoins > 1) {
DisplayState(nCoins, whoseTurn);
switch (whoseTurn) {
case Human: n = GetUserMove(nCoins);
break;
case Computer: n =
ChooseComputerMove(nCoins);
cout << “I take " << n << "." << ;
}
UpdateState (nCoinsn, whoseTurn);
}
AnnounceWinner(nCoins, whoseTurn);
}
Common Concepts
• Game State defines the current the status
of the game (board, turne, tc.) at a specific
point in time.
• A move is a possible action on the game.

• Generally, you may capture these in types


stateT and moveT.
main
Program
for a Game

Big O Notation
49
3. Mini-Max
• A backtracking algorithm used in decision
making, game theory and artificial
intelligence (AI).
• It is used to find the optimal move for a
player, assuming that the opponent is also
playing optimally.

51
3. Mini-Max
• For complex games it is impossible to
examine every possible outcome and can
take billions of years.
• In 1997, IBM’s “Deep Blue” supercomputer
beat the reigning world champion at that
time, Garry Kasparov.
▪ Not by exhaustive analysis of all possible
games; but instead it looked ahead a few
steps

52
3. Mini-Max
• The recursive concepts of good moves
and bad positions from the Nim game can
be generalized.
• The best move in any position is the one
that leaves your opponent in the worst
position.
• This is called the minimax algorithm
because the goal is to find the move that
minimizes your opponent’s maximum
opportunity. 53
MiniMax
Can choose this option
But end up in this state
This branch is better
Cannot be worse than -2
This branch is better
Cannot be worse than -2

• This position is hardly ideal, but is better


for you than the other possible outcomes,
assuming that your opponent is playing
rationally.
Limiting Search Depth
• Sometimes it is infeasible to search entire tree.
▪ If possible, do 2 mutually recursive functions, one that
finds the best move and one to evaluate position.
• If so, we need to cut off the search at a certain
point, at some max search depth, 5 or 6.
▪ If the game ends before that limit, evaluate the final
position by checking to see who won the game return
WINNING_POSITION or LOSING_POSITION.
▪ If you reach the limit, evaluate the game with static
analysis which depends only on the state of the game
as it stands.
▪ This is just an approximation of reality

You might also like