使用深度優先搜索DFS求解star battle游戲


  這里的star battle游戲不是指別的(像war frame),就是puzzle team club搞的游戲,在https://www.puzzle-star-battle.com/里面可以找到。

  這里要解題的話,不能再像上回那樣用舞蹈表(dancing link)了,因為游戲規則決定了方塊的占用位置不是全部都要用,一個方格可以相鄰1個或2個星星,無法像之前那樣使用精確覆蓋的做法。但是,這里要解題還是很簡單的,約束條件的某一種(行內必須正好有n個星星)可以利用這點搞DFS。

  star battle的規則如下:放置一定數量的星星在棋盤,使所有的星星鄰近8格沒有星星,且每行、每列、每個區域正好有指定數量的星星,至於指定數量是多少要看星星★左邊是哪個數字

  

 

 

 圖1.1★表示每行每列每塊必須正好有1個星星;3★則是正好有3個

  這里就需要初始化每行每列每塊占據的星星數為指定數字(這里的深度優先搜索參數就是行數,操作也是基於單行搜索,所以省略了每行占據星星數)

def init():
    with open('starBattleChess1.txt','r') as f:
        chessStr = f.read()
    rowStrs = chessStr.split('\n')
    global rowsize, colsize
    rowsize = len(rowStrs)
    colsize = len(rowStrs[0].split(' '))
    maxnum = 0
    for rowStr in rowStrs:
        row = []
        validrow = []
        for j in range(2):
            occupy[j].append(limit) #0是每列,1是每塊
        for j in range(limit):
            answer.append([])
        for colStr in rowStr.split(' '):
            maxnum = maxnum if int(colStr) < maxnum else int(colStr)
            row.append(int(colStr))
            validrow.append(1)
        chess.append(row)
        valid.append(validrow)
View Code

  深度優先搜索部分(基於單行橫向搜索),答案部分用一個謎面長度*限制大小為長度的數組儲存。這里對方格的占據操作使用類似於舞蹈表的消除(remove)和還原(resume)操作

def dfs(depth, occ, start):
    #print('depth : ' + str(depth) + ' occ : ' + str(occ) + ' start : ' + str(start))
    if depth == rowsize:
        print(answer)
        printAnswer()
        return
    for i in range(start, colsize-limit+occ+1):
        if occupy[0][i] <= 0 or occupy[1][chess[depth][i]] <= 0 or valid[depth][i] == 0:
            continue
        resumePos = []
        occupy[0][i] -= 1
        occupy[1][chess[depth][i]] -= 1
        for j in range(depth-1, depth+2):
            for k in range(i-1, i+2):
                if j < 0 or j > rowsize - 1:continue
                if k < 0 or k > rowsize - 1:continue
                if valid[j][k] != 0:
                    valid[j][k] = 0
                    resumePos.append([j, k])
        answer[depth * limit + occ] = [depth, i]
        if occ >= limit - 1:
            dfs(depth+1, 0, 0)
        else:
            dfs(depth, occ+1, i+1)
        for resume in resumePos:
            valid[resume[0]][resume[1]] = 1
        occupy[0][i] += 1
        occupy[1][chess[depth][i]] += 1
View Code

  輸出答案:

def printAnswer():
    defaultImg = ['+','|']
    for _ in range(rowsize):
        defaultImg[0] += '-+'
        defaultImg[1] += ' |'
    imgs = [defaultImg[0]]
    for i in range(rowsize):
        imgs.append(defaultImg[1])
        imgs.append(defaultImg[0])
    for ans in answer:
        a = ans[0]
        b = ans[1]
        imgs[2*a+1] = imgs[2*a+1][:2*b+1] + '*' + imgs[2*a+1][2*b+2:]
    print(reduce(lambda a,b:a+'\n'+b,imgs))
View Code

  總的代碼:

from functools import reduce
import time
chess = []
rowsize = 0
colsize = 0
limit = 2 # 看星星★左邊是哪個數字就填哪個
valid = []
occupy = [[],[]]# 0 is vertical; 1 is block
answer = []
#
def printAnswer():
    defaultImg = ['+','|']
    for _ in range(rowsize):
        defaultImg[0] += '-+'
        defaultImg[1] += ' |'
    imgs = [defaultImg[0]]
    for i in range(rowsize):
        imgs.append(defaultImg[1])
        imgs.append(defaultImg[0])
    for ans in answer:
        a = ans[0]
        b = ans[1]
        imgs[2*a+1] = imgs[2*a+1][:2*b+1] + '*' + imgs[2*a+1][2*b+2:]
    print(reduce(lambda a,b:a+'\n'+b,imgs))
#
def dfs(depth, occ, start):
    #print('depth : ' + str(depth) + ' occ : ' + str(occ) + ' start : ' + str(start))
    if depth == rowsize:
        print(answer)
        printAnswer()
        return
    for i in range(start, colsize-limit+occ+1):
        if occupy[0][i] <= 0 or occupy[1][chess[depth][i]] <= 0 or valid[depth][i] == 0:
            continue
        resumePos = []
        occupy[0][i] -= 1
        occupy[1][chess[depth][i]] -= 1
        for j in range(depth-1, depth+2):
            for k in range(i-1, i+2):
                if j < 0 or j > rowsize - 1:continue
                if k < 0 or k > rowsize - 1:continue
                if valid[j][k] != 0:
                    valid[j][k] = 0
                    resumePos.append([j, k])
        answer[depth * limit + occ] = [depth, i]
        if occ >= limit - 1:
            dfs(depth+1, 0, 0)
        else:
            dfs(depth, occ+1, i+1)
        for resume in resumePos:
            valid[resume[0]][resume[1]] = 1
        occupy[0][i] += 1
        occupy[1][chess[depth][i]] += 1
#
def init():
    with open('starBattleChess1.txt','r') as f:
        chessStr = f.read()
    rowStrs = chessStr.split('\n')
    global rowsize, colsize
    rowsize = len(rowStrs)
    colsize = len(rowStrs[0].split(' '))
    maxnum = 0
    for rowStr in rowStrs:
        row = []
        validrow = []
        for j in range(2):
            occupy[j].append(limit)
        for j in range(limit):
            answer.append([])
        for colStr in rowStr.split(' '):
            maxnum = maxnum if int(colStr) < maxnum else int(colStr)
            row.append(int(colStr))
            validrow.append(1)
        chess.append(row)
        valid.append(validrow)

if __name__ == "__main__":
    init()
    start = time.time()
    dfs(0, 0, 0)
    end = time.time()
    print('search time : ' + str(end-start) + 's')
View Code

  我們在同目錄下創建starBattleChess1.txt文件,並錄入棋盤:

0 1 1 1 1 1 2 2 2 2
0 0 0 3 3 1 4 4 2 2
0 0 0 3 3 3 4 4 2 2
0 5 4 4 4 4 4 4 2 2
0 5 4 6 6 6 6 6 2 2
5 5 5 6 6 6 6 6 6 2
5 5 5 5 5 5 7 7 7 7
5 5 5 5 8 5 7 7 7 7
5 9 9 8 8 8 8 7 7 7
9 9 8 8 8 8 8 7 7 7
View Code

  執行結果:

[[0, 3], [0, 5], [1, 1], [1, 8], [2, 3], [2, 5], [3, 7], [3, 9], [4, 0], [4, 2],
 [5, 6], [5, 8], [6, 1], [6, 4], [7, 7], [7, 9], [8, 2], [8, 4], [9, 0], [9, 6]]

+-+-+-+-+-+-+-+-+-+-+
| | | |*| |*| | | | |
+-+-+-+-+-+-+-+-+-+-+
| |*| | | | | | |*| |
+-+-+-+-+-+-+-+-+-+-+
| | | |*| |*| | | | |
+-+-+-+-+-+-+-+-+-+-+
| | | | | | | |*| |*|
+-+-+-+-+-+-+-+-+-+-+
|*| |*| | | | | | | |
+-+-+-+-+-+-+-+-+-+-+
| | | | | | |*| |*| |
+-+-+-+-+-+-+-+-+-+-+
| |*| | |*| | | | | |
+-+-+-+-+-+-+-+-+-+-+
| | | | | | | |*| |*|
+-+-+-+-+-+-+-+-+-+-+
| | |*| |*| | | | | |
+-+-+-+-+-+-+-+-+-+-+
|*| | | | | |*| | | |
+-+-+-+-+-+-+-+-+-+-+
search time : 0.9020516872406006s

  這個算法還有優化空間。既然這個網站的棋盤都是唯一解,那肯定可以找出查到唯一解的方式。這個后面再說。

  把這個答案錄入網頁:

 

圖2.不要吐槽時間為什么這么久,3天前就打開過又退出了

  嘿嘿,大功告成!

  解這道題時,請拋棄1星棋盤的常規解法,多從多星角度考慮。之前我寫代碼就把最后的答案儲存習慣性按1星寫入,結果答案就只剩一半了。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM