【日常训练】398. 随机数索引 难度 中等 165

该博客讨论了一种在包含重复元素的大型整数数组中随机选择目标数字索引的方法。解决方案实现了一个名为`Solution`的类,其中`pick`方法能以相等概率返回目标值的任意索引。示例展示了对于给定数组,如何正确地随机选取目标数字的索引。

题目

给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。

注意:
数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。

示例:
int[] nums = new int[] {1,2,3,3,3};
Solution solution = new Solution(nums);

// pick(3) 应该返回索引 2,3 或者 4。每个索引的返回概率应该相等。
solution.pick(3);

// pick(1) 应该返回 0。因为只有nums[0]等于1。
solution.pick(1);

代码

package dayLeetCode;

import java.util.Random;

public class dayleetcode398 {

}

class Solution{
    int[] nums;
    public Solution(int[] nums) {
        this.nums = nums.clone();
    }

    // 统计target的数量cnt,随机返回第k个target的坐标,1<=k<=cnt
    public int pick(int target) {
        int cnt = 0;
        for (int i = 0; i < nums.length; i++){
            if (nums[i] == target){
                cnt++;
            }
        }

        Random random = new Random();
        // random.next(cnt) 返回的是[0,cnt)
        int k = random.nextInt(cnt) + 1;
        int tmp = 0;

        for (int i = 0; i < nums.length; i++){
            if (nums[i] == target){
                tmp++;
                if (tmp == k){
                    return i;
                }
            }
        }
        return -1;
    }

}
# -*- coding: UTF-8 -*- """ 海龟迷宫闯关游戏 - 完整优化版 """ import turtle # 导入海龟绘图模块 import random # 导入随机数模块 import time # 导入时间模块 game_title = '小海龟大迷宫闯关小游戏' # 游戏名字 level = 1 # 当前关卡,从1开始 '''绘制地图用的全局变量''' txt_path = 'map/map1.txt' # 地图信息文本文件路径及名称 road_color = (191, 217, 225) # 迷宫通道的颜色 R, C = 0, 0 # 迷宫地图的总行数R、总列数C cell_size = 20 # 一个格子的尺寸 area_sign = {} # 记录入口出口索引位置 mazeList = [] # 地图列表 '''海龟对象''' map_t = turtle.Turtle() # 绘制地图的海龟 map_t.speed(0) # 设置绘图速度最快(地图绘制) sign_t = turtle.Turtle() # 绘制入口出口标记的海龟 auto_t = turtle.Turtle() # 自动走迷宫的海龟 auto_t.pensize(5) # 画笔粗细(自动) auto_t.speed(0) # 设置绘图速度最快(手动) auto_t.ht() # 隐藏海龟光标 manual_t = turtle.Turtle() # 手动走迷宫的海龟 manual_t.pensize(5) # 画笔粗细(手动) manual_t.speed(0) # 设置绘图速度最快(手动) manual_t.shape('turtle') # 设置海龟光标为小海龟(手动) manual_t.ht() # 隐藏手动走迷宫所用的海龟光标(手动) # 要探索4个方向对应索引的变化规则 direction = [ (1, 0), # 右 (-1, 0), # 左 (0, 1), # 上 (0, -1) # 下 ] # 计时相关变量 start_time = 0 timer_t = turtle.Turtle() # 显示计时器的海龟 def imoveto(ci, ri): """ 功能:根据索引位置移动海龟(不画线) :param ci: 列索引 :param ri: 行索引 :return: """ auto_t.penup() # 抬笔 cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置 auto_t.goto(cx, cy) # 移动到指定位置 auto_t.pendown() # 落笔 auto_t.shape('turtle') # 设置海龟光标的形状 auto_t.color('red') # 设置画笔颜色为红色 auto_t.st() # 显示海龟光标 def c_move_to(t, ctuple): # 移动到指定位置 """ 功能:根据坐标位置移动到指定位置(不画线) :param t: 海龟对象 :param ctuple: 记录坐标位置的元组 :return: """ t.ht() # 隐藏海龟光标 t.penup() # 抬笔 t.goto(ctuple[0], ctuple[1]) # 移动到坐标指定的位置 t.pendown() # 落笔 def itoc(ituple): """ 将索引位置转换为实际坐标位置 :param ituple: 行、列索引组成的元组 :return: 实际坐标位置 """ ci = ituple[0] ri = ituple[1] tx = ci * cell_size - C * cell_size / 2 # 根据索引值计算每个正方形的起点(x坐标) ty = R * cell_size / 2 - ri * cell_size # 根据索引值计算每个正方形的起点(y坐标) cx = tx + cell_size / 2 # 正方形中心的x坐标 cy = ty - cell_size / 2 # 正方形中心的y坐标 return (cx, cy) def ctoi(cx, cy): """ 根据cxcy求在列表中对应的索引 :param cx: x轴坐标 :param cy: y轴坐标 :return: 元组,(ci,ri) """ ci = ((C - 1) * cell_size / 2 + cx) / cell_size # 计算列索引 ri = ((R - 1) * cell_size / 2 - cy) / cell_size # 计算行索引 return (int(ci), int(ri)) # 返回行列索引的元组 def get_map(filename): """ 功能:读取保存地图的文本文件内容到列表 :param filename: 地图文件名 :return: 地图列表 """ try: with open(filename, 'r', encoding='utf-8') as f: # 打开文件 fl = f.readlines() # 读取全部行 maze_list = [] # 保存地图的列表 for line in fl: # 将读取的内容以空格分割为二维列表 line = line.strip() # 去掉空格 if line: # 确保不是空行 line_list = line.split(" ") # 以空格进行分割为列表 maze_list.append(line_list) # 将分割后的列表添加到地图列表中 return maze_list # 返回地图列表 except FileNotFoundError: print(f"地图文件 {filename} 未找到!") return [] def draw_square(ci, ri, colorsign): """ 功能:绘制组成地图的小正方形 :param ci: 列索引 :param ri: 行索引 :param colorsign: 填充颜色 :return: """ tx = ci * cell_size - C * cell_size / 2 # 根据索引值计算每个正方形的起点(x坐标) ty = R * cell_size / 2 - ri * cell_size # 根据索引值计算每个正方形的起点(y坐标) map_t.penup() # 抬笔 map_t.goto(tx, ty) # 移动到绘图起点(正方形的左上角) if colorsign == '1': # 判断是否为墙(如果为墙,则随机生成填充颜色) r = random.randint(100, 130) # 红色值 g = random.randint(150, 180) # 绿色值 b = random.randint(200, 220) # 蓝色值 map_t.fillcolor(r, g, b) # 指定填充颜色 map_t.pencolor(r//2, g//2, b//2) # 设置边框颜色 else: map_t.fillcolor(colorsign) # 设置为指定的通道颜色 map_t.pencolor(100, 100, 100) # 设置边框颜色 map_t.pendown() # 落笔 map_t.begin_fill() # 填充开始 for i in range(4): # 绘制正方形 map_t.fd(cell_size) map_t.right(90) map_t.end_fill() # 填充结束 def draw_map(mazelist): """ 功能:遍历地图列表绘制迷宫地图 :param mazelist: 保存地图数据的列表 :return: """ turtle.tracer(0) # 隐藏动画效果 global area_sign # 全局变量,记录入口出口索引位置 map_t.ht() # 隐藏海龟光标 for ri in range(R): # 遍历行 for ci in range(C): # 遍历列 item = mazelist[ri][ci] if item in ['1']: # 判断墙 draw_square(ci, ri, '1') # 绘制墙 elif item == "S": # 判断入口 draw_square(ci, ri, road_color) # 绘制通道 draw_sign(ci, ri, '入口') # 标记入口 area_sign['entry_i'] = (ci, ri) # 保存入口索引 elif item == "E": # 判断出口 draw_square(ci, ri, road_color) # 绘制通道 draw_sign(ci, ri, '出口') # 标记出口 area_sign['exit_i'] = (ci, ri) # 保存出口索引 else: draw_square(ci, ri, road_color) # 绘制通道 turtle.tracer(1) # 显示动画效果 def draw_sign(ci, ri, word): """ 功能:绘制入口出口标记 :param ci: 列索引 :param ri: 行索引 :param word: 标记文字内容 :return: """ cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置 sign_t.ht() # 隐藏海龟光标 sign_t.penup() # 抬笔 sign_t.goto(cx, cy) # 移动到标记位置 sign_t.color('red') # 设置画笔为红色 sign_t.write(word, align='center', font=('黑体', 12, 'normal')) # 绘制标记文字 def win_tip(): """ 功能:制作过关提示 :return: """ global level, start_time elapsed_time = time.time() - start_time c_move_to(manual_t, (0, 0)) manual_t.color('blue') if level == 3: message = f'\n恭喜您顺利通关!\n总用时: {elapsed_time:.1f}秒\n按Enter键退出游戏' manual_t.write(message, align='center', font=('黑体', 20, 'bold')) turtle.onkey(turtle.bye, 'Return') # 监听按下Enter键退出游戏 else: message = f'\n恭喜过关!用时: {elapsed_time:.1f}秒\n按下Enter进入下一关!' manual_t.write(message, align='center', font=('黑体', 20, 'bold')) level += 1 manual_t.color('red') turtle.onkey(level_init, 'Return') # 监听按下Enter键 def manual_move(d): """ 功能:手动走迷宫时通用探索并移动函数 :param d: 向不同方面走时索引的变化规则 :return: """ dc, dr = d # 将表示方向的元组分别赋值给两个变量dcdr,其中dc为x轴方向,dr为y轴方向 current_x = round(manual_t.xcor(), 1) current_y = round(manual_t.ycor(), 1) rici = ctoi(current_x + dc * cell_size, current_y + dr * cell_size) # 获取行列索引 ci, ri = rici # 检查索引是否在有效范围内 if not (0 <= ri < R and 0 <= ci < C): return point = mazeList[ri][ci] # 获取地图列表中对应点的值 if point == '0': # 通路 manual_t.color('red') mazeList[ri][ci] = '$' # 将当前位置标记为已探索 manual_t.forward(cell_size) # 向前移动 elif point == '$': # 已探索 manual_t.color(road_color) # 绘制通道相同颜色的线,达到擦除痕迹的效果 # 将当前位置的前一个点设置为未探索 prev_ri, prev_ci = ctoi(current_x, current_y) if 0 <= prev_ri < R and 0 <= prev_ci < C: mazeList[prev_ri][prev_ci] = '0' manual_t.forward(cell_size) manual_t.color('red') elif point == 'E': # 出口 win_tip() def up_move(): # 朝上 manual_t.setheading(90) # 设置海龟朝向 manual_move(direction[2]) # 手动探索并移动 def down_move(): # 朝下 manual_t.setheading(270) # 设置海龟朝向 manual_move(direction[3]) # 手动探索并移动 def left_move(): # 朝左 manual_t.setheading(180) # 设置海龟朝向 manual_move(direction[1]) # 手动探索并移动 def right_move(): # 朝右 manual_t.setheading(0) # 设置海龟朝向 manual_move(direction[0]) # 手动探索并移动 def manual_path(): """ 功能:手动走迷宫 :return: """ global start_time start_time = time.time() # 记录开始时间 update_timer() # 启动计时器 manual_t.clear() # 清除绘图 auto_t.ht() # 隐藏海龟 auto_t.clear() # 清除绘图 global mazeList # 定义全局变量 mazeList = get_map(txt_path) # 重新读取地图数据 c_move_to(manual_t, itoc(area_sign['entry_i'])) # 移动到入口位置 manual_t.st() # 显示手动走迷宫所用的海龟光标 manual_t.width(3) # 设置画笔粗细为3像素 manual_t.color('red') # 设置画笔为红色 turtle.listen() # 监听键盘事件 turtle.onkey(up_move, 'Up') # 按下向上方向键 turtle.onkey(down_move, 'Down') # 按下向下方向键 turtle.onkey(left_move, 'Left') # 按下向左方向键 turtle.onkey(right_move, 'Right') # 按下向右方向键 def auto_path(): """ 功能:查看答案(自动走迷宫) :return: """ global mazeList # 定义全局变量 mazeList = get_map(txt_path) # 重新读取地图数据 manual_t.ht() # 隐藏海龟 manual_t.clear() # 清除绘图 auto_t.clear() # 清除绘图 auto_t.pensize(5) # 设置画笔粗细 auto_t.speed(1) # 绘图速度(慢速以便观察) auto_t.ht() # 隐藏海龟光标 find(mazeList) # 开始探索 def find(mazeList): """ 功能:开始探索 :param mazeList: 地图列表 :return: """ auto_t.clear() # 清空帮助 start_r, start_c = 0, 0 for ri in range(R): for ci in range(C): item = mazeList[ri][ci] if item == "S": start_r, start_c = ri, ci auto_t.penup() # 抬笔 draw_path(start_c, start_r) find_next(mazeList, start_c, start_r) def find_next(mlist, ci, ri): """ 功能:递归搜索判断是否为通路 :param mlist: 地图列表 :param ci: 列索引 :param ri: 行索引 :return: 布尔值,表示是否为通路 """ if mlist[ri][ci] == "E": imoveto(ci, ri) # 移动到出口 return True if not (0 <= ci < C and 0 <= ri < R): # 判断位置是否不合法 return False if mlist[ri][ci] in ['1', '$']: # 判断是否为墙或者已探索过的 return False mlist[ri][ci] = "$" # 标记已探索过 for d in direction: # 尝试从不同方向探索是否为通路,如果发现一条通路,则不再继续探索 dc, dr = d # 将索引变化规则的值分别赋值给dcdr,其中dc为x轴方向,dr为y轴方向 found = find_next(mlist, ci + dc, ri + dr) # 递归调用 if found: # 如果是通路则绘制线路 draw_path(ci, ri) # 绘制线路 return True # 返回True,不再探索 return False # 当所有方向都不通时,返回False def draw_path(ci, ri, color="green"): # 自动绘制用 """ 功能:根据索引位置移动海龟(画线) :param ci: 列索引 :param ri: 行索引 :param color: 画笔颜色 :return: """ auto_t.st() # 显示海龟光标 cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置 auto_t.color(color) auto_t.goto(cx, cy) def update_timer(): """ 更新并显示计时器 """ if manual_t.isvisible(): # 只在手动模式下显示计时器 elapsed_time = time.time() - start_time timer_t.clear() timer_t.ht() timer_t.penup() timer_t.goto(0, R * cell_size / 2 + 20) timer_t.color("purple") timer_t.write(f"用时: {elapsed_time:.1f}秒", align="center", font=("宋体", 14, "normal")) turtle.ontimer(update_timer, 100) # 每100毫秒更新一次 def draw_game_info(): """ 绘制游戏信息(标题、关卡、操作说明) """ # 绘制游戏标题 title_t = turtle.Turtle() title_t.ht() title_t.penup() title_t.goto(0, R * cell_size / 2 + 60) title_t.color("purple") title_t.write(game_title, align="center", font=("黑体", 24, "bold")) # 绘制关卡信息 level_t = turtle.Turtle() level_t.ht() level_t.penup() level_t.goto(0, R * cell_size / 2 + 30) level_t.color("navy") level_t.write(f"关卡: {int(level)}", align="center", font=("楷体", 18, "bold")) # 绘制操作说明 hint_t = turtle.Turtle() hint_t.ht() hint_t.penup() hint_t.goto(0, -R * cell_size / 2 - 40) hint_t.color("darkgreen") hint_t.write("操作说明: ↑↓←→移动海龟 | F1:自动寻路 | F2:手动模式 | R:重新开始", align="center", font=("宋体", 12, "normal")) def restart_game(): """ 重新开始当前关卡 """ global mazeList manual_t.clear() auto_t.clear() turtle.clear() level_init() def level_init(): """ 功能:关卡初始化(手动绘制关卡数) """ global txt_path, level, mazeList, R, C # 清除所有绘图 manual_t.clear() auto_t.clear() turtle.clear() # 设置关卡地图文件 if level == 1: # 第一关 txt_path = "map/map1.txt" elif level == 2: # 第二关 txt_path = "map/map2.txt" elif level == 3: # 第三关 txt_path = "map/map3.txt" else: turtle.bye() # 退出程序 return # 获取地图数据 mazeList = get_map(txt_path) if not mazeList: print("无法加载地图,游戏结束") turtle.bye() return R, C = len(mazeList), len(mazeList[0]) # 设置窗口尺寸(根据地图大小调整) turtle.setup(width=C * cell_size + 200, height=R * cell_size + 200) turtle.bgcolor("#f0f8ff") # 设置背景色为淡蓝色 turtle.title("海龟迷宫闯关游戏") # 绘制游戏信息 draw_game_info() # 绘制地图 draw_map(mazeList) # 设置键盘监听 turtle.listen() turtle.onkey(auto_path, "F1") # F1键自动寻路 turtle.onkey(manual_path, "F2") # F2键手动模式 turtle.onkey(restart_game, "r") # R键重新开始 turtle.onkey(restart_game, "R") # R键重新开始 # 创建地图文件(如果不存在) def create_map_files(): """创建默认地图文件""" import os os.makedirs("map", exist_ok=True) # 第一关地图 if not os.path.exists("map/map1.txt"): with open("map/map1.txt", "w", encoding="utf-8") as f: f.write("1 1 1 1 1 1 1 1 1 1\n") f.write("1 S 0 0 0 1 0 0 0 1\n") f.write("1 1 1 0 1 1 0 1 0 1\n") f.write("1 0 0 0 0 0 0 1 0 1\n") f.write("1 0 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 1\n") f.write("1 1 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 1 1\n") f.write("1 E 1 1 1 1 1 1 1 1\n") # 第二关地图 if not os.path.exists("map/map2.txt"): with open("map/map2.txt", "w", encoding="utf-8") as f: f.write("1 1 1 1 1 1 1 1 1 1\n") f.write("1 S 0 0 0 1 0 0 0 1\n") f.write("1 1 1 0 1 1 0 1 0 1\n") f.write("1 0 0 0 0 0 0 1 0 1\n") f.write("1 0 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 1\n") f.write("1 1 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 1 1\n") f.write("1 0 0 0 0 0 0 0 E 1\n") f.write("1 1 1 1 1 1 1 1 1 1\n") # 第三关地图 if not os.path.exists("map/map3.txt"): with open("map/map3.txt", "w", encoding="utf-8") as f: f.write("1 1 1 1 1 1 1 1 1 1 1 1\n") f.write("1 S 0 0 0 1 0 0 0 0 0 1\n") f.write("1 1 1 0 1 1 0 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 1 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 0 1 1 1\n") f.write("1 0 0 0 0 0 0 0 0 0 0 1\n") f.write("1 1 1 1 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 1 1 1 1\n") f.write("1 0 0 0 0 0 0 0 0 0 E 1\n") f.write("1 1 1 1 1 1 1 1 1 1 1 1\n") # 主程序入口 if __name__ == "__main__": # 创建地图文件(如果不存在) create_map_files() # 初始化游戏 level = 1 level_init() # 隐藏海龟光标 turtle.ht() # 启动游戏主循环 turtle.mainloop()随机生成迷宫
08-25
# -*- coding: UTF-8 -*- """ 海龟迷宫闯关游戏 - 增强版(食物、分数状态系统) """ import turtle # 导入海龟绘图模块 import random # 导入随机数模块 import time # 导入时间模块 import os # 导入操作系统模块 # 设置全局颜色模式 turtle.colormode(255) # 允许使用0-255的RGB值 game_title = '小海龟大迷宫闯关小游戏' # 游戏名字 level = 1 # 当前关卡,从1开始 random_mode = False # 是否随机生成迷宫 score = 0 # 玩家分数 hunger_level = 100 # 饥饿度(100为饱,0为饿) last_eat_time = time.time() # 上次进食时间 status_message = "" # 状态消息 status_expire = 0 # 状态消息过期时间 '''绘制地图用的全局变量''' txt_path = 'map/map1.txt' # 地图信息文本文件路径及名称 road_color = (191, 217, 225) # 迷宫通道的颜色 food_color = (255, 215, 0) # 食物颜色(金色) R, C = 0, 0 # 迷宫地图的总行数R、总列数C cell_size = 20 # 一个格子的尺寸 area_sign = {} # 记录入口出口索引位置 mazeList = [] # 地图列表 food_positions = [] # 食物位置列表 # 创建海龟对象的函数 def create_turtles(): global map_t, sign_t, auto_t, manual_t, timer_t, score_t, status_t # 重置所有海龟对象 for t in [map_t, sign_t, auto_t, manual_t, timer_t, score_t, status_t]: if t: t.clear() t.hideturtle() del t # 创建新的海龟对象 map_t = turtle.Turtle() # 绘制地图的海龟 map_t.speed(0) # 设置绘图速度最快(地图绘制) map_t.hideturtle() sign_t = turtle.Turtle() # 绘制入口出口标记的海龟 sign_t.hideturtle() auto_t = turtle.Turtle() # 自动走迷宫的海龟 auto_t.pensize(5) # 画笔粗细(自动) auto_t.speed(0) # 设置绘图速度最快(手动) auto_t.hideturtle() manual_t = turtle.Turtle() # 手动走迷宫的海龟 manual_t.pensize(5) # 画笔粗细(手动) manual_t.speed(0) # 设置绘图速度最快(手动) manual_t.shape('turtle') # 设置海龟光标为小海龟(手动) manual_t.hideturtle() timer_t = turtle.Turtle() # 显示计时器的海龟 timer_t.hideturtle() score_t = turtle.Turtle() # 显示分数的海龟 score_t.hideturtle() status_t = turtle.Turtle() # 显示状态的海龟 status_t.hideturtle() # 初始化海龟对象 map_t = None sign_t = None auto_t = None manual_t = None timer_t = None score_t = None status_t = None create_turtles() # 要探索4个方向对应索引的变化规则 direction = [ (1, 0), # 右 (-1, 0), # 左 (0, 1), # 上 (0, -1) # 下 ] # 计时相关变量 start_time = 0 def imoveto(ci, ri): """ 功能:根据索引位置移动海龟(不画线) :param ci: 列索引 :极param ri: 行索引 :return: """ auto_t.penup() # 抬笔 cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置 auto_t.goto(c极x, cy) # 移动到指定位置 auto_t.pendown() # 落笔 auto_t.shape('turtle') # 设置海龟光标的形状 auto_t.color('red') # 设置画笔颜色为红色 auto_t.showturtle() # 显示海龟光标 def c_move_to(t, ctuple): # 移动到指定位置 """ 功能:根据坐标位置移动到指定位置(不画线) :param t: 海龟对象 :param ctuple: 记录坐标位置的元组 :return: """ t.hideturtle() # 隐藏海龟光标 t.penup() # 抬笔 t.goto(ctuple[0], ctuple[1]) # 移动到坐标指定的位置 t.pendown() # 落笔 def itoc(ituple): """ 将索引位置转换为实际坐标位置 :param ituple: 行、列索引组成的元组 :return: 实际坐标位置 """ ci = ituple[0] ri = ituple[1] tx = ci * cell_size - C * cell_size / 2 # 根据索引值计算每个正方形的起点(x坐标) ty = R * cell_size / 2 - ri * cell_size # 根据索引值计算每个正方形的起点(y坐标) cx = tx + cell_size / 2 # 正方形中心的x坐标 cy = ty - cell_size / 2 # 正方形中心的y坐标 return (cx, cy) def ctoi(cx, cy): """ 根据cxcy求在列表中对应的索引 :param cx: x轴坐标 :param cy: y轴坐标 :return: 元组,(ci,ri) """ ci = ((C - 1) * cell_size / 2 + cx) / cell_size # 计算列索引 ri = ((R - 1) * cell_size / 2 - cy) / cell_size # 计算行索引 return (int(ci), int(ri)) # 返回行列索引的元组 def get_map(filename): """ 功能:读取保存地图的文本文件内容到列表 :param filename: 地图文件名 :return: 地图列表 """ try: with open(filename, 'r', encoding='utf-8') as f: # 打开文件 fl = f.readlines() # 读取全部行 maze_list = [] # 保存地图的列表 for line in fl: # 将读取的内容以空格分割为二维列表 line = line.strip() # 去掉空格 if line: # 确保不是空行 line_list = line.split(" ") # 以空格进行分割为列表 maze_list.append(line_list) # 将分割后的列表添加到地图列表中 return maze_list # 返回地图列表 except FileNotFoundError: print(f"地图文件 {filename} 未找到!") return [] def draw_square(ci, ri, colorsign): """ 功能:绘制组成地图的小正方形 :param ci: 列索引 :param ri: 行索引 :param colorsign: 填充颜色 :return: """ tx = ci * cell_size - C * cell_size / 2 # 根据索引值计算每个正方形的起点(x坐标) ty = R * cell_size / 2 - ri * cell_size # 根据索引值计算每个正方形的起点(y坐标) map_t.penup() # 抬笔 map_t.goto(tx, ty) # 移动到绘图起点(正方形的左上角) if colorsign == '1': # 判断是否为墙 r = random.randint(100, 130) # 红色值 g = random.randint(150, 180) # 绿色值 b = random.randint(200, 220) # 蓝色值 map_t.fillcolor(r, g, b) # 指定填充颜色 map_t.pencolor(r//2, g//2, b//2) # 设置边框颜色 elif colorsign == 'F': # 食物 map_t.fillcolor(food_color) # 食物颜色 map_t.pencolor(180, 150, 0) # 食物边框颜色 else: # 通道 map_t.fillcolor(road_color) # 设置为通道颜色 map_t.pencolor(100, 100, 100) # 设置边框颜色 map_t.pendown() # 落笔 map_t.begin_fill() # 填充开始 for i in range(4): # 绘制正方形 map_t.fd(cell_size) map_t.right(90) map_t.end_fill() # 填充结束 # 如果是食物,绘制食物图标 if colorsign == 'F': cx, cy = itoc((ci, ri)) map_t.penup() map_t.goto(cx, cy) map_t.pendown() map_t.dot(cell_size // 2, food_color) def draw_map(mazelist): """ 功能:遍历地图列表绘制迷宫地图 :param mazelist: 保存地图数据的列表 :return: """ global food_positions food_positions = [] # 重置食物位置 turtle.tracer(0) # 隐藏动画效果 global area_sign # 全局变量,记录入口出口索引位置 map_t.hideturtle() # 隐藏海龟光标 for ri in range(R): # 遍历行 for ci in range(C): # 遍历列 item = mazelist[ri][ci] if item in ['1']: # 判断墙 draw_square(ci, ri, '1') # 绘制墙 elif item == "S": # 判断入口 draw_square(ci, ri, '0') # 绘制通道 draw_sign(ci, ri, '入口') # 标记入口 area_sign['entry_i'] = (ci, ri) # 保存入口索引 elif item == "E": # 判断出口 draw_square(ci, ri, '0') # 绘制通道 draw_sign(ci, ri, '出口') # 标记出口 area_sign['exit_i'] = (ci, ri) # 保存出口索引 elif item == "F": # 食物 draw_square(ci, ri, 'F') # 绘制食物 food_positions.append((ci, ri)) # 记录食物位置 else: draw_square(ci, ri, '0') # 绘制通道 turtle.tracer(1) # 显示动画效果 def draw_sign(ci, ri, word): """ 功能:绘制入口出口标记 :param ci: 列索引 :param ri: 行索引 :param word: 标记文字内容 :return: """ cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置 sign_t.hideturtle() # 隐藏海龟光标 sign_t.penup() # 抬笔 sign_t.goto(cx, cy) # 移动到标记位置 sign_t.color('red') # 设置画笔为红色 sign_t.write(word, align='center', font=('黑体', 12, 'normal')) # 绘制标记文字 def win_tip(): """ 功能:制作过关提示 :return: """ global level, start_time, random_mode, score elapsed_time = time.time() - start_time c_move_to(manual_t, (0, 0)) manual_t.color('blue') if random_mode: message = f'\n恭喜您成功通过随机迷宫!\n用时: {elapsed_time:.1f}秒\n得分: {score}\n按Enter键返回主菜单' manual_t.write(message, align='center', font=('黑体', 20, 'bold')) turtle.onkey(main_menu, 'Return') # 监听按下Enter键返回主菜单 elif level == 3: message = f'\n恭喜您顺利通关!\n总用时: {elapsed_time:.1f}秒\n总得分: {score}\n按Enter键退出游戏' manual_t.write(message, align='center', font=('黑体', 20, 'bold')) turtle.onkey(turtle.bye, 'Return') # 监听按下Enter键退出游戏 else: message = f'\n恭喜过关!用时: {elapsed_time:.1f}秒\n得分: {score}\n按下Enter进入下一关!' manual_t.write(message, align='center', font=('黑体', 20, 'bold')) level += 1 manual_t.color('red') turtle.onkey(level_init, 'Return') # 监听按下Enter键 def manual_move(d): """ 功能:手动走迷宫时通用探索并移动函数 :param d: 向不同方面走时索引的变化规则 :return: """ global score, hunger_level, last_eat_time, status_message, status_expire dc, dr = d # 将表示方向的元组分别赋值给两个变量dcdr,其中dc为x轴方向,dr为y轴方向 current_x = round(manual_t.xcor(), 1) current_y = round(manual_t.ycor(), 1) rici = ctoi(current_x + dc * cell_size, current_y + dr * cell_size) # 获取行列索引 ci, ri = rici # 检查索引是否在有效范围内 if not (0 <= ri < R and 0 <= ci < C): return point = mazeList[ri][ci] # 获取地图列表中对应点的值 if point == '0': # 通路 manual_t.color('red') mazeList[ri][ci] = '$' # 将当前位置标记为已探索 manual_t.forward(cell_size) # 向前移动 elif point == '$': # 已探索 manual_t.color(road_color) # 绘制通道相同颜色的线,达到擦除痕迹的效果 # 将当前位置的前一个点设置为未探索 prev_ri, prev_ci = ctoi(current_x, current_y) if 0 <= prev_ri < R and 0 <= prev_ci < C: mazeList[prev_ri][prev_ci] = '0' manual_t.forward(cell_size) manual_t.color('red') elif point == 'F': # 食物 # 吃到食物 manual_t.color('red') mazeList[ri][ci] = '$' # 将当前位置标记为已探索 manual_t.forward(cell_size) # 向前移动 # 更新分数饥饿度 score += 10 hunger_level = min(100, hunger_level + 30) # 增加饥饿度 last_eat_time = time.time() # 显示状态消息 status_message = "我吃饱了!" status_expire = time.time() + 2 # 2秒后消失 # 更新分数显示 update_score() # 更新状态显示 update_status() # 移除食物 if (ci, ri) in food_positions: food_positions.remove((ci, ri)) # 重新绘制该位置(变成通道) draw_square(ci, ri, '0') elif point == 'E': # 出口 win_tip() def up_move(): # 朝上 manual_t.setheading(90) # 设置海龟朝向 manual_move(direction[2]) # 手动探索并移动 def down_move(): # 朝下 manual_t.setheading(270) # 设置海龟朝向 manual_move(direction[3]) # 手动探索并移动 def left_move(): # 朝左 manual_t.setheading(180) # 设置海龟朝向 manual_move(direction[1]) # 手动探索并移动 def right_move(): # 朝右 manual_t.setheading(0) # 设置海龟朝向 manual_move(direction[0]) # 手动探索并移动 def manual_path(): """ 功能:手动走迷宫 :return: """ global start_time, hunger_level, last_eat_time, status_message, status_expire start_time = time.time() # 记录开始时间 last_eat_time = time.time() # 重置进食时间 hunger_level = 100 # 重置饥饿度 status_message = "" # 清空状态消息 update_timer() # 启动计时器 update_score() # 更新分数显示 update_status() # 更新状态显示 manual_t.clear() # 清除绘图 auto_t.hideturtle() # 隐藏海龟 auto_t.clear() # 清除绘图 global mazeList # 定义全局变量 if not random_mode: mazeList = get_map(txt_path) # 重新读取地图数据 c_move_to(manual_t, itoc(area_sign['entry_i'])) # 移动到入口位置 manual_t.showturtle() # 显示手动走迷宫所用的海龟光标 manual_t.width(3) # 设置画笔粗细为3像素 manual_t.color('red') # 设置画笔为红色 turtle.listen() # 监听键盘事件 turtle.onkey(up_move, 'Up') # 按下向上方向键 turtle.onkey(down_move, 'Down') # 按下向下方向键 turtle.onkey(left_move, 'Left') # 按下向左方向键 turtle.onkey(right_move, 'Right') # 按下向右方向键 def auto_path(): """ 功能:查看答案(自动走迷宫) :return: """ global mazeList # 定义全局变量 if not random_mode: mazeList = get_map(txt_path) # 重新读取地图数据 manual_t.hideturtle() # 隐藏海龟 manual_t.clear() # 清除绘图 auto_t.clear() # 清除绘图 auto_t.pensize(5) # 设置画笔粗细 auto_t.speed(1) # 绘图速度(慢速以便观察) auto_t.hideturtle() # 隐藏海龟光标 find(mazeList) # 开始探索 def find(mazeList): """ 功能:开始探索 :param mazeList: 地图列表 :return: """ auto_t.clear() # 清空帮助 start_r, start_c = 0, 0 for ri in range(R): for ci in range(C): item = mazeList[ri][ci] if item == "S": start_r, start_c = ri, ci auto_t.penup() # 抬笔 draw_path(start_c, start_r) find_next(mazeList, start_c, start_r) def find_next(mlist, ci, ri): """ 功能:递归搜索判断是否为通路 :param mlist: 地图列表 :param ci: 列索引 :param ri: 行索引 :return: 布尔值,表示是否为通路 """ if mlist[ri][ci] == "E": imoveto(ci, ri) # 移动到出口 return True if not (0 <= ci < C and 0 <= ri < R): # 判断位置是否不合法 return False if mlist[ri][ci] in ['1', '$']: # 判断是否为墙或者已探索过的 return False mlist[ri][ci] = "$" # 标记已探索过 for d in direction: # 尝试从不同方向探索是否为通路,如果发现一条通路,则不再继续探索 dc, dr = d # 将索引变化规则的值分别赋值给dcdr,其中dc为x轴方向,dr为y轴方向 found = find_next(mlist, ci + dc, ri + dr) # 递归调用 if found: # 如果是通路则绘制线路 draw_path(ci, ri) # 绘制线路 return True # 返回True,不再探索 return False # 当所有方向都不通时,返回False def draw_path(ci, ri, color="green"): # 自动绘制用 """ 功能:根据索引位置移动海龟(画线) :param ci: 列索引 :param ri: 行索引 :param color: 画笔颜色 :return: """ auto_t.showturtle() # 显示海龟光标 cx, cy = itoc((ci, ri)) # 将索引位置转换为坐标位置 auto_t.color(color) auto_t.goto(cx, cy) def update_timer(): """ 更新并显示计时器 """ if manual_t.isvisible(): # 只在手动模式下显示计时器 elapsed_time = time.time() - start_time timer_t.clear() timer_t.hideturtle() timer_t.penup() timer_t.goto(0, R * cell_size / 2 + 20) timer_t.color("purple") timer_t.write(f"用时: {elapsed_time:.1f}秒", align="center", font=("宋体", 14, "normal")) turtle.ontimer(update_timer, 100) # 每100毫秒更新一次 def update_score(): """ 更新并显示分数 """ global score_t score_t.clear() score_t.hideturtle() score_t.penup() score_t.goto(-C * cell_size / 2 + 10, R * cell_size / 2 + 10) score_t.color("blue") score_t.write(f"分数: {score}", align="left", font=("宋体", 14, "bold")) def update_status(): """ 更新并显示状态 """ global hunger_level, status_message, status_expire, status_t current_time = time.time() # 更新饥饿度(每10秒减少10点) if current_time - last_eat_time > 10 and hunger_level > 0: hunger_level = max(0, hunger_level - 10) last_eat_time = current_time # 检查是否需要显示饥饿状态 if hunger_level < 30 and (status_expire < current_time or status_message == "我吃饱了!"): status_message = "我饿了!" status_expire = current_time + 3 # 显示3秒 # 清除过期的状态消息 if status_expire < current_time and status_message: status_message = "" # 绘制状态消息 status_t.clear() status_t.hideturtle() status_t.penup() status_t.goto(0, R * cell_size / 2 - 10) if status_message: if status_message == "我饿了!": status_t.color("red") else: status_t.color("green") status_t.write(status_message, align="center", font=("黑体", 16, "bold")) # 每秒钟更新一次状态 turtle.ontimer(update_status, 1000) def draw_game_info(): """ 绘制游戏信息(标题、关卡、操作说明) """ # 绘制游戏标题 title_t = turtle.Turtle() title_t.hideturtle() title_t.penup() title_t.goto(0, R * cell_size / 2 + 60) title_t.color("purple") title_t.write(game_title, align="center", font=("黑体", 24, "bold")) title_t.hideturtle() # 绘制关卡信息 level_t = turtle.Turtle() level_t.hideturtle() level_t.penup() level_t.goto(0, R * cell_size / 2 + 30) level_t.color("navy") if random_mode: level_t.write("随机迷宫", align="center", font=("楷体", 18, "bold")) else: level_t.write(f"关卡: {int(level)}", align="center", font=("楷体", 18, "bold")) level_t.hideturtle() # 绘制操作说明 hint_t = turtle.Turtle() hint_t.hideturtle() hint_t.penup() hint_t.goto(0, -R * cell_size / 2 - 40) hint_t.color("darkgreen") if random_mode: hint_t.write("操作说明: ↑↓←→移动海龟 | F1:自动寻路 | R:重新开始 | ESC:主菜单", align="center", font=("宋体", 12, "normal")) else: hint_t.write("操作说明: ↑↓←→移动海龟 | F1:自动寻路 | F2:手动模式 | R:重新开始 | ESC:主菜单", align="center", font=("宋体", 12, "normal")) hint_t.hideturtle() def restart_game(): """ 重新开始当前关卡 """ global score score = 0 # 重置分数 create_turtles() # 重新创建海龟对象 turtle.clear() # 清除屏幕 if random_mode: generate_random_maze_display(15, 15) # 重新生成随机迷宫 else: level_init() def level_init(): """ 功能:关卡初始化 """ global txt_path, level, mazeList, R, C, random_mode, score score = 0 # 重置分数 # 清除所有绘图 create_turtles() # 重新创建海龟对象 turtle.clear() # 清除屏幕 # 设置关卡地图文件 if level == 1: # 第一关 txt_path = "map/map1.txt" elif level == 2: # 第二关 txt_path = "map/map2.txt" elif level == 3: # 第三关 txt_path = "map/map3.txt" else: turtle.bye() # 退出程序 return # 获取地图数据 mazeList = get_map(txt_path) if not mazeList: print("无法加载地图,游戏结束") turtle.bye() return R, C = len(mazeList), len(mazeList[0]) random_mode = False # 设置窗口尺寸(根据地图大小调整) turtle.setup(width=C * cell_size + 200, height=R * cell_size + 200) turtle.bgcolor("#f0f8ff") # 设置背景色为淡蓝色 turtle.title("海龟迷宫闯关游戏") # 绘制游戏信息 draw_game_info() # 绘制地图 draw_map(mazeList) # 设置键盘监听 turtle.listen() turtle.onkey(auto_path, "F1") # F1键自动寻路 turtle.onkey(manual_path, "F2") # F2键手动模式 turtle.onkey(restart_game, "r") # R键重新开始 turtle.onkey(restart_game, "R") # R键重新开始 turtle.onkey(main_menu, "Escape") # ESC键返回主菜单 # 随机迷宫生成算法(深度优先搜索) def generate_random_maze(rows, cols): """ 使用深度优先搜索算法生成随机迷宫 :param rows: 迷宫行数 :param cols: 迷宫列数 :return: 二维列表表示的迷宫 """ # 初始化迷宫,全部为墙 maze = [['1' for _ in range(cols)] for _ in range(rows)] # 确保行数列数为奇数 if rows % 2 == 0: rows -= 1 if cols % 2 == 0: cols -= 1 # 设置起点终点 start_r, start_c = 1, 1 end_r, end_c = rows-2, cols-2 # 使用深度优先搜索生成迷宫 stack = [(start_r, start_c)] maze[start_r][start_c] = 'S' maze[end_r][end_c] = 'E' # 定义四个方向(上、右、下、左) directions = [(0, 2), (2, 0), (0, -2), (-2, 0)] while stack: current_r, current_c = stack[-1] random.shuffle(directions) found = False for dr, dc in directions: nr, nc = current_r + dr, current_c + dc # 检查新位置是否在迷宫范围内且是墙 if 0 < nr < rows-1 and 0 < nc < cols-1 and maze[nr][nc] == '1': # 打通路径 maze[current_r + dr//2][current_c + dc//2] = '0' maze[nr][nc] = '0' stack.append((nr, nc)) found = True break if not found: stack.pop() # 确保起点终点是连通的 maze[start_r][start_c] = 'S' maze[end_r][end_c] = 'E' # 添加一些随机路径增加复杂度 for _ in range(rows*cols//10): r = random.randint(1, rows-2) c = random.randint(1, cols-2) if maze[r][c] == '1': # 检查是否可以作为通路 wall_count = 0 for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]: nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols: if maze[nr][nc] == '1': wall_count += 1 if wall_count >= 3: # 至少三面是墙 maze[r][c] = '0' # 添加食物 food_count = min(rows * cols // 20, 10) # 食物数量(最少10个) for _ in range(food_count): while True: r = random.randint(1, rows-2) c = random.randint(1, cols-2) if maze[r][c] == '0': # 确保在通路上 maze[r][c] = 'F' break return maze def generate_random_maze_display(rows, cols): """ 生成随机迷宫并显示 :param rows: 迷宫行数 :param cols: 迷宫列数 """ global mazeList, R, C, random_mode, level, score score = 0 # 重置分数 # 重新创建海龟对象 create_turtles() turtle.clear() # 生成随机迷宫 mazeList = generate_random_maze(rows, cols) R, C = len(mazeList), len(mazeList[0]) random_mode = True # 设置窗口尺寸(根据地图大小调整) turtle.setup(width=C * cell_size + 200, height=R * cell_size + 200) turtle.bgcolor("#f0f8ff") # 设置背景色为淡蓝色 turtle.title("随机迷宫挑战") # 绘制游戏信息 draw_game_info() # 绘制地图 draw_map(mazeList) # 找到入口位置 for ri in range(R): for ci in range(C): if mazeList[ri][ci] == "S": area_sign['entry_i'] = (ci, ri) # 设置键盘监听 turtle.listen() turtle.onkey(auto_path, "F1") # F1键自动寻路 turtle.onkey(manual_path, "F2") # F2键手动模式 turtle.onkey(restart_game, "r") # R键重新开始 turtle.onkey(restart_game, "R") # R键重新开始 turtle.onkey(main_menu, "Escape") # ESC键返回主菜单 def main_menu(): """ 显示游戏主菜单 """ global level, random_mode, score score = 0 # 重置分数 # 重新创建海龟对象 create_turtles() turtle.clear() turtle.bgcolor("#f0f8ff") turtle.title("海龟迷宫闯关游戏") turtle.hideturtle() # 重置游戏状态 level = 1 random_mode = False # 绘制游戏标题 title_t = turtle.Turtle() title_t.hideturtle() title_t.penup() title_t.goto(0, 100) title_t.color("purple") title_t.write(game_title, align="center", font=("黑体", 36, "bold")) title_t.hideturtle() # 绘制选项 menu_t = turtle.Turtle() menu_t.hideturtle() menu_t.penup() menu_t.goto(0, 0) menu_t.color("navy") menu_t.write("游戏选项", align="center", font=("楷体", 24, "bold")) menu_t.hideturtle() # 绘制菜单选项 options = [ ("1. 开始游戏 (关卡1)", 1, -30), ("2. 随机迷宫 (简单)", 2, -60), ("3. 随机迷宫 (中等)", 3, -90), ("4. 随机迷宫 (困难)", 4, -120), ("ESC. 退出游戏", 5, -180) ] for text, option_id, y_pos in options: t = turtle.Turtle() t.hideturtle() t.penup() t.goto(0, y_pos) t.color("darkgreen") t.write(text, align="center", font=("宋体", 18, "normal")) t.hideturtle() # 设置键盘监听 turtle.listen() turtle.onkey(lambda: start_game(1), "1") turtle.onkey(lambda: generate_random_maze_display(15, 15), "2") turtle.onkey(lambda: generate_random_maze_display(21, 21), "3") turtle.onkey(lambda: generate_random_maze_display(31, 31), "4") turtle.onkey(turtle.bye, "Escape") def start_game(selected_level): """ 开始指定关卡的游戏 :param selected_level: 选择的关卡 """ global level level = selected_level level_init() # 创建地图文件(如果不存在) def create_map_files(): """创建默认地图文件""" import os os.makedirs("map", exist_ok=True) # 第一关地图(添加食物) if not os.path.exists("map/map1.txt"): with open("map/map1.txt", "w", encoding="utf-8") as f: f.write("1 1 1 1 1 1 1 1 1 1\n") f.write("1 S 0 0 0 1 0 0 0 1\n") f.write("1 1 1 0 1 1 0 1 0 1\n") f.write("1 0 0 0 F 0 0 1 0 1\n") f.write("1 0 1 1 1 1 1 1 0 1\n") f.write("1 0 F 0 0 0 0 0 F 1\n") f.write("1 1 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 F 0 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 1 1\n") f.write("1 E 1 1 1 1 1 1 1 1\n") # 第二关地图(添加食物) if not os.path.exists("map/map2.txt"): with open("map/map2.txt", "w", encoding="utf-8") as f: f.write("1 1 1 1 1 1 1 1 1 1\n") f.write("1 S 0 0 0 1 0 0 0 1\n") f.write("1 1 1 0 1 1 0 1 0 1\n") f.write("1 0 0 F 0 0 0 1 0 1\n") f.write("1 0 1 1 1 1 1 1 0 1\n") f.write("1 0 0 F 0 0 F 0 0 1\n") f.write("1 1 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 1 1\n") f.write("1 0 0 0 0 0 0 0 E 1\n") f.write("1 1 1 1 1 1 1 1 1 1\n") # 第三关地图(添加食物) if not os.path.exists("map/map3.txt"): with open("map/map3.txt", "w", encoding="utf-8") as f: f.write("1 1 1 1 1 1 1 1 1 1 1 1\n") f.write("1 S 0 0 0 1 0 0 0 0 0 1\n") f.write("1 1 1 0 1 1 0 1 1 1 0 1\n") f.write("1 0 0 F 0 0 0 1 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 0 1 1 1\n") f.write("1 0 0 0 F 0 0 0 0 0 0 1\n") f.write("1 1 1 1 1 1 1 1 1 1 0 1\n") f.write("1 0 0 0 0 0 0 0 0 0 0 1\n") f.write("1 0 1 1 1 1 1 1 1 1 1 1\n") f.write("1 0 F 0 0 0 0 0 0 0 E 1\n") f.write("极1 1 1 1 1 1 1 1 1 1 1 1\n") # 主程序入口 if __name__ == "__main__": # 创建地图文件(如果不存在) create_map_files() # 显示主菜单 main_menu() # 启动游戏主循环 turtle.mainloop() 小乌龟呢,修改后的完整代码
最新发布
08-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值