You are on page 1of 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