一个“一笔画”问题的求解器

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
回老家跟侄子玩,因为不想惯着他玩手机的臭毛病,就跟他玩一些纸笔游戏,比如这个:

一个“一笔画”问题的求解器_第1张图片

从起点出发抵达终点,如何在不走重复格子的前提下,走过尽可能多的格子,并吃到尽可能多的金币?其中,黑色格子是墙壁不能走。

感觉还挺有意思的,不妨就来写一个“一笔画”的求解器,找出所有最优的轨迹。

思路:

可以先输出所有从起点到终点的轨迹,然后建立一个评分系统对所有轨迹进行评估:每走一个格子+1分,每吃一个金币+1分,最后输出评分最高的轨迹即可。

Step1:建模

一个如图的迷宫可以很自然地用矩阵描述,对于每一个元素,值0表示空格子,值1表示金币,值-1表示墙壁,值2表示起点,值3表示终点。

import numpy as np
ROW, COL = 6, 4
maze = np.zeros((ROW,COL))
START = (0, 3)
OUT = (5, 0)
maze[START] = 2
maze[OUT] = 3
maze[1, 1] = 1
maze[5, 3] = 1
maze[2, 1] = -1
maze[4, 2] = -1

View Code

Step2:寻迹算法

一条轨迹可以拆分成多个“步”,在走每一步之前,所做的事情是一样的:找到下一步能到达的合法位置,然后选择其中一个。如果新位置是死路或者是终点,那么结束或输出该轨迹,并返回到上一步所在的位置,选择其他步。这一过程可以很好地由递归函数表达:

def step(start, track, env):
 x, y = start
 next\_pos = []
 for i, j in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]:
 if 0 <= i <= ROW-1 and 0 <= j <= COL-1: # 边界限制
            if (i, j) in track: # 重复的格子不能走
                continue
            if env[i,j] == -1: # 有墙壁的格子不能走
                continue
 next\_pos.append((i, j))
 
 for i, j in next\_pos: # 如果next\_pos为空,不会进入循环,所以无需专门处理边界情况
        track\_new = track.copy()
 track\_new.append((i, j))
 if (i, j) == OUT:
 global tracks
 tracks.append(track\_new)
 return
 start\_new = (i, j)
 step(start\_new, track\_new, env)

运行一下:

tracks = []
step(START, [START], maze) 
print(len(tracks))

输出为192,说明从起点到终点的轨迹有192条。

Step3:评分器

每走一个格子+1分,每吃一个金币+1分,输出评分最高的轨迹。

def findOptimalTracks(tracks):
 # 统计所有track的分数
    # 分数:每走过一个格子+1,若是金币格子则额外+1
    scores = []
 for track in tracks:
 score = 0
 if (1, 1) in track:
 score += 1
        if (5, 3) in track:
 score += 1
 score += len(track)
 scores.append(score)
 
 tracks\_optimal = []
 maxScore = max(scores)
 for i, score in enumerate(scores):
 if score == maxScore:
 tracks\_optimal.append(tracks[i])
 return tracks\_optimal

运行一下:

tracks\_optimal = findOptimalTracks(tracks)
print(len(tracks\_optimal))

输出为9,说明最优的轨迹有9条。

Step4:可视化

其实事情到上一步就已经结束了,这里就当看个乐子吧!用pygame把所有轨迹呈现出来:

class tracksRender():
 def \_\_init\_\_(self, tracks, env):
 pygame.init()
 SIZE = 75
 ROW, COL = env.shape
 self.window = pygame.display.set\_mode((COL*SIZE, ROW*SIZE))
 pygame.display.set\_caption('一笔画')
 
 self.running = True
 self.clock = pygame.time.Clock() # 帧数控制
        self.time = time.time() # 时间控制
        self.INTERVAL = 0.1 # 动作间隔
        self.FPS = 30 # 帧数
 
 self.env = env
 self.tracks = tracks
 self.track = [] # 存放当前将显示的轨迹
        self.track\_render = [] # 存放当前显示的部分轨迹
    
    
    def processInput(self):
 for event in pygame.event.get():
 # 按下右上角的退出键
            if event.type == pygame.QUIT:
 self.running = False
 break
    
    
    def update(self):
 if time.time() - self.time < self.INTERVAL: # 设定更新间隔
             return
         if self.tracks == []: # 如果已经显示完所有轨迹 则函数返回
             return
         if self.track == []: # 如果当前轨迹已经完全显示 则显示下一条轨迹
             self.track = self.tracks.pop(0)
 self.track\_render = []
 self.track\_render.append(self.track.pop(0))
 self.time = time.time()
 
 
 def render(self):
 SIZE = 75
 ROW, COL = self.env.shape
 BGCOLOR = (226, 240, 217) # 浅草绿
 self.window.fill(BGCOLOR)
 DarkKhaki = (189, 183, 107) # 深卡其布
        Wall = (139, 126, 102) # 灰墙
        Orange = (255, 165, 0) # 橘子
        DodgerBlue = (30, 144, 255) # 道奇蓝
        Gold = (255, 215, 0) # 黄金
        Font = pygame.font.SysFont('arial', 40)
 
 # 画分隔线
        for i in range(ROW + 1):
 pygame.draw.line(self.window, DarkKhaki, (0, i*SIZE), (COL*SIZE, i*SIZE), 3)
 for i in range(COL + 1):
 pygame.draw.line(self.window, DarkKhaki, (i*SIZE, 0), (i*SIZE, ROW*SIZE), 3)
 
 # 画特殊格子
        for i in range(ROW):
 for j in range(COL):
 if self.env[i, j] == -1: # 墙壁
 pygame.draw.polygon(self.window, Wall, 
 ((j*SIZE, i*SIZE), ((j+1)*SIZE, i*SIZE), ((j+1)*SIZE, (i+1)*SIZE), (j*SIZE, (i+1)*SIZE))
 ,0)
 if self.env[i, j] == 2: # 起点
                    Start\_text = Font.render('S', True, Orange)
 self.window.blit(Start\_text, ((j+0.3)*SIZE, (i+0.2)*SIZE))
 if self.env[i, j] == 3: # 终点
                    Start\_text = Font.render('E', True, DodgerBlue)
 self.window.blit(Start\_text, ((j+0.3)*SIZE, (i+0.2)*SIZE))
 if self.env[i, j] == 1: # 金币
                    pygame.draw.circle(self.window, Gold, ((j+0.5)*SIZE,(i+0.5)*SIZE), 20)
 
 # 画轨迹线
        if len(self.track\_render) > 1:
 for i, point in enumerate(self.track\_render):
 if i == 0: continue
 x\_, y\_ = self.track\_render[i-1]
 x, y = point
 pygame.draw.line(self.window, Orange, ((y\_+0.5)*SIZE, (x\_+0.5)*SIZE), ((y+0.5)*SIZE, (x+0.5)*SIZE), 5) 
 
 pygame.display.update() # 刷新显示
        
    def run(self):
 while self.running:
 self.processInput()
 self.update()
 self.render()
 self.clock.tick(self.FPS)

效果如下:

一个“一笔画”问题的求解器_第2张图片

整体代码如下:

"""
走迷宫
玩家从起点出发,到达终点走出迷宫
迷宫可由矩阵描述,例如以下6×4的迷宫
[ 0 0 0 2
 0 1 0 0
 0 -1 0 0
 0 0 0 0
 0 0 -1 0
 3 0 0 1 ]
其中,2表示起点,3表示终点,-1表示墙壁(无法行动到该位置),1表示金币
问,如何在不走重复格子的前提下,走出迷宫,并吃到最多的金币,且走过最多的格子?请打印路径
"""
import numpy as np
import pygame
import time

def step(start, track, env):
 x, y = start
 next\_pos = []
 for i, j in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]:
 if 0 <= i <= ROW-1 and 0 <= j <= COL-1: # 边界限制
            if (i, j) in track: # 重复的格子不走
                continue
            if env[i,j] == -1: # 有墙壁的格子不能走
                continue
 next\_pos.append((i, j))
 
 for i, j in next\_pos:
 track\_new = track.copy()
 track\_new.append((i, j))
 if (i, j) == OUT:
 global tracks
 tracks.append(track\_new)
 return
 start\_new = (i, j)
 step(start\_new, track\_new, env)


def findOptimalTracks(tracks):
 # 统计所有track的分数
    # 分数:每走过一个格子+1,若是金币格子则额外+1
    scores = []
 for track in tracks:
 score = 0
 if (1, 1) in track:
 score += 1
        if (5, 3) in track:
 score += 1
 score += len(track)
 scores.append(score)
 
 tracks\_optimal = []
 maxScore = max(scores)
 for i, score in enumerate(scores):
 if score == maxScore:
 tracks\_optimal.append(tracks[i])
 return tracks\_optimal


class tracksRender():
 def \_\_init\_\_(self, tracks, env):
 pygame.init()
 SIZE = 75
 ROW, COL = env.shape
 self.window = pygame.display.set\_mode((COL*SIZE, ROW*SIZE))
 pygame.display.set\_caption('一笔画')
 
 self.running = True
 self.clock = pygame.time.Clock() # 帧数控制
        self.time = time.time() # 时间控制
        self.INTERVAL = 0.1 # 动作间隔
        self.FPS = 30 # 帧数
 
 self.env = env
 self.tracks = tracks
 self.track = [] # 存放当前将显示的轨迹
        self.track\_render = [] # 存放当前显示的部分轨迹
    
    
    def processInput(self):
 for event in pygame.event.get():
 # 按下右上角的退出键
            if event.type == pygame.QUIT:
 self.running = False
 break
    
    
    def update(self):
 if time.time() - self.time < self.INTERVAL: # 设定更新间隔
             return
         if self.tracks == []: # 如果已经显示完所有轨迹 则函数返回
             return
         if self.track == []: # 如果当前轨迹已经完全显示 则显示下一条轨迹
             self.track = self.tracks.pop(0)
 self.track\_render = []
 self.track\_render.append(self.track.pop(0))
 self.time = time.time()
 
 
 def render(self):
 SIZE = 75
 ROW, COL = self.env.shape
 BGCOLOR = (226, 240, 217) # 浅草绿
 self.window.fill(BGCOLOR)
 DarkKhaki = (189, 183, 107) # 深卡其布
        Wall = (139, 126, 102) # 灰墙
        Orange = (255, 165, 0) # 橘子
        DodgerBlue = (30, 144, 255) # 道奇蓝
        Gold = (255, 215, 0) # 黄金
        Font = pygame.font.SysFont('arial', 40)
 
 # 画分隔线
        for i in range(ROW + 1):
 pygame.draw.line(self.window, DarkKhaki, (0, i*SIZE), (COL*SIZE, i*SIZE), 3)
 for i in range(COL + 1):
 pygame.draw.line(self.window, DarkKhaki, (i*SIZE, 0), (i*SIZE, ROW*SIZE), 3)
 
 # 画特殊格子
        for i in range(ROW):
 for j in range(COL):
 if self.env[i, j] == -1: # 墙壁
 pygame.draw.polygon(self.window, Wall, 
 ((j*SIZE, i*SIZE), ((j+1)*SIZE, i*SIZE), ((j+1)*SIZE, (i+1)*SIZE), (j*SIZE, (i+1)*SIZE))
 ,0)
 if self.env[i, j] == 2: # 起点
                    Start\_text = Font.render('S', True, Orange)
 self.window.blit(Start\_text, ((j+0.3)*SIZE, (i+0.2)*SIZE))
 if self.env[i, j] == 3: # 终点
                    Start\_text = Font.render('E', True, DodgerBlue)
 self.window.blit(Start\_text, ((j+0.3)*SIZE, (i+0.2)*SIZE))
 if self.env[i, j] == 1: # 金币
                    pygame.draw.circle(self.window, Gold, ((j+0.5)*SIZE,(i+0.5)*SIZE), 20)
 
 # 画轨迹线
        if len(self.track\_render) > 1:
 for i, point in enumerate(self.track\_render):
 if i == 0: continue
 x\_, y\_ = self.track\_render[i-1]
 x, y = point
 pygame.draw.line(self.window, Orange, ((y\_+0.5)*SIZE, (x\_+0.5)*SIZE), ((y+0.5)*SIZE, (x+0.5)*SIZE), 5) 
 
 pygame.display.update() # 刷新显示
        
    def run(self):
 while self.running:
 self.processInput()
 self.update()
 self.render()
 self.clock.tick(self.FPS)


if \_\_name\_\_ == "\_\_main\_\_":
 ROW, COL = 6, 4
 maze = np.zeros((ROW,COL))
 START = (0, 3)
 OUT = (5, 0)
 maze[START] = 2
 maze[OUT] = 3
 maze[1, 1] = 1
 maze[5, 3] = 1
 maze[2, 1] = -1
 maze[4, 2] = -1
    print(' Maze:\n', maze)
 tracks = []
 step(START, [START], maze) 
 tracks\_optimal = findOptimalTracks(tracks)
 tracksRender = tracksRender(tracks\_optimal, maze)
 tracksRender.run()
 pygame.quit() 

View Code

你可能感兴趣的:(python,算法,计算机)