Skip to content

Commit

Permalink
V5
Browse files Browse the repository at this point in the history
  • Loading branch information
RainyCodeWizard committed Jun 10, 2020
1 parent f2f8203 commit 50b846e
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

*.txt
4 changes: 2 additions & 2 deletions CS3243_P2_Sudoku_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ def conflict(self, var1, var2):
def regenerateCurrDomains(self):
''' Create a copy of domains as currDomains '''
# self.currDomains = dict()
if not self.currDomains:
for var in self.domains:
if self.currDomains is None:
for var in self.variables:
self.currDomains[var] = {i for i in self.domains[var]}
return

Expand Down
17 changes: 12 additions & 5 deletions CS3243_P2_Sudoku_v4.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import copy
from random import shuffle
from time import time
from sortedcontainers import SortedSet
#from sortedcontainers import SortedSet
# from collections import defaultdict, Counter
# Running script: given code can be run with the command:
# python file.py, ./path/to/init_state.txt ./output/output.txt
Expand Down Expand Up @@ -39,13 +39,19 @@ def initialiseCSP(self, puzzle):
def solve(self):

# self.ans is a list of lists
#print("BEFORE")
#self.printKeyVal(self.csp.domains)
self.AC3(self.csp)
#print("AFTER")
#self.printKeyVal(self.csp.curr_domains)
assignment = self.backtracking_search(self.csp)
for var in assignment:
(i, j) = var.coordinate
self.ans[i][j] = assignment[var]
return self.ans

def printKeyVal(self, cspAttr):
for key, value in cspAttr.items():
print(key, value)
# def dom_j_up(self, csp, queue):
# return SortedSet(queue, key=lambda t: -(len(csp.curr_domains[t[1]])))

Expand Down Expand Up @@ -296,18 +302,19 @@ def mac(self, csp, var, value, assignment, removals):
return self.AC3(csp, {(X, var) for X in csp.neighbors[var]}, removals)

# The search, proper

def complete(self, assignment,csp):
return len(assignment) == len(csp.variables)
def backtracking_search(self, csp):
"""[Figure 6.5]"""
assignment = csp.infer_assignment()
def backtrack(assignment):
# self.printAssignment(assignment)
# print(len(assignment), len(csp.variables))
if len(assignment) == len(csp.variables):
if self.complete(assignment,csp):
return assignment
var = self.mrv(assignment, csp)
for value in self.lcv(var, assignment, csp):
if 0 == csp.nconflicts(var, value, assignment):
if 0 == csp.nconflicts(var, value, assignment): #if consistent
csp.assign(var, value, assignment)
removals = csp.suppose(var, value)
if self.forward_checking(csp, var, value, assignment, removals):
Expand Down
270 changes: 270 additions & 0 deletions CS3243_P2_Sudoku_v5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# CS3243 Introduction to Artificial Intelligence
# Project 2, Part 1: Sudoku

import sys
import copy
from collections import deque
from time import time

# Running script: given code can be run with the command:
# python file.py, ./path/to/init_state.txt ./output/output.txt

class Sudoku(object):
def __init__(self, puzzle):
# you may add more attributes if you need
self.puzzle = puzzle # self.puzzle is a list of lists
self.ans = copy.deepcopy(puzzle) # self.ans is a list of lists
self.csp = CSP(puzzle)

def solve(self):
self.AC3(self.csp)
assignment = self.backtrackingSearch(self.csp)
for var in assignment:
(i, j) = var.coordinate
self.ans[i][j] = assignment[var]
# self.ans is a list of lists
return self.ans



def backtrackingSearch(self, csp):
assignment = csp.inferAssignment()
return self.backtrack(csp, assignment)

def backtrack(self, csp, assignment = dict()):
if self.complete(assignment,csp):
return assignment

var = self.mrv(csp, assignment)
for value in self.lcv(var, assignment, csp):
if self.isConsistent(var, value, assignment, csp):
assignment[var] = value
#print(var)
csp.unassignedVars.remove(var)
removals = self.assume(var, value, csp)

# self.printAssignment(assignment)
# inferences = self.inference(csp, var, value)
if self.forwardChecking(var, value, assignment, removals,csp):
result = self.backtrack(csp, assignment)
if result:
return result
csp.unassignedVars.add(var)
self.addBack(removals,csp)
if var in assignment:
#print("@@@@@@@@@@@@@@@@@@@@@@")
#print(var)

del assignment[var]
# del inferences from assignment
return False

def forwardChecking(self, var, val, assignment, removals, csp):
csp.copyCurrDomain()
for N in csp.neighbour[var]:
if N not in assignment:
for value in csp.currDomains[N].copy():
if csp.constraints[(var,N)](val,value):
csp.currDomains[N].remove(value)
removals.append((N,value))
if not csp.currDomains[N]:
return False
return True

def assume(self, var, value, csp):
csp.copyCurrDomain()
removals = list()
for val in csp.currDomains[var]:
if val != value:
removals.append((var,val))
csp.currDomains[var] = {value}
return removals

def addBack(self, removals, csp):
for (var,val) in removals:
csp.currDomains[var].add(val)

def complete(self, assignment,csp):
return len(assignment) == len(csp.variables)

def isConsistent(self,var, val, assignment, csp):
for key in assignment:
try:
if csp.constraints[(var,key)](val,assignment[var]):
return False
except:
continue
return True

def mrv(self,csp,assignment): # minimum remaining values
seq = csp.unassignedVars.copy() #filter(lambda var: var not in assignment,csp.variables)
return min(seq,key=lambda var: len(csp.currDomains[var]))

###check one more time ###
def lcv(self, var, assignment,csp): # least constraining values
def numConflicts(val): #number of conflicts
conflict = 0
for key in assignment:
try:
if csp.constraints[(var,key)](val,assignment[var]):
conflict +=1
except:
continue
return conflict
ls = list(csp.currDomains[var])
ls.sort(key = numConflicts)
return ls
# def numConflicts(self, csp, var, assignment,val): #number of conflicts
# conflict = 0
# for key in assignment:
# if csp.constraints[(var,key)](val,assignment[var]):
# conflict +=1
# return conflict





def AC3(self,csp):
arc_q = deque()
for cons in csp.constraints:
arc_q.append(cons)
csp.copyCurrDomain()
while arc_q:
(xi, xj) = arc_q.popleft()
if self.revise(csp, xi, xj):
if not csp.currDomains[xi]:
return False
for xk in csp.neighbour[xi]:
if xk != xj:
arc_q.append((xk, xi))
return True
def revise(self, csp, xi, xj):
revised = False
for x in csp.currDomains[xi].copy():
conflict = True
#if any(map(lambda y : csp.constraints[(xi, xj)](x, y),csp.currDomains[xj])):
#xi.domain.remove(x)
for y in csp.currDomains[xj]:
if not csp.constraints[(xi,xj)](x,y):
conflict = False
if not conflict:
break
if conflict:
csp.currDomains[xi].remove(x)
revised = True
return revised

# you may add more classes/functions if you think is useful
# However, ensure all the classes/functions are in this file ONLY
# Note that our evaluation scripts only call the solve method.
# Any other methods that you write should be used within the solve() method.
class Variable(object):
def __init__(self, coordinate, value):
self.coordinate = coordinate # (x, y)
self.value = value

def __hash__(self):
return hash(self.coordinate)

def __eq__(self, var):
return self.coordinate == var.coordinate

def __str__(self):
return str(self.coordinate)

def isSameUnit(self, var):
return self.isSameRow(var) or self.isSameCol(var) or self.isSameSquare(var)

def isSameRow(self, var):
return self.coordinate[0] == var.coordinate[0]

def isSameCol(self, var):
return self.coordinate[1] == var.coordinate[1]

def isSameSquare(self, var):
''' square refers to 3*3 square '''
return (self.coordinate[0]//3, self.coordinate[1]//3) == (var.coordinate[0]//3, var.coordinate[1]//3)

class CSP(object):
def __init__(self, puzzle): #removed constraints

self.variables = set() # set of var
self.domains = dict() # key = var, value = var_domain (set)
self.neighbour = dict() # key = var, value = var_neighbour (set)

# set of binary constraints (constraint function involving two var) represented by pair(scope, relation)
# scope = (x, y), relation = f(x, y), where x, y are vars
self.constraints = dict()
self.initialiseCSP(puzzle)
self.unassignedVars = self.variables.copy()
self.currDomains = None

def initialiseCSP(self,puzzle): #initialise csp
# going through 2d list
for i in range(9):
for j in range(9):
self.variables.add(Variable((i, j), puzzle[i][j]))
# going through var set
for var1 in self.variables:
self.domains[var1] = set([1, 2, 3, 4, 5, 6, 7, 8, 9]) if not var1.value else set([var1.value])
self.neighbour[var1] = set()
for var2 in self.variables:
if var1 != var2 and var1.isSameUnit(var2):
self.constraints[(var1, var2)] = lambda x,y: x==y #to check if in conflict
self.neighbour[var1].add(var2)

def copyCurrDomain(self):
''' Create a copy of domains as currDomains '''
if self.currDomains is None:
self.currDomains = dict()
for var in self.variables:
self.currDomains[var] = {i for i in self.domains[var]}
return
def inferAssignment(self):
assignment = {}
print("infer assignment")
for var in self.currDomains:
if len(self.currDomains[var])==1:
assignment[var] = list(self.currDomains[var])[0]
self.unassignedVars.remove(var)
return assignment

if __name__ == "__main__":
# STRICTLY do NOT modify the code in the main function here
if len(sys.argv) != 3:
print ("\nUsage: python CS3243_P2_Sudoku_XX.py input.txt output.txt\n")
raise ValueError("Wrong number of arguments!")

try:
f = open(sys.argv[1], 'r')
except IOError:
print ("\nUsage: python CS3243_P2_Sudoku_XX.py input.txt output.txt\n")
raise IOError("Input file not found!")

puzzle = [[0 for i in range(9)] for j in range(9)]
lines = f.readlines()

i, j = 0, 0
for line in lines:
for number in line:
if '0' <= number <= '9':
puzzle[i][j] = int(number)
j += 1
if j == 9:
i += 1
j = 0

sudoku = Sudoku(puzzle)
start = time()
ans = sudoku.solve()
end = time()
# outfile.close()
# print(ans)
with open(sys.argv[2], 'a') as f:
for i in range(9):
for j in range(9):
f.write(str(ans[i][j]) + " ")
f.write("\n")
f.write("time taken: " + str(end-start) + "\n")
f.write("-------------------------------\n")

0 comments on commit 50b846e

Please sign in to comment.