0% found this document useful (0 votes)
30 views

AI Lab Assignment#02

The document describes code for solving the N-Queens problem using a genetic algorithm approach. It defines functions for generating random chromosomes to represent board configurations, calculating their fitness based on conflicts, selecting chromosomes for reproduction probabilistically based on fitness, performing crossover and mutation to generate new chromosomes, and running the genetic algorithm over generations until a solution is found. The code is tested by running it to find a solution for an 8 Queens problem.

Uploaded by

Babar Ali
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views

AI Lab Assignment#02

The document describes code for solving the N-Queens problem using a genetic algorithm approach. It defines functions for generating random chromosomes to represent board configurations, calculating their fitness based on conflicts, selecting chromosomes for reproduction probabilistically based on fitness, performing crossover and mutation to generate new chromosomes, and running the genetic algorithm over generations until a solution is found. The code is tested by running it to find a solution for an 8 Queens problem.

Uploaded by

Babar Ali
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

AI LAB

Lab Assignment # 02
Name: Parkash
Student ID: 11089
Class ID: 110029
Question:
Code:
import numpy as np
import random
import math
import time as tm

class NQueens:
def __init__(self, num_cols, board=None, seed=0):
self._rand_obj = np.random.RandomState()
self._rand_obj.seed(seed)
self._board_size = num_cols
self._board = board
self.seed = seed
if (board is None):
self._new_board()

def _new_board(self):
self._board = [None] * self._board_size
for i in range(self._board_size):
self._board[i] = self._rand_obj.randint(0, self._board_size - 1)

def size(self):
return (self._board_size)
def place_queen(self, row, col):
self._board[col] = row

# we only need the col to remove the queen


def remove_queen(self, col):
self._board[col] = None

def reset(self): # O(n)


self._new_board()

def get_board(self):
return (self._board)

# Prints Board
def draw(self):
temp_str = ""
temp_board = self._board[:]
for i in range(self._board_size):
find_indices = []
cumul_ind = 0
try:
for j in range(0, temp_board.count(i)):
find_indices.append(temp_board.index(i))
temp_board[temp_board.index(i)] = None
find_indices.sort()
temp_str += ("x " * (find_indices[0])) + "Q" + " "
cumul_ind += find_indices[0]
for j in range(1, len(find_indices)):
cumul_ind += find_indices[j]
temp_str += ("x " * (find_indices[j] - find_indices[j - 1] - 1)) + \
"Q" + " "
temp_str += ("x " * (self._board_size - cumul_ind - 1))
except:
temp_str = temp_str + ("x " * self._board_size)
temp_str += "\n"
print(temp_str)

class _local_node:
def __init__(self, state, h_val):
self.state = state
self.h = h_val

class NQueensAgent:
def __init__(self, initial_state, seed):
self._curr_node = _local_node(initial_state, self.h(initial_state))
self._rand_obj = np.random.RandomState()
self._rand_obj.seed(seed)
self.seed = seed

def get_state(self):
return (self._curr_node.state)

def get_h(self):
return (self._curr_node.h)

def h(self, state):


total_h = 0
for col in range(state.size()):
row = state.get_board()[col]
# We only need to check the columns in front of the current Queen
for i in range(col + 1, state.size()):
if (state.get_board()[i] == row):
total_h += 1
# Handle upper diagonal
elif (state.get_board()[i] == (row - (col - i))):
total_h += 1
# Handle lower diagonal
elif (state.get_board()[i] == (row + (col - i))):
total_h += 1

return (total_h)

def expand_children(self, state):


#Generates list of possible states (NQueens boards) from given state)
child_list = []
num_cols = state.size() # num_cols and num_rows are equal
lowest_h = -1
# We'll shuffle the order so that we don't get as much bias in our choices
col_list = list(range(num_cols))
self._rand_obj.shuffle(col_list)

for i in col_list:
for j in range(1, num_cols):
temp_state = NQueens(num_cols, state._board[:], state.seed)
old_q_p = temp_state.get_board()[i]
temp_state.place_queen((old_q_p + j) % num_cols, i)
temp_node = _local_node(temp_state, self.h(temp_state))
# We always insert the node with the lowest h
# at the beginning of the list and sometimes when the current h is
# equal to the lowest h (controlled by randint)
if (lowest_h < 0 or temp_node.h <= lowest_h):
lowest_h = temp_node.h
if (temp_node.h < lowest_h or self._rand_obj.randint(0, 1) == 0):
child_list.insert(0, temp_node)
else:
child_list.append(temp_node)
else:
child_list.append(temp_node)
return (child_list)

def _local_hill_climb(self):
#Implments a form of Local Hill Climb for NQueens
prev_node = self._curr_node
curr_node = self._curr_node
children = self.expand_children(self._curr_node.state)
while curr_node.h > 0:
# save a bit of computation, by only expanding when needed
if (prev_node != curr_node):
children = self.expand_children(curr_node.state)
# Get neighbor node w/ lowest value (which we inserted w/ our expansion)
neighbor_node = children[0]
if ((neighbor_node is None) or (neighbor_node.h >= curr_node.h)):
break
curr_node = neighbor_node
curr_node.state.draw()
# Now that we've found our final state, change to that state
self._curr_node = curr_node

def find_best_answer(self):
self._local_hill_climb()
# --------Code to run NQueens Agent--------#
seed = 6
game_size = 4
test_game = NQueens(game_size, None, seed)
print("==Before Searching for Solution==\n")
test_game.draw()
print("---------------------------------\n")

test_agent = NQueensAgent(test_game, seed)


test_time = tm.time()
test_agent.find_best_answer()
test_time = tm.time() - test_time
print("==After Searching for Solution==\n")
test_agent.get_state().draw()
print("Test Agent: ",test_agent.get_h())
print("Test Time: ",test_time)

Output:
Question:
Code:
import random

def random_chromosome(size): #making random chromosomes

return [ random.randint(1, nq) for _ in range(nq) ]

def fitness(chromosome):

horizontal_collisions = sum([chromosome.count(queen)-1 for queen in chromosome])/2

diagonal_collisions = 0

n = len(chromosome)

left_diagonal = [0] * 2*n

right_diagonal = [0] * 2*n

for i in range(n):

left_diagonal[i + chromosome[i] - 1] += 1

right_diagonal[len(chromosome) - i + chromosome[i] - 2] += 1

diagonal_collisions = 0

for i in range(2*n-1):

counter = 0

if left_diagonal[i] > 1:

counter += left_diagonal[i]-1

if right_diagonal[i] > 1:

counter += right_diagonal[i]-1

diagonal_collisions += counter / (n-abs(i-n+1))

return int(maxFitness - (horizontal_collisions + diagonal_collisions)) #28-(2+3)=23

def probability(chromosome, fitness):


return fitness(chromosome) / maxFitness

def random_pick(population, probabilities):

populationWithProbabilty = zip(population, probabilities)

total = sum(w for c, w in populationWithProbabilty)

r = random.uniform(0, total)

upto = 0

for c, w in zip(population, probabilities):

if upto + w >= r:

return c

upto += w

assert False, "Shouldn't get here"

def reproduce(x, y): #doing cross_over between two chromosomes

n = len(x)

c = random.randint(0, n - 1)

return x[0:c] + y[c:n]

def mutate(x): #randomly changing the value of a random index of a chromosome

n = len(x)

c = random.randint(0, n - 1)

m = random.randint(1, n)

x[c] = m

return x

def genetic_queen(population, fitness):

mutation_probability = 0.03

new_population = []

probabilities = [probability(n, fitness) for n in population]

for i in range(len(population)):

x = random_pick(population, probabilities) #best chromosome 1


y = random_pick(population, probabilities) #best chromosome 2

child = reproduce(x, y) #creating two new chromosomes from the best 2 chromosomes

if random.random() < mutation_probability:

child = mutate(child)

print_chromosome(child)

new_population.append(child)

if fitness(child) == maxFitness: break

return new_population

def print_chromosome(chrom):

print("Chromosome = {}, Fitness = {}"

.format(str(chrom), fitness(chrom)))

if __name__ == "__main__":

nq = int(input("Enter Number of Queens: ")) #say N = 8

maxFitness = (nq*(nq-1))/2 # 8*7/2 = 28

population = [random_chromosome(nq) for _ in range(100)]

generation = 1

while not maxFitness in [fitness(chrom) for chrom in population]:

print("=== Generation {} ===".format(generation))

population = genetic_queen(population, fitness)

print("")

print("Maximum Fitness = {}".format(max([fitness(n) for n in population])))

generation += 1

chrom_out = []

print("Solved in Generation {}!".format(generation-1))

for chrom in population:

if fitness(chrom) == maxFitness:

print("");
print("One of the solutions: ")

chrom_out = chrom

print_chromosome(chrom)

board = []

for x in range(nq):

board.append(["x"] * nq)

for i in range(nq):

board[nq-chrom_out[i]][i]="Q"

def print_board(board):

for row in board:

print (" ".join(row))

print()

print_board(board)

Output:

You might also like