zoukankan      html  css  js  c++  java
  • 使用深度优先搜索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('
    ')
        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+'
    '+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+'
    '+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('
    ')
        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星写入,结果答案就只剩一半了。。。

  • 相关阅读:
    idea控制台乱码问题解决
    部署springboot项目到云服务器的两种方式(jar+war)
    case...when...then....(else...)....end的使用小案例
    多线程学习(二)
    多线程学习(一)
    Swagger学习笔记
    Docker进阶篇
    Docker入门基础篇
    Markdown语法学习(推荐使用Typora编辑器)
    git(六)——IDEA中使用git
  • 原文地址:https://www.cnblogs.com/dgutfly/p/11961079.html
Copyright © 2011-2022 走看看