简体   繁体   English

Python:实现深度优先搜索Freecell Solitaire

[英]Python: Implementing Depth-First Search for Freecell Solitaire

I've been trying so solve the Freecell Solitaire game with a DFS for well over a month now with only partial success. 我一直在尝试用DFS解决Freecell Solitaire游戏一个多月,现在只取得了部分成功。 I'm a novice, with a non-CS background, that used classes and recursion for the first to solve a problem, mainly trying to imitate what I found through Google-searches and Stack Overflow posts. 我是一个新手,具有非CS背景,第一个使用类和递归来解决问题,主要是试图模仿我通过Google搜索和Stack Overflow帖子发现的内容。 (general comments on code clarity etc are more than welcome) (关于代码清晰度等的一般评论非常受欢迎)

The code for the game is here: https://pastebin.com/39KGZAW1 , which I hope is understandable. 游戏的代码在这里: https//pastebin.com/39KGZAW1 ,我希望这是可以理解的。 The idea behind it is: 它背后的想法是:

  • Create instances of the Board (class), something like snapshots in the game 创建Board (类)的实例,类似于游戏中的快照
  • Find all possible moves 找到所有可能的动作
  • Create one new instance per move and update (class function) the board with that move 每次移动创建一个新实例,并使用该移动update (类功能)板

The problems are: 问题是:

  • It seems to be taking too long (the computer ran out of memory when I tried saving every board, so I stopped and used a generator and a deque), never finding a solution 这似乎花了太长时间(当我尝试保存每个电路板时计算机内存不足,所以我停下来使用了发电机和双端电池),从未找到解决方案
  • When I tried using mini_stacks (a card deck with less than 13 cards, currently just 1s and 2s!) it doesn't seem to be finding any solutions either. 当我尝试使用mini_stacks (一张卡片少于13张,当前只有1张和2张!)它似乎也找不到任何解决方案。
  • The recursive approach seems faster (at least comparing how many Boards instances are created) than the two implementations of the while-loop 递归方法似乎比while循环的两个实现更快(至少比较创建了多少个Boards实例)

More details: After the game logic is coded, I mainly tried two approaches to the DFS. 更多细节:在对游戏逻辑进行编码之后,我主要尝试了两种DFS方法。 The first was with a recursive function: 第一个是递归函数:

def rec(board):

    if not board.moves:
        return 
    else:
        for i in board.moves:
            new = copy.deepcopy(board) # instead of a deep copy I also tried 
            # creating a new instance taking inputs directly from the board
            globals()["it"] += 1 # for lack of a better way
            new.tt = globals()["it"]
            new.update(i)
            if new._end == True:
                raise Exception("Solved!") # didn't focus on this yet
            boards.append(new)
            rec(new)

game = Board(mini_stacks) # or full_stacks, to initialize the recursion
rec(game) # start the recursion with the game

The second approach was using a while loop: 第二种方法是使用while循环:

game = Board(mini_stacks)
boards = deque()
boards.append(game)
while boards:  

    current_search = boards.popleft()
    if current_search._end:
        print("Win")
        winning = copy.deepcopy(current_search)
        break # win

    if current_search.moves:
        for no,move in enumerate(current_search.moves):
            new = copy.deepcopy(current_search)
            it += 1
            new.tt = it
            new.update(move)
            boards.insert(no,new)

With a slight modification, I created a generator function (also new to the concept) and used it for the while loop, adding a stack (=deque?): 稍作修改,我创建了一个生成器函数(也是概念的新功能)并将其用于while循环,添加了一个堆栈(= deque?):

def next_generator(boards=boards):

    if boards[0].moves:
        for no,move in enumerate(boards[0].moves):
            new = copy.deepcopy(boards[0])
            globals()["it"] += 1
            new.tt = globals()["it"]
            new.update(move)
            boards.append(new)
        yield boards.popleft()

while True:

    current_search = next(next_generator())
    if current_search._end:
        print("Win")
        winning = copy.deepcopy(current_search)
        break # win 

game = Board(mini_stacks)
boards = deque()
boards.append(game)
next_generator()

So after taking a break I found it. 所以休息后我找到了。 As it seems, it was not the recurion nor the while loops. 看起来,它不是复发,也不是while循环。 I did the following: 我做了以下事情:

Major changes in the core code (pastebin link): 核心代码的重大变化(pastebin链接):

  • Removed the part where I was checking if the first item in memory was in the slice [1:] and instead, in the add_moves function, just before appending a move to the self._moves I check if it already is in memory: if move not in self.memory: self._moves.append(move) (did this for every append) 删除我在那里检查,如果在第一个项目的一部分memory在片[1:],相反,在add_moves功能,附加一个移动到之前self._moves我检查,如果它已经存在于内存: if move not in self.memory: self._moves.append(move) (为每个附加做了这个)
    • Instead of hardcoding the win condition I now use the number of cards in the game (the maximum number found in the stacks) 我现在使用游戏中的牌数(堆栈中找到的最大数量)而不是硬编码胜利条件

Minor changes in the core code: 核心代码的细微变化:

  • Removed self.memory.append("move 0") , as it was not necessary 删除了self.memory.append("move 0") ,因为没有必要
  • In add_moves function I added a color to each card and stack, instead of just having a different shape. add_moves函数中,我为每个卡片和堆栈添加了一种颜色,而不是仅仅具有不同的形状。 A simple: if card[2][1] in ["D","H"]: card_color = "red" and else: card_color = "black" (and another one for each card in the stacks, changing card to stack[-1][1] 一个简单的: if card[2][1] in ["D","H"]: card_color = "red"else: card_color = "black" (堆栈中每张卡的另一张卡,将卡更改为stack[-1][1]

Using the second approach ( while loop without function definitions) I tested for stacks of various sizes. 使用第二种方法( while循环没有函数定义)我测试了各种大小的堆栈。 When taking too much time, I stop the program, shuffle the deck and re-run. 当花费太多时间时,我停止程序,洗牌并重新运行。 It found solutions relatively fast for sizes 2-6, and then for 7+ the time (as well as Board objects created) skyrockets. 它发现2-6尺寸的解决方案相对较快,然后7倍以上(以及创建的Board对象)突然出现。 I tried with 9,11 and 13 cards but it didn't find a solution quickly and I got bored eventually. 我尝试了9,11和13张卡,但它没有快速找到解决方案,最终我感到无聊。

Here you can see no of Boards created till solution (or stop) of 5 re-runs (shuffles) per deck size. 在这里你可以看到在每个甲板大小的5次重新运行(shuffles)的解决方案(或停止)之前创建的板。

卡与董事会

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM