中山大學人工智能實驗4 Futoshiki Puzzle ( Forward Checking)

描述

在這裏插入圖片描述
在這裏插入圖片描述

解決方案

#棋盤給定的值
board = [
    [0,0,0,7,3,8,0,5,0],
    [0,0,7,0,0,2,0,0,0],
    [0,0,0,0,0,9,0,0,0],
    [0,0,0,4,0,0,0,0,0],
    [0,0,1,0,0,0,6,4,0],
    [0,0,0,0,0,0,2,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,6]
]


#棋盤給定的不等式關係
#其中-1,1,-2,2分別表示向左,右,上,下指的箭頭
#箭頭將在下面被解碼爲不等式,並保存在字典中

ineq = [
    [0,-1,1,0,0,0,0,0,0],
    [0,0,0,0,-1,0,2,-1,0],
    [1,0,0,-1,0,9,0,0,0],
    [0,2,1,-2,1,2,-1,1,0],
    [0,-1,0,0,2,0,0,0,2],
    [0,0,-1,0,0,-1,1,0,2],
    [0,-2,0,0,-1,0,0,2,0],
    [0,0,2,0,0,0,0,0,2],
    [0,-2,0,0,0,-2,-1,0,0]
]
from copy import deepcopy

sample = [1,2,3,4,5,6,7,8,9]
visited = [[0 for _ in range(9)] for _ in range(9)]
domain = [[[0] for _ in range(9)] for _ in range(9)]
ineqnum = [[0 for _ in range(9)] for _ in range(9)]
greater = {}
less = {}
for i in range(9):
    for j in range(9):
        if board[i][j]:
            domain[i][j] = [board[i][j]]
        else:
            domain[i][j] = deepcopy(sample)
        if ineq[i][j]==-1:
            greater[(i,j)] = (i,j-1)
            less[(i,j-1)] = (i,j)
            ineqnum[i][j]+=1
            ineqnum[i][j-1]+=1
        elif ineq[i][j] == 1:
            greater[(i,j)] = (i,j+1)
            less[(i,j+1)] = (i,j)
            ineqnum[i][j]+=1
            ineqnum[i][j+1]+=1
        elif ineq[i][j] == -2:
            greater[(i,j)] = (i-1,j)
            less[(i-1,j)] = (i,j)
            ineqnum[i][j]+=1
            ineqnum[i-1][j]+=1
        elif ineq[i][j] == 2:
            greater[(i,j)] = (i+1,j)
            less[(i+1,j)] = (i,j)
            ineqnum[i][j]+=1
            ineqnum[i+1][j]+=1
greater[(5,6)] = (6,6)
less[(6,6)] = (5,6)
#根據所有不等式關係壓縮值域
def update(domain):
    
    for key in greater:
        sx,sy = key
        ex,ey = greater[key]
        if len(domain[sx][sy])==0:
            return domain
        if len(domain[ex][ey])==0:
            return domain
        val = max(domain[sx][sy])
        domain[ex][ey] = [ele for ele in domain[ex][ey] if ele<=val]
        
    for key in less:
        sx,sy = key
        ex,ey = less[key]
        if len(domain[sx][sy])==0:
            return domain
        if len(domain[ex][ey])==0:
            return domain
        val = min(domain[sx][sy])
        domain[ex][ey] = [ele for ele in domain[ex][ey] if ele>=val]
    
    return domain




def forward_check(domain,x,y):
    #向前檢驗函數,用給定的x,y位置的信息更新domain
    if len(domain[x][y])!=1:
        return domain
    val = domain[x][y][0]
    #橫向縱向去重
    for j in range(9):
        if j!=y:
            try:
                domain[x][j].remove(val)
            except:pass
        if j!=x:
            try:
                domain[j][y].remove(val)
            except:pass
    #不等式關係去重
    domain = update(domain)
    return domain
def fail(domain):
    #檢測當前是否有空域,如果有說明當前的回溯失敗了
    for row in domain:
        for lst in row:
            if len(lst)==0:
                return True
    return False

def success(domain):
    #檢測當前是否已經完成搜索,如果完成則每個格子的visited都爲1
    for row in domain:
        for lst in row:
            if len(lst)!=1:
                return False
    #檢測約束條件
    for i in range(9):
        for j in range(9):
            if not visited[i][j]:
                return False
            if greater.get((i,j)):
                x,y = greater[(i,j)]
                if domain[x][y]>=domain[i][j]:
                    return False
    return True
fal = -999
succ = 999


for i in range(9):
    for j in range(9):
        if len(domain[i][j])==1:
            domain = forward_check(domain,i,j)
            visited[i][j] = 1


from numpy import array

def backtrack(domain):
    
    
    if fail(domain):
        return fal
    
    if success(domain):
        return domain
    
    
    #從domain中選出值域最小的未被選擇的格子
    #在值域最小的格子中選擇不等關係最多的格子
    x = y = 0;
    minlen = 10
    for i in range(9):
        for j in range(9):
            if (not visited[i][j]) and len(domain[i][j])<minlen:
                x = i;y = j;
                minlen = len(domain[i][j])
            elif (not visited[i][j]) and len(domain[i][j])==minlen:
                if ineqnum[i][j]>ineqnum[x][y]:
                    x = i;y = j;
    if minlen==10:
        return fal
    visited[x][y] = 1
    for value in domain[x][y]:
        new_domain = deepcopy(domain)
        new_domain[x][y] = [value]#只保留一個值
        new_domain = forward_check(new_domain,x,y)#前向檢測
        result = backtrack(new_domain)
        if result!=fal:
            return (result)
    visited[x][y] = 0
    return fal


backtrack(domain)

運行結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章