diff --git a/第23讲扫雷/课堂成果/扫雷.py b/第23讲扫雷/课堂成果/扫雷.py new file mode 100644 index 0000000..2a19ba2 --- /dev/null +++ b/第23讲扫雷/课堂成果/扫雷.py @@ -0,0 +1,136 @@ +import tkinter as tk +from tkinter import messagebox +import random + +class MineSweeper: + def __init__(self, master, rows, cols, mines): + self.master = master + self.rows = rows + self.cols = cols + self.mines = mines + self.buttons = {} + self.board = [[0 for _ in range(cols)] for _ in range(rows)] + self.revealed = [[False for _ in range(cols)] for _ in range(rows)] + self.flags = [[False for _ in range(cols)] for _ in range(rows)] + self.game_over = False + + self.generate_mines() + self.create_widgets() + + def generate_mines(self): + count = 0 + while count < self.mines: + r = random.randint(0, self.rows - 1) + c = random.randint(0, self.cols - 1) + if self.board[r][c] == -1: + continue + self.board[r][c] = -1 + count += 1 + + # 更新周围数字 + for i in range(r - 1, r + 2): + for j in range(c - 1, c + 2): + if 0 <= i < self.rows and 0 <= j < self.cols and self.board[i][j] != -1: + self.board[i][j] += 1 + + def create_widgets(self): + for r in range(self.rows): + for c in range(self.cols): + b = tk.Button(self.master, width=2, height=1, font=('Arial', 14), + command=lambda r=r, c=c: self.reveal_cell(r, c)) + b.bind("", lambda e, r=r, c=c: self.toggle_flag(r, c)) + b.grid(row=r, column=c) + self.buttons[(r, c)] = b + + def reveal_cell(self, r, c): + if self.game_over or self.revealed[r][c] or self.flags[r][c]: + return + if self.board[r][c] == -1: + self.buttons[(r, c)].config(text="💣", bg="red") + self.game_over = True + self.show_all_mines() + messagebox.showerror("游戏结束", "你踩到雷啦!") + return + + self.flood_fill(r, c) + self.check_win() + + def flood_fill(self, r, c): + if not (0 <= r < self.rows and 0 <= c < self.cols): + return + if self.revealed[r][c] or self.flags[r][c]: + return + self.revealed[r][c] = True + val = self.board[r][c] + btn = self.buttons[(r, c)] + btn.config(relief=tk.SUNKEN, text=str(val) if val > 0 else "", state=tk.DISABLED, bg="lightgray") + if val == 0: + for dr in (-1, 0, 1): + for dc in (-1, 0, 1): + if dr != 0 or dc != 0: + self.flood_fill(r + dr, c + dc) + + def toggle_flag(self, r, c): + if self.revealed[r][c] or self.game_over: + return + if self.flags[r][c]: + self.flags[r][c] = False + self.buttons[(r, c)].config(text="") + else: + self.flags[r][c] = True + self.buttons[(r, c)].config(text="🚩") + + def show_all_mines(self): + for r in range(self.rows): + for c in range(self.cols): + if self.board[r][c] == -1: + self.buttons[(r, c)].config(text="💣") + + def check_win(self): + for r in range(self.rows): + for c in range(self.cols): + if self.board[r][c] != -1 and not self.revealed[r][c]: + return + self.game_over = True + messagebox.showinfo("胜利!", "恭喜你,排雷成功!") + +# 自定义参数 +def start_game(): + try: + rows = int(entry_rows.get()) + cols = int(entry_cols.get()) + mines = int(entry_mines.get()) + if rows <= 0 or cols <= 0 or mines <= 0 or mines >= rows * cols: + raise ValueError + except: + messagebox.showerror("输入错误", "请输入合理的行列和雷数") + return + + settings_window.destroy() + root = tk.Tk() + root.title("扫雷") + MineSweeper(root, rows, cols, mines) + root.mainloop() + +# 设置窗口 +settings_window = tk.Tk() +settings_window.title("扫雷设置") + +tk.Label(settings_window, text="行数:").grid(row=0, column=0) +entry_rows = tk.Entry(settings_window) +entry_rows.insert(0, "10") +entry_rows.grid(row=0, column=1) + +tk.Label(settings_window, text="列数:").grid(row=1, column=0) +entry_cols = tk.Entry(settings_window) +entry_cols.insert(0, "10") +entry_cols.grid(row=1, column=1) + +tk.Label(settings_window, text="雷数:").grid(row=2, column=0) +entry_mines = tk.Entry(settings_window) +entry_mines.insert(0, "10") +entry_mines.grid(row=2, column=1) + +tk.Button(settings_window, text="开始游戏", command=start_game).grid(row=3, column=0, columnspan=2) + +settings_window.mainloop() diff --git a/第24讲贪吃蛇/课堂成果/贪吃蛇.py b/第24讲贪吃蛇/课堂成果/贪吃蛇.py new file mode 100644 index 0000000..c769c35 --- /dev/null +++ b/第24讲贪吃蛇/课堂成果/贪吃蛇.py @@ -0,0 +1,71 @@ +"""Snake,经典街机游戏。 + +练习: +1. 如何让蛇变快或变慢? +2. 如何让蛇可以从一边穿越到另一边? +3. 如何让食物移动? +4. 修改为点击鼠标控制蛇移动。 +""" + +from random import randrange +from turtle import * +from freegames import square, vector +#pip install freegames -i https://pypi.douban.com/simple/ +# 食物位置 +food = vector(0, 0) +# 蛇的身体坐标列表(初始只有一个身体段) +snake = [vector(10, 0)] +# 移动方向 +aim = vector(10, 0) +def change(x, y): + """改变蛇的移动方向。""" + aim.x = x + aim.y = y +def inside(head): + """判断蛇头是否在边界内。""" + return -200 < head.x < 190 and -200 < head.y < 190 +def move(): + """让蛇向前移动一个单位。""" + head = snake[-1].copy() + head.move(aim) + # 如果撞墙或撞到自己,则游戏结束 + if not inside(head) or head in snake: + square(head.x, head.y, 9, 'red') # 撞到后显示红色方块 + update() + return + snake.append(head) # 增加新的蛇头 + if head == food: + print('蛇长:', len(snake)) + # 随机生成新的食物位置(在格子上) + food.x = randrange(-15, 15) * 10 + food.y = randrange(-15, 15) * 10 + else: + snake.pop(0) # 移除蛇尾 + + clear() + + # 画出蛇的每一节身体 + for body in snake: + square(body.x, body.y, 9, 'black') + + # 画出食物 + square(food.x, food.y, 9, 'green') + update() + ontimer(move, 100) # 每100毫秒调用一次 move,实现动画 + + +# 初始化窗口 +setup(420, 420, 370, 0) +hideturtle() # 隐藏箭头图标 +tracer(False) # 关闭自动刷新 +listen() # 开启键盘监听 + +# 绑定按键改变方向 +onkey(lambda: change(10, 0), 'Right') +onkey(lambda: change(-10, 0), 'Left') +onkey(lambda: change(0, 10), 'Up') +onkey(lambda: change(0, -10), 'Down') + +# 开始游戏 +move() +done()