Skip to content

Commit

Permalink
hitori_heuristic
Browse files Browse the repository at this point in the history
  • Loading branch information
phongnguyentu1104 committed Apr 20, 2024
1 parent ea06059 commit a90a4e9
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 0 deletions.
209 changes: 209 additions & 0 deletions hitori_heuristic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
from timeit import default_timer
from inspect import stack
import os
import psutil

class HitoriPuzzleSolover:
def __init__(self, array: list, level: int):
self.begin_array = array
self.array = array
self.level = level
self.log = []
self.latest = array

def print_array(self, array, step=-1):
if step == 0:
print('-----Init-----')
elif step == -1:
if array:
print('------Result-----')
else:
print('=====Can not Solve=====')
return
else:
print('-----Step '+str(step)+'-----')
for row in array:
for e in row:
print(e, end=' ')
print()

def print_log(self):
# In ra lịch sử các bước giải quyết
for i, v in enumerate(self.log[::-1]):
self.print_array(v, i)
self.print_array(self.array)

def copy_array(self, array):
return [row.copy() for row in array]

def solve(self):
self.array = self.explore_and_fill(self.array)

def explore_and_fill(self, array, row=0, col=0, step=0):
# tìm kiếm và điền giá trị vào ma trận
if array != self.latest:
self.print_array(array, step)
self.latest = array

if row == self.level:
self.log.append(array)
return array

t_array = self.copy_array(array)

if t_array[row][col] != 'x' and self.is_not_good_position(t_array, row, col) and self.is_valid_move(t_array, row, col, t_array[row][col], True):
stacks.append((row, col, step))

if t_array[row][col] != 'x' and not self.is_not_good_position(t_array, row, col) and self.is_valid_move(t_array, row, col, t_array[row][col], True):
t_array[row][col] = 'x'
res = self.explore_and_fill(t_array, (row if col < self.level-1 else row+1), (col+1 if col < self.level-1 else 0), step+1)
if res:
self.log.append(array)
return res

if self.is_valid_move(t_array, row, col, self.begin_array[row][col], False):
t_array[row][col] = self.begin_array[row][col]
res = self.explore_and_fill(t_array, (row if col < self.level-1 else row+1), (col+1 if col < self.level-1 else 0), step)
if res:
self.log.append(array)
return res
elif len(stacks) > 0:
temp = stacks.pop()
new_step = step
if self.is_valid_move(t_array, row, col, t_array[row][col], True):
t_array[temp[0]][temp[1]] = 'x'
else:
t_array[temp[0]][temp[1]] = 'x'
row = temp[0]
col = temp[1]
new_step = temp[2]
for i in range (input_level):
for j in range (input_level):
if (i > temp[0]):
t_array[i][j] = self.begin_array[i][j]
if i == temp[0] and j > temp[1]:
t_array[i][j] = self.begin_array[i][j]

res = self.explore_and_fill(t_array, (row if col < self.level-1 else row+1), (col+1 if col < self.level-1 else 0), new_step+1)
if res:
self.log.append(array)
return res

return None

def count_adjacent_occurrences(self, array, row, col):
# Đếm số lần xuất hiện của giá trị tại ô (row, col) trong hàng và cột tương ứng
m = 0
for i in range(self.level):
if array[row][i] == array[row][col] and i != col:
m += 1
if array[i][col] == array[row][col] and i != row:
m += 1
return m

def is_not_good_position(self, array, row, col):
#Kiểm tra ô có nen lay hay không
count_before = sum(1 for i in range(self.level) if i < row and array[i][col] == array[row][col]) + sum(1 for i in range(self.level) if i < col and array[row][i] == array[row][col])
count_last = sum(1 for i in range(self.level) if i > row and array[i][col] == array[row][col]) + sum(1 for i in range(self.level) if i > col and array[row][i] == array[row][col])
if count_before > 0:
return False
if count_last == 0:
return False
list_of_op = [self.count_adjacent_occurrences(array, row, i) for i in range(self.level) if col != i and array[row][col] == array[row][i]] + [self.count_adjacent_occurrences(array, i, col) for i in range(self.level) if row != i and array[row][col] == array[i][col]]
return len(list_of_op) == 0 or self.count_adjacent_occurrences(array, row, col) < min(list_of_op)

def is_valid_move(self, array, row, col, op, is_fill):
#Kiểm tra nước đi có hợp lệ.
def check_row_column(lst):
return lst.count(op) > 1

def has_adjacent_patterns():

if (row > 0 and row < self.level - 1 and (array[row-1][col] == 'x' or array[row+1][col] == 'x')):
return False
elif (col > 0 and col < self.level - 1 and (array[row][col-1] == 'x' or array[row][col+1] == 'x')):
return False
elif (row == col == 0 and (array[0][1] == 'x' or array[1][0] == 'x')):
return False
elif (row == col == self.level - 1 and (array[row][col-1] == 'x' or array[row-1][col] == 'x')):
return False
elif (row == 0 and col == self.level - 1 and (array[row][col-1] == 'x' or array[1][col] == 'x')):
return False
elif (row == self.level - 1 and col == 0 and (array[row-1][col] == 'x' or array[row][col+1] == 'x')):
return False
elif (row == self.level - 1 and col > 0 and col < self.level - 1 and (array[row-1][col] == 'x')):
return False
elif (col == self.level - 1 and row > 0 and row < self.level - 1 and (array[row][col-1] == 'x')):
return False
elif (row == 0 and col > 0 and col < self.level - 1 and (array[row+1][col] == 'x')):
return False
elif (col == 0 and row > 0 and row < self.level - 1 and (array[row][col+1] == 'x')):
return False
return True

def non_shape_pattern(i, j):
if (i == j == 0 and array[0][1] == 'x' and array[1][0] == 'x'):
return True
elif (i == 0 and j == self.level - 1 and array[0][j-1] == 'x' and array[1][j] == 'x'):
return True
elif (i == self.level - 1 and j == 0 and array[i][1] == 'x' and array[i-1][0] == 'x'):
return True
elif (i == j == self.level - 1 and array[i-1][j] == 'x' and array[i][j-1] == 'x'):
return True
elif (j == self.level - 1 and i > 0 and i < self.level - 1 and array[i-1][j] == 'x' and array[i+1][j] == 'x' and array[i][j-1] == 'x'):
return True
elif (i == self.level - 1 and j > 0 and j < self.level - 1 and array[i-1][j] == 'x' and array[i][j-1] == 'x' and array[i][j+1] == 'x'):
return True
elif (i == 0 and j > 0 and j < self.level - 1 and array[i][j-1] == 'x' and array[i][j+1] == 'x' and array[i+1][j] == 'x'):
return True
elif (j == 0 and i > 0 and i < self.level - 1 and array[i-1][j] == 'x' and array[i+1][j] == 'x' and array[i][j+1] == 'x'):
return True
elif (i > 0 and i < self.level - 1 and j > 0 and j < self.level - 1 and array[i-1][j] == 'x' and array[i+1][j] == 'x' and array[i][j+1] == 'x' and array[i][j-1] == 'x'):
return True
return False

def is_non_shaded_valid():
for i in range(self.level):
for j in range(self.level):
if (array[i][j] != 'x' and non_shape_pattern(i, j)):
return False
return True

if is_fill:
if (check_row_column(array[row]) or check_row_column([array[i][col] for i in range(self.level)])) and has_adjacent_patterns() and is_non_shaded_valid():
return True
if not is_fill:
for i in range(col):
if (array[row][i] == op):
return False
for i in range(row):
if (array[i][col] == op):
return False
return True

def process_memory():
process = psutil.Process(os.getpid())
mem_info = process.memory_info()
return mem_info.rss

start = default_timer()
memory_before = process_memory()
stacks = []
with open('testcase10x10.txt', 'r') as f:
input_data = f.read().split('\n')
input_level = 0
input_array = []
for i in range(len(input_data)):
temp = input_data[i].split(',')
input_level = len(temp)
input_row = []
for j in range(input_level):
input_row.append(temp[j])
input_array.append(input_row)

solver = HitoriPuzzleSolover(input_array, input_level)
solver.solve()
memory_after = process_memory()
print('Memory Usage:', memory_after - memory_before, 'bytes')
stop = default_timer()
print('Execution Time: ', stop - start)
10 changes: 10 additions & 0 deletions testcase10x10.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
4,3,4,7,3,8,6,1,10,7
4,4,6,8,8,9,10,10,3,9
3,10,5,4,6,9,7,2,7,9
5,2,2,5,4,3,8,6,6,4
5,7,9,8,4,4,2,7,6,10
10,5,6,6,9,4,9,10,5,1
2,4,10,7,3,6,5,2,1,7
7,8,7,1,8,10,10,9,4,1
10,9,2,1,1,5,4,7,8,6
7,6,5,10,9,10,7,6,5,4

0 comments on commit a90a4e9

Please sign in to comment.