init
This commit is contained in:
parent
8684b92edf
commit
2601cdc863
144
SourceCollector.py
Normal file
144
SourceCollector.py
Normal file
@ -0,0 +1,144 @@
|
||||
import csv
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self):
|
||||
self.fa = None
|
||||
self.children = []
|
||||
self.pos = None
|
||||
self.val = 0
|
||||
self.id = 0
|
||||
self.dp = 0
|
||||
self.path = []
|
||||
self.dp_path = []
|
||||
|
||||
class SourceCollector:
|
||||
def __init__(self, filename, maze=None):
|
||||
self.filename = filename
|
||||
self.maze = maze
|
||||
self.start_pos = None
|
||||
self.end_pos = None
|
||||
self.path = []
|
||||
|
||||
if self.filename:
|
||||
self.maze = []
|
||||
with open(f"{self.filename}",'r') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
t = []
|
||||
for i in row:
|
||||
if i.startswith('b') or i.startswith('l'):
|
||||
t.append('0')
|
||||
else:
|
||||
t.append(i)
|
||||
self.maze.append(t)
|
||||
self.rowNums = len(self.maze)
|
||||
self.colNums = len(self.maze[0])
|
||||
|
||||
|
||||
for i in range(self.rowNums):
|
||||
for j in range(self.colNums):
|
||||
if self.maze[i][j] =='s':
|
||||
self.start_pos = (i,j)
|
||||
if self.maze[i][j] =='e':
|
||||
self.end_pos = (i,j)
|
||||
|
||||
def dfs_show(self,u):
|
||||
if u.id != 0:
|
||||
print(f"id: {u.id} , fa:{u.fa.id} , val:{u.val}")
|
||||
else:
|
||||
print(f"id: {u.id} , val:{u.val}")
|
||||
print(u.path)
|
||||
for child in u.children:
|
||||
|
||||
self.dfs_show(child)
|
||||
|
||||
def outofmap(self,x,y):
|
||||
return x < 0 or y < 0 or x > self.rowNums or y > self.colNums
|
||||
def build_a_tree(self):
|
||||
sn = TreeNode()
|
||||
sn.pos = self.start_pos
|
||||
id = 0
|
||||
sn.id = id
|
||||
sn.val = 0
|
||||
sn.path = [sn.pos]
|
||||
|
||||
st = [[False] * self.colNums for _ in range(self.rowNums)]
|
||||
qsk = []
|
||||
sx, sy = self.start_pos
|
||||
st[sx][sy] = True
|
||||
qsk.append((self.start_pos[0],self.start_pos[1], sn,[]))
|
||||
dx = [-1,0,1,0]
|
||||
dy = [0,-1,0,1]
|
||||
|
||||
while qsk:
|
||||
x, y, u,path = qsk.pop()
|
||||
for _x, _y in zip(dx,dy):
|
||||
nx, ny = x + _x, y + _y
|
||||
if self.outofmap(nx,ny):
|
||||
continue
|
||||
if self.maze[nx][ny] == '1' or st[nx][ny]:
|
||||
continue
|
||||
|
||||
st[nx][ny] = True
|
||||
to = self.maze[nx][ny]
|
||||
new_path = path + [(nx,ny)]
|
||||
if to.startswith('g') or to.startswith('t'):
|
||||
new_node = TreeNode()
|
||||
id+=1
|
||||
new_node.id = id
|
||||
new_node.pos = (nx, ny)
|
||||
new_node.fa = u
|
||||
num_str = to[1:]
|
||||
new_node.path = new_path
|
||||
|
||||
try:
|
||||
if to.startswith('g'):
|
||||
new_node.val = int(num_str)
|
||||
else:
|
||||
new_node.val = -int(num_str)
|
||||
except ValueError:
|
||||
print("wa ! ")
|
||||
u.children.append(new_node)
|
||||
|
||||
qsk.append((nx, ny, new_node,[]))
|
||||
else:
|
||||
qsk.append((nx, ny, u,new_path))
|
||||
|
||||
return sn
|
||||
def dfs(self,sn):
|
||||
sn.dp = sn.val
|
||||
for child in sn.children:
|
||||
# print(f"cur : {child.pos} , fa : {child.fa.pos} , childrens : {child.path}")
|
||||
self.dfs(child)
|
||||
if child.dp > 0:
|
||||
sn.dp += child.dp
|
||||
sn.dp_path += child.path + child.dp_path + child.path[::-1]
|
||||
|
||||
|
||||
def find_path(self):
|
||||
self.path = []
|
||||
sn = self.build_a_tree()
|
||||
self.dfs(sn)
|
||||
return self.path
|
||||
|
||||
if __name__ == '__main__':
|
||||
obj = SourceCollector(filename="maze.csv")
|
||||
sn = obj.build_a_tree()
|
||||
# obj.dfs_show(sn)
|
||||
obj.dfs(sn)
|
||||
print(len(sn.dp_path))
|
||||
print(sn.pos)
|
||||
pre = sn.pos
|
||||
for _ in sn.dp_path:
|
||||
dx,dy = _[0] - pre[0],_[1]-pre[1]
|
||||
if dx > 0:
|
||||
print("down")
|
||||
elif dx < 0:
|
||||
print("up")
|
||||
elif dy > 0:
|
||||
print("right")
|
||||
elif dy < 0:
|
||||
print("left")
|
||||
pre = _
|
||||
|
||||
|
136
boss_fight.py
Normal file
136
boss_fight.py
Normal file
@ -0,0 +1,136 @@
|
||||
# -*-coding: GBK -*-
|
||||
import heapq # 导入堆模块,用于实现优先队列
|
||||
from typing import List, Optional, Tuple # 导入类型提示相关模块
|
||||
|
||||
|
||||
def boss_strategy(coin: int, blood: int) -> Optional[Tuple[List[str], int]]:
|
||||
# 使用分支限界法寻找最小回合数击败BOSS的技能序列
|
||||
|
||||
# 参数:
|
||||
# coin: 玩家初始金币数
|
||||
# blood: BOSS初始血量
|
||||
|
||||
# 返回:
|
||||
# 最小回合数的技能序列列表,如果无解则返回None
|
||||
|
||||
start_coin = coin # 记录初始金币数,用于计算消耗金币
|
||||
|
||||
# 初始状态: (回合数, 剩余金币, BOSS血量, 充能, 连续E计数, 技能序列元组)
|
||||
start_state = (0, coin, blood, 0, 0, ())
|
||||
|
||||
# 创建优先队列: 元素为 (优先级, 状态)
|
||||
# 优先级元组: (回合数, 消耗金币, BOSS血量) - 值越小优先级越高
|
||||
heap = []
|
||||
heapq.heappush(heap, ((0, 0, blood), start_state)) # 将初始状态加入优先队列
|
||||
|
||||
# 创建访问集合: 用于避免重复状态
|
||||
# 状态键: (回合数, 剩余金币, BOSS血量, 充能, min(连续E计数,3))
|
||||
visited = set()
|
||||
start_ce_compressed = min(0, 3) # 压缩连续E计数状态
|
||||
visited.add((0, coin, blood, 0, start_ce_compressed)) # 将初始状态加入已访问集合
|
||||
|
||||
# 设置最大回合数限制,避免无限循环
|
||||
max_round = blood * 2 # 每次至少造成1点伤害,最多回合数为血量两倍
|
||||
|
||||
# 主循环: 处理优先队列中的状态
|
||||
while heap:
|
||||
# 从优先队列中取出优先级最高的状态
|
||||
priority, state = heapq.heappop(heap)
|
||||
# 解包状态元组
|
||||
round_cnt, cur_coin, cur_blood, energy, ce, seq_tuple = state
|
||||
|
||||
# 检查是否已击败BOSS
|
||||
if cur_blood <= 0:
|
||||
return (list(seq_tuple), cur_coin) # 返回技能序列和剩余金币
|
||||
|
||||
# 超过最大回合数则跳过当前状态
|
||||
if round_cnt >= max_round:
|
||||
continue
|
||||
|
||||
# 尝试使用技能A
|
||||
if cur_coin >= 2: # 检查金币是否足够
|
||||
new_coin = cur_coin - 2 # 扣除金币
|
||||
new_blood = cur_blood - 1 # 减少BOSS血量
|
||||
new_energy = energy + 1 # 增加充能
|
||||
new_ce = 0 # 使用非E技能,重置连续E计数
|
||||
new_round = round_cnt + 1 # 回合数+1
|
||||
new_seq = seq_tuple + ('A',) # 添加技能到序列
|
||||
|
||||
# 压缩连续E计数状态(>=3视为3)
|
||||
ce_compressed = min(new_ce, 3)
|
||||
# 创建状态键用于去重
|
||||
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
|
||||
|
||||
# 检查是否为新状态
|
||||
if state_key not in visited:
|
||||
visited.add(state_key) # 标记为已访问
|
||||
cost = start_coin - new_coin # 计算已消耗金币
|
||||
# 创建新状态
|
||||
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
|
||||
# 将新状态加入优先队列
|
||||
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
|
||||
|
||||
# 尝试使用技能E
|
||||
cost_e = 3 # 默认消耗金币
|
||||
if ce >= 2: # 检查是否为连续第三次使用E
|
||||
cost_e = 2 # 第三次消耗2金币
|
||||
|
||||
if cur_coin >= cost_e: # 检查金币是否足够
|
||||
new_coin = cur_coin - cost_e # 扣除金币
|
||||
new_blood = cur_blood - 3 # 减少BOSS血量
|
||||
new_energy = energy + 1 # 增加充能
|
||||
new_ce = ce + 1 # 增加连续E计数
|
||||
new_round = round_cnt + 1 # 回合数+1
|
||||
new_seq = seq_tuple + ('E',) # 添加技能到序列
|
||||
|
||||
# 压缩连续E计数状态
|
||||
ce_compressed = min(new_ce, 3)
|
||||
# 创建状态键用于去重
|
||||
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
|
||||
|
||||
# 检查是否为新状态
|
||||
if state_key not in visited:
|
||||
visited.add(state_key) # 标记为已访问
|
||||
cost = start_coin - new_coin # 计算已消耗金币
|
||||
# 创建新状态
|
||||
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
|
||||
# 将新状态加入优先队列
|
||||
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
|
||||
|
||||
# 尝试使用技能Q
|
||||
if energy >= 3 and cur_coin >= 3: # 检查充能和金币是否足够
|
||||
new_coin = cur_coin - 3 # 扣除金币
|
||||
new_blood = cur_blood - 8 # 减少BOSS血量
|
||||
new_energy = energy - 3 # 消耗充能
|
||||
new_ce = 0 # 使用非E技能,重置连续E计数
|
||||
new_round = round_cnt + 1 # 回合数+1
|
||||
new_seq = seq_tuple + ('Q',) # 添加技能到序列
|
||||
|
||||
# 压缩连续E计数状态
|
||||
ce_compressed = min(new_ce, 3)
|
||||
# 创建状态键用于去重
|
||||
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
|
||||
|
||||
# 检查是否为新状态
|
||||
if state_key not in visited:
|
||||
visited.add(state_key) # 标记为已访问
|
||||
cost = start_coin - new_coin # 计算已消耗金币
|
||||
# 创建新状态
|
||||
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
|
||||
# 将新状态加入优先队列
|
||||
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
|
||||
|
||||
return None # 队列为空,无解
|
||||
|
||||
|
||||
# 主程序入口
|
||||
if __name__ == "__main__":
|
||||
# 测试示例: 金币=10, BOSS血量=10
|
||||
result = boss_strategy(20, 20)
|
||||
|
||||
if result is None:
|
||||
print("无法击败BOSS")
|
||||
else:
|
||||
skill_sequence, remaining_coins = result
|
||||
print("最小回合数技能序列:", skill_sequence) # 预期输出: ['A', 'A', 'A', 'Q']
|
||||
print("剩余金币:", remaining_coins) # 预期输出: 1
|
30
maze.csv
30
maze.csv
@ -1,16 +1,16 @@
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
1,0,l30,e,0,1,0,1,0,t17,1,0,g13,1,g23,1
|
||||
1,1,1,1,0,1,0,0,0,0,1,0,0,1,0,1
|
||||
1,0,0,t14,0,0,0,1,0,0,1,1,0,1,t20,1
|
||||
1,0,t17,0,0,1,0,1,0,0,1,0,0,1,0,1
|
||||
1,1,0,1,1,1,1,1,1,1,1,0,0,1,0,1
|
||||
1,0,0,0,l19,0,0,1,0,0,1,0,0,1,0,1
|
||||
1,1,0,1,1,1,1,1,0,0,0,0,0,1,0,1
|
||||
1,0,l30,0,0,t18,0,g14,0,0,1,0,0,0,0,1
|
||||
1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1
|
||||
1,0,0,g26,0,0,0,0,0,b66,1,0,1,0,0,1
|
||||
1,1,0,1,0,1,1,1,0,t20,1,l22,1,0,g27,1
|
||||
1,0,0,1,g16,0,0,1,0,0,0,t17,s,0,0,1
|
||||
1,1,1,1,1,1,1,1,g12,1,1,0,1,0,1,1
|
||||
1,0,0,0,0,0,0,t7,0,0,1,0,1,0,0,1
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
1,0,0,1,0,e,t7,1,0,0,0,0,0,g19,0,1
|
||||
1,0,1,1,0,1,1,1,1,1,1,1,1,1,0,1
|
||||
1,0,l25,0,t5,0,0,1,0,0,0,0,t9,0,g27,1
|
||||
1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1
|
||||
1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,1
|
||||
1,0,1,0,1,0,0,1,1,1,1,0,0,0,0,1
|
||||
1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1
|
||||
1,1,1,0,0,0,0,1,1,1,1,0,1,0,1,1
|
||||
1,l25,g10,0,1,0,0,0,0,1,0,0,1,0,b73,1
|
||||
1,1,1,1,1,t9,1,1,g20,1,0,0,1,0,g12,1
|
||||
1,0,1,0,1,0,0,1,0,1,0,0,1,0,0,1
|
||||
1,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1
|
||||
1,1,1,0,0,0,0,1,t7,1,0,1,1,0,0,1
|
||||
1,g12,0,0,1,0,s,1,0,0,t13,0,1,l17,0,1
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
|
100
readme.md
Normal file
100
readme.md
Normal file
@ -0,0 +1,100 @@
|
||||
# 算法驱动的迷宫探险游戏开发
|
||||
|
||||
## 设计目标
|
||||
|
||||
***\*地图:\****
|
||||
|
||||
一个二维矩阵,采⽤分治法⽣成,⽆孤⽴区域,存在唯⼀通路。
|
||||
|
||||
输⼊:迷宫尺⼨ 。
|
||||
|
||||
输出:迷宫矩阵(可存储为JSON或CSV),包括起点Start(S)、 终点Exit(E)、墙壁(#)、通路(空格)、资源(例如⾦币G)、 陷阱Trap(T)、机关Locker(L)、BOSS(B)。
|
||||
|
||||
|
||||
|
||||
墙壁:不可通过,无value的节点
|
||||
|
||||
通路:可通过,无value的节点
|
||||
|
||||
金币:可通过,有value 的节点。BOSS战时的行动需要消耗金币
|
||||
|
||||
陷阱:可通过,但经过时会扣除玩家的金币
|
||||
|
||||
机关:三位密码锁。一般分布在某条路的尽头,必须开启所有机关后,终点大门才会打开
|
||||
|
||||
BOSS:在必经之路上,有value(血量) , 必须打败才能继续前进
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
***\*玩家:\****
|
||||
|
||||
有三种攻击方式(初始充能为0):
|
||||
|
||||
A:消耗2枚金币造成1点伤害并获得1点充能
|
||||
|
||||
E:消耗3枚金币造成3点伤害并获得1点充能,连续第三次使用E技能时少消耗1枚金币
|
||||
|
||||
Q:消耗3枚金币和3点充能,造成8点伤害
|
||||
|
||||
|
||||
|
||||
## 任务分解
|
||||
|
||||
### 1. 分治法生成迷宫
|
||||
|
||||
**需求 : **
|
||||
|
||||
输入 : 正整数 n( 100>=n>=7 ) , 生成类型选择(json或者csv)
|
||||
|
||||
输出 : json文件 , csv文件 , 表示地图 , 无孤立区 , 存在唯一通路
|
||||
|
||||
S : 起点
|
||||
|
||||
E : 终点
|
||||
|
||||
\# :墙壁
|
||||
|
||||
blank : 通路
|
||||
|
||||
G : 资源
|
||||
|
||||
T : 陷阱
|
||||
|
||||
L : 机关
|
||||
|
||||
B : Boss
|
||||
|
||||
**递归分割法生成迷宫**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2. 动态规划进行资源收集路径规划
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 3. 贪心算法设计实时资源拾取策略
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 4. 回溯法解迷关卡
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 5. 分支限界设计boss战
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user