This commit is contained in:
Guan Inf 2025-06-26 21:30:21 +08:00
parent 8684b92edf
commit 2601cdc863
4 changed files with 395 additions and 15 deletions

144
SourceCollector.py Normal file
View 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
View 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

View File

@ -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,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
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2 1 0 l30 0 e 1 0 1 e 0 t7 1 0 t17 0 1 0 0 g13 0 1 g19 g23 0 1
3 1 1 0 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 1 1 0 1
4 1 0 0 l25 t14 0 0 t5 0 0 1 0 0 1 0 1 0 0 t9 1 0 t20 g27 1
5 1 0 t17 1 0 1 0 1 1 0 1 1 0 1 0 1 1 0 0 1 1 0 1 1
6 1 1 0 0 1 1 0 1 1 0 1 0 1 1 0 1 0 1 0 0 0 1 1 0 0 1
7 1 0 0 1 0 l19 1 0 0 1 0 1 0 1 1 0 0 1 0 0 1
8 1 1 0 0 1 0 1 1 0 1 0 1 0 0 0 0 0 1 1 0 0 1
9 1 0 1 l30 1 0 0 t18 0 0 g14 1 0 1 0 1 1 0 0 1 0 0 1 1
10 1 1 l25 1 g10 1 0 1 1 0 1 0 1 0 1 0 1 1 0 0 1 1 0 1 b73 1
11 1 0 1 0 1 g26 1 0 1 0 t9 0 1 0 1 0 g20 b66 1 1 0 0 1 0 0 g12 1
12 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 t20 1 1 0 l22 0 1 0 g27 0 1
13 1 0 0 1 0 g16 1 0 0 1 0 0 1 0 t17 0 s 1 0 0 1
14 1 1 1 1 0 1 0 1 0 1 0 1 g12 t7 1 1 0 0 1 1 0 1 0 1
15 1 0 g12 0 0 0 1 0 0 s t7 1 0 0 1 t13 0 1 0 l17 0 1
16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

100
readme.md Normal file
View File

@ -0,0 +1,100 @@
# 算法驱动的迷宫探险游戏开发
## 设计目标
***\*地图:\****
一个二维矩阵,采⽤分治法⽣成,⽆孤⽴区域,存在唯⼀通路。
输⼊:迷宫尺⼨ 。
输出迷宫矩阵可存储为JSON或CSV包括起点StartS、 终点ExitE、墙壁#、通路空格、资源例如⾦币G、 陷阱TrapT、机关LockerL、BOSSB
墙壁不可通过无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战