Compare commits

...

1 Commits

Author SHA1 Message Date
5488a216f2 feat(第7讲 字母卡牌): 实现记忆游戏并添加开始界面
- 新增 jiyi.py 文件,实现字母翻牌记忆游戏功能
- 添加 youxijiemian.py 文件,创建游戏开始界面
- 使用 turtle 和 tkinter 模块分别实现游戏和界面
- 支持选择不同难度的游戏模式
2025-11-16 13:29:06 +08:00
23 changed files with 1064 additions and 392 deletions

2
.idea/PythonV3_V4.iml generated
View File

@ -4,7 +4,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" /> <excludeFolder url="file://$MODULE_DIR$/.venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.10 (PythonV3_V4)" jdkType="Python SDK" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="PyDocumentationSettings"> <component name="PyDocumentationSettings">

60
test.py
View File

@ -1,19 +1,43 @@
import datetime import random
dict={ landmarks = {"中国": "长城", "古埃及": "金字塔", "法国": "埃菲尔铁塔",
0:'星期一', "印度": "泰姬陵", "英国": "大本钟", "美国": "自由女神像",
1:'星期二', "意大利": "比萨斜塔", "澳大利亚": "悉尼歌剧院", "希腊": "帕特农神庙",
2:'星期三', "日本": "富士山", "南非": "好望角", "丹麦": "小美人鱼铜像",
3:'星期四', "巴西": "基督像", "泰国": "大皇宫", "加拿大": "CN塔",
4:'星期五', "荷兰": "桑斯安斯风车村", "俄罗斯": "红场", "捷克": "布拉格城堡",
5:'星期六', "沙特阿拉伯": "麦加大清真寺", "西班牙": "圣家族大教堂"
6:'星期日' }
}
print(datetime.datetime.now())
print(datetime.datetime.now().year)
print(datetime.datetime.now().month)
print(datetime.datetime.now().day)
print(datetime.datetime.now().hour)
print(datetime.datetime.now().minute)
print(datetime.datetime.now().second)
print(dict[datetime.datetime.now().weekday()])
for i in range(1, 11):
filename = "小小旅行家" + str(i) + ".txt"
ansfilename = "小小旅行家" + str(i) + "答案.txt"
file = open(filename, "w")
ansfile = open(ansfilename, "w")
# 写入抬头
file.write("姓名\n\n班级\n\n日期\n\n成绩\n\n")
file.write(" " * 20 + f"小小旅行家挑战赛 form {i}\n")
# 生成题目
questions = list(landmarks.keys())
random.shuffle(questions)
for j in range(10):
# 生成题目
key = questions[j]
file.write(f"{j + 1}.{key}的标志性建筑是()\n")
# 生成选项
correct_option = landmarks[key]
options = list(landmarks.values())
options.remove(correct_option)
random.shuffle(options)
all_options = options[0:4] + [correct_option]
random.shuffle(all_options)
for k in range(4):
if (all_options[k] == correct_option):
# 写入答案
ansfile.write(f"{j + 1}.{chr(65 + k)}\n")
file.write(f"{chr(65 + k)}.{all_options[k]}\n")
file.write("\n")
file.close()
ansfile.close()

View File

@ -1,3 +1,11 @@
```bash ```bash
pip install baidu-aip chardet -i https://pypi.tuna.tsinghua.edu.cn/simple pip install baidu-aip chardet -i https://pypi.tuna.tsinghua.edu.cn/simple
``` ```
```bash
pip freeze > requirements.txt
```
```bash
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
```

View File

@ -5,9 +5,9 @@
账号注册指南 账号注册指南
https://huewq7h021.feishu.cn/wiki/Ry3UwaoceiMRXgklbWtc9mEUn9f?from=from_copylink https://huewq7h021.feishu.cn/wiki/Ry3UwaoceiMRXgklbWtc9mEUn9f?from=from_copylink
""" """
#baidu-aip
from aip import AipSpeech from aip import AipSpeech
# pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ baidu-aip
# 定义常量 # 定义常量
APP_ID = '117920031' APP_ID = '117920031'
API_KEY = '4icZSO1OlMCU2ZiRMhgGCXFu' API_KEY = '4icZSO1OlMCU2ZiRMhgGCXFu'
@ -17,16 +17,16 @@ SECRET_KEY = '6wJldJ08m1jIX9hb0ULcJrIJ9D1OJW3c'
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY) client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
# 要合成的文本 # 要合成的文本
text = "歪比巴卜" text = "你好"
# 调用【语音合成器】 # 调用【语音合成器】
res = client.synthesis(text, 'zh', 1, { res = client.synthesis(text, 'zh', 1, {
'vol': 5, # 音量 'vol': 5, # 音量
'per': 0, # 发音人 'per': 5118, # 发音人
'spd': 5 # 语速 'spd': 2 # 语速
}) })
print(res) print(res)
#json
# 判断调用是否成功 # 判断调用是否成功
if not isinstance(res, dict): if not isinstance(res, dict):
# 获取合成音频结果 # 获取合成音频结果

View File

@ -33,7 +33,7 @@ update_id = None
root = tk.Tk() root = tk.Tk()
root.geometry('700x700') root.geometry('700x700')
root.title('颜值测评') root.title('颜值测评')
root.resizable(0, 0) root.resizable(1, 1)
# 百度AI的API配置信息 # 百度AI的API配置信息
APP_ID = '你的 AppID' APP_ID = '你的 AppID'
API_KEY = '你的 API Key' API_KEY = '你的 API Key'

View File

@ -0,0 +1,26 @@
import time
import pyautogui
import keyboard # 监听按键
#使用阿里云镜像源
#pip install pyautogui -i https://mirrors.aliyun.com/pypi/simple/
def double_click():
# 第一次点击
pyautogui.click()
print("第一次点击完成")
# 等待 10 秒
time.sleep(9.889)
# 第二次点击
pyautogui.click()
print("第二次点击完成")
print("'o' 键触发两次点击...")
# 持续监听
while True:
if keyboard.is_pressed('o'): # 当按下 o 键
double_click()
# 防止按住键多次触发
time.sleep(0.5)

View File

@ -4,7 +4,7 @@ from tkinter import messagebox
# 创建主窗口 # 创建主窗口
root = tk.Tk() root = tk.Tk()
root.geometry("300x400") # 设置窗口大小 root.geometry("300x400+500+200") # 设置窗口大小
root.title("10秒挑战") root.title("10秒挑战")
bg_image = tk.PhotoImage(file = '十秒挑战.png') bg_image = tk.PhotoImage(file = '十秒挑战.png')
@ -18,7 +18,7 @@ flag = False
def update_time(): def update_time():
if flag: if flag:
now = time.time() now = time.time()
timex = now - start_time timex = abs(now - start_time)
timex_label.config(text=f"时间:{timex:.3f}\n") timex_label.config(text=f"时间:{timex:.3f}\n")
root.after(10, update_time) # 10ms 后更新一次 root.after(10, update_time) # 10ms 后更新一次
@ -33,11 +33,11 @@ def change():
else: # 如果计时已经开始 else: # 如果计时已经开始
flag = False # 停止更新时间显示 flag = False # 停止更新时间显示
end_time = time.time() # 获取当前时间戳 end_time = time.time() # 获取当前时间戳
timex = end_time - start_time # 计算实际耗时 timex =end_time - start_time # 计算实际耗时
goal = 10 # 目标耗时 goal = 10 # 目标耗时
# 计算时间差 # 计算时间差
time_difference = timex - goal time_difference = abs(timex - goal)
# 更新显示 # 更新显示
timex_label.config(text=f"实际时间:{timex:.3f}\n误差:{time_difference:.3f}") timex_label.config(text=f"实际时间:{timex:.3f}\n误差:{time_difference:.3f}")
@ -46,7 +46,7 @@ def change():
if f'{time_difference:.3f}' == '0.000': if f'{time_difference:.3f}' == '0.000':
messagebox.showinfo("结果","难以置信!你就是掌控时间的神!") messagebox.showinfo("结果","难以置信!你就是掌控时间的神!")
else: else:
messagebox.showinfo("结果","还差点点,再接再厉吧!") messagebox.showinfo("结果","还差亿点点,再接再厉吧!")
# 创建标签 # 创建标签
title_label = tk.Label(root, text="10 秒挑战", font=("楷体", 16)) title_label = tk.Label(root, text="10 秒挑战", font=("楷体", 16))

View File

@ -0,0 +1,12 @@
# 预置内容,请勿改动
a = {"01新疆碎片": (225, 231), "02西藏碎片": (244, 457), "03内蒙古碎片": (623, 203),
"04青海碎片": (383, 396), "05四川碎片": (491, 521), "06黑龙江碎片": (861, 122),
"07甘肃碎片": (472, 358), "08云南碎片": (457, 621), "09广西碎片": (580, 656),
"10湖南碎片": (640, 584), "11陕西碎片": (586, 415), "12河北碎片": (718, 330),
"13吉林碎片": (859, 228), "14湖北碎片": (654, 506), "15广东碎片": (678, 677),
"16贵州碎片": (549, 593), "17河南碎片": (672, 446), "18江西碎片": (724, 584),
"19山东碎片": (758, 392), "20山西碎片": (650, 369), "21辽宁碎片": (805, 284),
"22安徽碎片": (740, 487), "23福建碎片": (766, 609), "24江苏碎片": (770, 462),
"25浙江碎片": (793, 538), "26重庆碎片": (573, 525), "27宁夏碎片": (548, 375),
"28台湾碎片": (824, 652), "29海南碎片": (610, 753), "30北京碎片": (715, 310),
"31天津碎片": (730, 326), "32上海碎片": (818, 495)} # 省份文件名称和对应的正确位置

View File

@ -9,22 +9,24 @@ a = {"01新疆碎片": (225, 231), "02西藏碎片": (244, 457), "03内蒙古碎
"22安徽碎片": (740, 487), "23福建碎片": (766, 609), "24江苏碎片": (770, 462), "22安徽碎片": (740, 487), "23福建碎片": (766, 609), "24江苏碎片": (770, 462),
"25浙江碎片": (793, 538), "26重庆碎片": (573, 525), "27宁夏碎片": (548, 375), "25浙江碎片": (793, 538), "26重庆碎片": (573, 525), "27宁夏碎片": (548, 375),
"28台湾碎片": (824, 652), "29海南碎片": (610, 753), "30北京碎片": (715, 310), "28台湾碎片": (824, 652), "29海南碎片": (610, 753), "30北京碎片": (715, 310),
"31天津碎片": (730, 326), "32上海碎片": (818, 495)} # 省份文件名称和对应的正确位置 "31天津碎片": (730, 326), "32上海碎片": (818, 495)} # 省份文件名称和对应的正确位置
#基类 pygame pgzero
# 导入库
import pgzrun import pgzrun
import os import os
import random import random # 导入操作系统和随机数库
# 设置窗口 # 设置窗口
WIDTH = 1000 # 窗口的宽度 WIDTH = 1000
HEIGHT = 800 # 窗口的高度 HEIGHT = 800
# 设置窗口标题 # 设置窗口标题
TITLE = '中国地图我来拼' TITLE = '中国地图我来拼'
# 初始值设置 # 初始值设置
selected_piece = None # 当前被选择的碎片 s_piece = None # 当前被选择的碎片
selected_name = None # 当前被选择的碎片名字 s_name = None # 当前被选择的碎片名字
count = 0 # 记录游戏中正确放置的碎片数量
time = 120 # 游戏限时
game_over = False # 标志游戏是否结束
# 加载碎片 # 加载碎片
pieces = [] pieces = []
@ -33,37 +35,65 @@ for filename in os.listdir('images'):
if filename[-6:] == '碎片.png': # 只处理以碎片.png结尾的文件 if filename[-6:] == '碎片.png': # 只处理以碎片.png结尾的文件
img = Actor(filename[:-4], (random.randint(0, WIDTH), random.randint(0, HEIGHT))) img = Actor(filename[:-4], (random.randint(0, WIDTH), random.randint(0, HEIGHT)))
pieces.append([img, filename[:-4], False]) # 添加所有碎片和碎片信息到列表 pieces.append([img, filename[:-4], False]) # 添加所有碎片和碎片信息到列表
# 加载游戏结束画面
win_img = Actor("成功", (WIDTH // 2, HEIGHT // 2))
lose_img = Actor("失败", (WIDTH // 2, HEIGHT // 2))
# 刷新屏幕 # 刷新屏幕
def draw(): def draw():
screen.blit('中国地图背景', (0, 0)) # 绘制背景 screen.blit('中国地图背景',(0,0)) # 绘制背景
# 绘制每个碎片 # 绘制每个碎片
for piece in pieces: for piece in pieces:
piece[0].draw() piece[0].draw()
# 显示剩余时间
screen.draw.text(f"Time: {time}", (40, 20), fontsize=40, color="white")
# 如果游戏结束,显示胜利或失败的图片
if time == 0:
lose_img.draw()
if count == len(a):
win_img.draw()
# 鼠标按下事件 # 鼠标按下事件
def on_mouse_down(pos): def on_mouse_down(pos):
global selected_piece, selected_name global s_piece, s_name
for piece, name, placed in pieces: if not game_over:
if piece.collidepoint(pos) and not placed: # 检查是否点击到未正确放置的碎片 for piece, name, placed in pieces:
selected_piece = piece # 记录选择的碎片 if piece.collidepoint(pos) and not placed: # 检查是否点击到未正确放置的碎片
selected_name = name # 记录碎片名称 s_piece = piece # 记录选择的碎片
break s_name = name # 记录碎片名称
break
# 鼠标移动事件 # 鼠标移动事件
def on_mouse_move(pos): def on_mouse_move(pos):
if selected_piece != None and 0 < pos[0] < 1000 and 0 < pos[1] < 800: # 如果有选中碎片且鼠标未移出窗口,就随着鼠标移动 if s_piece != None and 0<pos[0]<1000 and 0<pos[1]<800: # 如果有选中碎片且鼠标未移出窗口,就随着鼠标移动
selected_piece.pos = pos # 更新选中碎片的位置 s_piece.pos = pos # 更新选中碎片的位置
# 鼠标释放事件 # 鼠标释放事件
def on_mouse_up(pos): def on_mouse_up(pos):
# <检测碎片是否正确放置> global count, s_piece
pass if s_piece != None: # 如果有选中的碎片
c_pos = a[s_name] # 选中碎片的正确位置
# 检查当前位置与正确位置的距离
if abs(pos[0] - c_pos[0]) < 50 and abs(pos[1] - c_pos[1]) < 50:
s_piece.pos = c_pos # 放置到正确位置
for piece in pieces:
if piece[0] == s_piece and not piece[2]:
piece[2] = True # 标记为已正确放置
count += 1 # 正确放置的计数器加一
break
s_piece = None # 清除当前选中的碎片
# 更新剩余时间
def update_time():
global time, game_over
if time > 0:
if count < len(a):
time -= 1 # 每秒减少一个时间单位
else:
game_over = True # 游戏结束
# 每隔 1 秒调用更新时间的函数
clock.schedule_interval(update_time, 1)
# 启动游戏 # 启动游戏
pgzrun.go() pgzrun.go()

View File

@ -0,0 +1,127 @@
import pgzrun
import random
import time
import pygame
WIDTH = 1000
HEIGHT = 725
TOTAL_TIME = 30 # 初始倒计时
# 载入中文字体Windows 推荐使用 SimHei 或 Microsoft YaHei
try:
FONT = pygame.font.SysFont("SimHei", 80) # 黑体
SMALL_FONT = pygame.font.SysFont("SimHei", 50)
except:
FONT = pygame.font.Font(None, 80)
SMALL_FONT = pygame.font.Font(None, 50)
def draw():
screen.fill((30, 30, 30))
if win:
text1 = FONT.render("游戏成功!", True, (255, 255, 0))
text2 = SMALL_FONT.render(f"用时:{TOTAL_TIME - remain_time:.1f}s", True, (255, 255, 255))
screen.surface.blit(text1, text1.get_rect(center=(WIDTH/2, HEIGHT/2 - 50)))
screen.surface.blit(text2, text2.get_rect(center=(WIDTH/2, HEIGHT/2 + 50)))
elif lose:
text1 = FONT.render("游戏失败!", True, (255, 0, 0))
text2 = SMALL_FONT.render("时间到", True, (255, 255, 255))
screen.surface.blit(text1, text1.get_rect(center=(WIDTH/2, HEIGHT/2 - 50)))
screen.surface.blit(text2, text2.get_rect(center=(WIDTH/2, HEIGHT/2 + 50)))
else:
t = SMALL_FONT.render(f"{max(0, remain_time):.1f}s", True, (255, 255, 0))
screen.surface.blit(t, t.get_rect(center=(900, 662)))
for grid in grids:
screen.draw.filled_rect(Rect(grid['pos'], (side, side)), grid['color'])
for fx in effects:
fx_font = pygame.font.SysFont("SimHei", int(fx['size']))
txt = fx_font.render(fx['text'], True, pygame.Color(fx['color']))
screen.surface.blit(txt, txt.get_rect(center=fx['pos']))
def update():
global remain_time, lose, effects
remain_time = TOTAL_TIME - (time.time() - start_t)
if remain_time <= 0 and not win:
lose = True
remain_time = 0
new_fx = []
for fx in effects:
x, y = fx['pos']
fx['pos'] = (x, y - 1.5)
fx['life'] -= 0.05
fx['size'] += 0.5
if fx['life'] > 0:
new_fx.append(fx)
effects = new_fx
def base_color():
return (random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255))
def dif_color(base):
r = min(base[0] + random.randint(8, 12), 255)
g = min(base[1] + random.randint(8, 12), 255)
b = min(base[2] + random.randint(8, 12), 255)
return (r, g, b)
def game():
global grids, side, dc
side = 700 // n * 0.9
space = 700 // n * 0.1
grids = []
bc = base_color()
dc = dif_color(bc)
i = 1
dif_index = random.randint(1, n * n)
for row in range(n):
for col in range(n):
info = {
'pos': (space + col * (side + space),
space + row * (side + space)),
'color': dc if i == dif_index else bc
}
grids.append(info)
i += 1
def on_mouse_down(pos):
global n, win, start_t, effects
if win or lose:
return
for grid in grids:
grid_rect = Rect(grid['pos'], (side, side))
if grid_rect.collidepoint(pos):
if grid['color'] == dc:
start_t += 10
effects.append({
'text': '+10s',
'pos': pos,
'size': 60,
'color': 'green',
'life': 1.0
})
if n > 10:
win = True
else:
n += 1
game()
else:
start_t -= 3
effects.append({
'text': '-3s',
'pos': pos,
'size': 60,
'color': 'red',
'life': 1.0
})
break
# 初始化
n = 3
win = False
lose = False
start_t = time.time()
remain_time = TOTAL_TIME
effects = []
game()
pgzrun.go()

View File

@ -31,9 +31,9 @@ def dif_color(base):
参数 参数
base表示基础颜色的元组格式为r,g,b base表示基础颜色的元组格式为r,g,b
""" """
r = min(base[0] + random.randint(20, 30), 255) r = min(base[0] + random.randint(5, 7), 255)
g = min(base[1] + random.randint(20, 30), 255) g = min(base[1] + random.randint(5, 7), 255)
b = min(base[2] + random.randint(20, 30), 255) b = min(base[2] + random.randint(5, 7), 255)
return (r, g, b) return (r, g, b)
def game(): def game():
@ -71,12 +71,10 @@ def on_mouse_down(pos):
else: else:
n += 1 # 进入下一关 n += 1 # 进入下一关
game() game()
# 初始化游戏参数 # 初始化游戏参数
n = 3 # 初始为3×3的矩阵 n = 3 # 初始为3×3的矩阵
win = False # 记录游戏成功状态 win = False # 记录游戏成功状态
start_t = time.time() # 记录开始时间 start_t = time.time() # 记录开始时间
game() game()
# 启动游戏 # 启动游戏
pgzrun.go() pgzrun.go()

View File

@ -0,0 +1,315 @@
import math
import pygame
from pgzero.actor import Actor, POS_TOPLEFT, ANCHOR_CENTER, transform_anchor
from pgzero import game, loaders
import types
import sys
import time
mod = sys.modules['__main__']
_fullscreen = False
def set_fullscreen():
global _fullscreen
mod.screen.surface = pygame.display.set_mode((mod.WIDTH, mod.HEIGHT), pygame.FULLSCREEN)
_fullscreen = True
def set_windowed():
global _fullscreen
mod.screen.surface = pygame.display.set_mode((mod.WIDTH, mod.HEIGHT))
_fullscreen = False
def toggle_fullscreen():
if _fullscreen:
set_windowed()
else:
set_fullscreen()
def hide_mouse():
pygame.mouse.set_visible(False)
def show_mouse():
pygame.mouse.set_visible(True)
class Actor(Actor):
def __init__(self, image, pos=POS_TOPLEFT, anchor=ANCHOR_CENTER, **kwargs):
self._flip_x = False
self._flip_y = False
self._scale = 1
self._mask = None
self._animate_counter = 0
self.fps = 5
self.direction = 0
super().__init__(image, pos, anchor, **kwargs)
def distance_to(self, actor):
dx = actor.x - self.x
dy = actor.y - self.y
return math.sqrt(dx**2 + dy**2)
def direction_to(self, actor):
dx = actor.x - self.x
dy = self.y - actor.y
angle = math.degrees(math.atan2(dy, dx))
if angle > 0:
return angle
return 360 + angle
def move_towards(self, actor, dist):
angle = math.radians(self.direction_to(actor))
dx = dist * math.cos(angle)
dy = dist * math.sin(angle)
self.x += dx
self.y -= dy
def point_towards(self, actor):
print(self.direction_to(actor))
self.angle = self.direction_to(actor)
def move_in_direction(self, dist):
angle = math.radians(self.direction)
dx = dist * math.cos(angle)
dy = dist * math.sin(angle)
self.x += dx
self.y -= dy
def move_forward(self, dist):
angle = math.radians(self.angle)
dx = dist * math.cos(angle)
dy = dist * math.sin(angle)
self.x += dx
self.y -= dy
def move_left(self, dist):
angle = math.radians(self.angle + 90)
dx = dist * math.cos(angle)
dy = dist * math.sin(angle)
self.x += dx
self.y -= dy
def move_right(self, dist):
angle = math.radians(self.angle - 90)
dx = dist * math.cos(angle)
dy = dist * math.sin(angle)
self.x += dx
self.y -= dy
def move_back(self, dist):
angle = math.radians(self.angle)
dx = -dist * math.cos(angle)
dy = -dist * math.sin(angle)
self.x += dx
self.y -= dy
@property
def images(self):
return self._images
@images.setter
def images(self, images):
self._images = images
if len(self._images) != 0:
self.image = self._images[0]
def next_image(self):
if self.image in self._images:
current = self._images.index(self.image)
if current == len(self._images) - 1:
self.image = self._images[0]
else:
self.image = self._images[current + 1]
else:
self.image = self._images[0]
def animate(self):
now = int(time.time() * self.fps)
if now != self._animate_counter:
self._animate_counter = now
self.next_image()
@property
def angle(self):
return self._angle
@angle.setter
def angle(self, angle):
self._angle = angle
self._transform_surf()
@property
def scale(self):
return self._scale
@scale.setter
def scale(self, scale):
self._scale = scale
self._transform_surf()
@property
def flip_x(self):
return self._flip_x
@flip_x.setter
def flip_x(self, flip_x):
self._flip_x = flip_x
self._transform_surf()
@property
def flip_y(self):
return self._flip_y
@flip_y.setter
def flip_y(self, flip_y):
self._flip_y = flip_y
self._transform_surf()
@property
def image(self):
return self._image_name
@image.setter
def image(self, image):
self._image_name = image
self._orig_surf = self._surf = loaders.images.load(image)
self._update_pos()
self._transform_surf()
def _transform_surf(self):
self._surf = self._orig_surf
p = self.pos
if self._scale != 1:
size = self._orig_surf.get_size()
self._surf = pygame.transform.scale(self._surf, (int(size[0] * self.scale), int(size[1] * self.scale)))
if self._flip_x:
self._surf = pygame.transform.flip(self._surf, True, False)
if self._flip_y:
self._surf = pygame.transform.flip(self._surf, False, True)
self._surf = pygame.transform.rotate(self._surf, self._angle)
self.width, self.height = self._surf.get_size()
w, h = self._orig_surf.get_size()
ax, ay = self._untransformed_anchor
anchor = transform_anchor(ax, ay, w, h, self._angle)
self._anchor = (anchor[0] * self.scale, anchor[1] * self.scale)
self.pos = p
self._mask = None
def collidepoint_pixel(self, x, y=0):
if isinstance(x, tuple):
y = x[1]
x = x[0]
if self._mask == None:
self._mask = pygame.mask.from_surface(self._surf)
xoffset = int(x - self.left)
yoffset = int(y - self.top)
if xoffset < 0 or yoffset < 0:
return 0
width, height = self._mask.get_size()
if xoffset > width or yoffset > height:
return 0
return self._mask.get_at((xoffset, yoffset))
def collide_pixel(self, actor):
for a in [self, actor]:
if a._mask == None:
a._mask = pygame.mask.from_surface(a._surf)
xoffset = int(actor.left - self.left)
yoffset = int(actor.top - self.top)
return self._mask.overlap(actor._mask, (xoffset, yoffset))
def collidelist_pixel(self, actors):
for i in range(len(actors)):
if self.collide_pixel(actors[i]):
return i
return -1
def collidelistall_pixel(self, actors):
collided = []
for i in range(len(actors)):
if self.collide_pixel(actors[i]):
collided.append(i)
return collided
def obb_collidepoints(self, actors):
angle = math.radians(self._angle)
costheta = math.cos(angle)
sintheta = math.sin(angle)
width, height = self._orig_surf.get_size()
half_width = width / 2
half_height = height / 2
i = 0
for actor in actors:
tx = actor.x - self.x
ty = actor.y - self.y
rx = tx * costheta - ty * sintheta
ry = ty * costheta + tx * sintheta
if rx > -half_width and rx < half_width and ry > -half_height and ry < half_height:
return i
i += 1
return -1
def obb_collidepoint(self, x, y=0):
if isinstance(x, tuple):
y = x[1]
x = x[0]
angle = math.radians(self._angle)
costheta = math.cos(angle)
sintheta = math.sin(angle)
width, height = self._orig_surf.get_size()
half_width = width / 2
half_height = height / 2
tx = x - self.x
ty = y - self.y
rx = tx * costheta - ty * sintheta
ry = ty * costheta + tx * sintheta
if rx > -half_width and rx < half_width and ry > -half_height and ry < half_height:
return True
return False
def circle_collidepoints(self, radius, actors):
rSquare = radius ** 2
i = 0
for actor in actors:
dSquare = (actor.x - self.x)**2 + (actor.y - self.y)**2
if dSquare < rSquare:
return i
i += 1
return -1
def circle_collidepoint(self, radius, x, y=0):
if isinstance(x, tuple):
y = x[1]
x = x[0]
rSquare = radius ** 2
dSquare = (x - self.x)**2 + (y - self.y)**2
if dSquare < rSquare:
return True
return False
def draw(self):
game.screen.blit(self._surf, self.topleft)
def get_rect(self):
return self._rect

View File

@ -0,0 +1,267 @@
import pgzrun
import random
import itertools
from pgzhelper import *
WIDTH = 1500
HEIGHT = 800
# 背景
bg1 = Actor('连续道路', topleft=(0, 0))
bg2 = Actor('连续道路', topleft=(1500, 0))
# 摩托车
motor = Actor('摩托1', (200, HEIGHT // 2))
motor.images = ['摩托1', '摩托2']
# 障碍物
obstacles = [Actor('路障', (random.randint(WIDTH, WIDTH + 1200),
random.randint(150, HEIGHT - 150))) for _ in range(2)]
# 终点线
line = Actor('终点线', topleft=(30000, 163))
# 钻石
zuan = Actor('钻石', topleft=(WIDTH + 100, random.randint(150, HEIGHT - 150)))
# 游戏参数
speed = 10
flag = 0 # 0=进行中,1=胜利,2=失败
score = 0
combo = 0
particles = []
blink = itertools.cycle([255, 180, 120, 180])
# 加速状态
boost_active = False
boost_time = 0
BOOST_DURATION = 5 # 秒
BOOST_SPEED = 20
BOOST_CD = 8 # 技能冷却时间
boost_cd_timer = 0 # CD倒计时
# 游戏计时
game_time = 0
# 加速提示圆圈参数
boost_circle_radius = 50
boost_circle_pos = (WIDTH - 100, HEIGHT - 100)
# 绘制函数
def draw():
bg1.draw()
bg2.draw()
if flag == 1 or flag == 2:
music.stop()
color = (255, next(blink), next(blink))
result_text = '胜利!' if flag == 1 else '失败!'
screen.draw.text(result_text, center=(WIDTH / 2, HEIGHT / 2 - 100),
fontsize=120, color=color, fontname='simkai')
# 显示用时和钻石数量
screen.draw.text(f'用时:{game_time:.2f}s', center=(WIDTH / 2, HEIGHT / 2 + 50),
fontsize=60, color='white', fontname='simkai')
screen.draw.text(f'收集钻石:{score}', center=(WIDTH / 2, HEIGHT / 2 + 120),
fontsize=60, color='yellow', fontname='simkai')
# 显示总评分
total_score = calculate_score()
screen.draw.text(f'最终评分:{total_score}', center=(WIDTH / 2, HEIGHT / 2 + 190),
fontsize=60, color='orange', fontname='simkai')
else:
for obs in obstacles:
obs.draw()
draw_particles()
motor.draw()
line.draw()
zuan.draw()
# draw_collision_rect() # 可选调试
x = int(line.x - motor.x)
screen.draw.text(f'距终点:{x} m', (50, 700),
fontname='simkai', fontsize=50)
screen.draw.text(f'钻石:{score}', (50, 100),
fontname='simkai', fontsize=50)
screen.draw.text(f'速度:{int(speed)}', (1300, 50),
fontname='simkai', fontsize=40, color='yellow')
# 绘制加速技能圆圈
draw_boost_circle()
def draw_particles():
for p in particles:
screen.draw.filled_circle((p['x'], p['y']), 4,
(255, 255, 150, int(p['alpha'])))
def update_particles():
for p in particles:
p['x'] -= 8
p['alpha'] -= 4
particles[:] = [p for p in particles if p['alpha'] > 0]
# 背景与障碍物移动
def bg_move():
bg1.x -= speed
bg2.x -= speed
if bg1.right <= 0:
bg1.left = bg2.right
if bg2.right <= 0:
bg2.left = bg1.right
line.x -= speed
zuan.x -= speed
if zuan.x <= 0:
zuan.x = random.randint(WIDTH, WIDTH + 1200)
zuan.y = random.randint(150, HEIGHT - 150)
def obs_move():
for obs in obstacles:
obs.x -= speed
if obs.x < -100:
obs.x = random.randint(WIDTH, WIDTH + 1200)
obs.y = random.randint(150, HEIGHT - 150)
def line_collide():
global flag
if motor.collide_pixel(line):
flag = 1
else:
motor.x += 10
def obs_collide():
global flag
mx, my = motor.x, motor.y
mw, mh = motor.width, motor.height
rect_width = int(mw * 0.3)
rect_height = int(mh * 0.2)
left = int(mx - rect_width / 2)
top = int(my + mh / 2 - rect_height)
bottom_center_rect = Rect((left, top), (rect_width, rect_height))
for obs in obstacles:
obs_rect = Rect((obs.left, obs.top), (obs.width, obs.height))
if bottom_center_rect.colliderect(obs_rect):
flag = 2
def zuan_collide():
global score, combo
if motor.collide_pixel(zuan):
combo += 1
score += combo
zuan.x = random.randint(WIDTH, WIDTH + 1200)
zuan.y = random.randint(150, HEIGHT - 150)
else:
combo = max(combo - 0.03, 0)
def draw_collision_rect():
# 调试用,可注释
mx, my = motor.x, motor.y
mw, mh = motor.width, motor.height
rect_width = int(mw * 0.3)
rect_height = int(mh * 0.2)
left = int(mx - rect_width / 2)
top = int(my + mh / 2 - rect_height)
collision_rect = Rect((left, top), (rect_width, rect_height))
screen.draw.rect(collision_rect, (255, 0, 0))
def draw_boost_circle():
# 圆圈颜色随CD变化
if boost_cd_timer > 0:
color = (100, 100, 100)
elif boost_active:
color = (0, 255, 255)
else:
color = (0, 200, 255)
screen.draw.circle(boost_circle_pos, boost_circle_radius, color)
screen.draw.text("SHIFT", center=boost_circle_pos, color="white", fontsize=30)
# 评分计算
def calculate_score():
"""根据用时、钻石和胜利情况计算最终得分"""
global score, game_time, flag
base_score = 0
# 胜利奖励
if flag == 1:
base_score += 500
# 钻石加分每颗钻石10分
base_score += score * 10
# 时间加分,时间越短,额外加分
time_score = max(0, int(1000 - game_time * 10))
base_score += time_score
return base_score
def update():
global speed, boost_active, boost_time, boost_cd_timer, game_time
if flag == 0:
# 计时
game_time += 1 / 60
# 更新技能CD
if boost_cd_timer > 0 and not boost_active:
boost_cd_timer -= 1 / 60
if boost_cd_timer < 0:
boost_cd_timer = 0
# Shift 加速
if (keyboard['lshift'] or keyboard['rshift']) and not boost_active and boost_cd_timer == 0:
boost_active = True
boost_time = BOOST_DURATION
speed = BOOST_SPEED
if boost_active:
boost_time -= 1 / 60
if boost_time <= 0:
boost_active = False
speed = 10
boost_cd_timer = BOOST_CD # 启动冷却
# 正常加减速
if not boost_active:
if keyboard.right:
speed = min(speed + 0.2, 18)
elif keyboard.left:
speed = max(speed - 0.2, 6)
# 背景移动
if line.x > WIDTH - 200:
bg_move()
obs_move()
else:
line_collide()
# 上下移动
if keyboard.up and motor.top > 0:
motor.y -= 6
if keyboard.down and motor.bottom < HEIGHT - 50:
motor.y += 6
# 碰撞检测
obs_collide()
zuan_collide()
# 尾焰粒子
particles.append({'x': motor.x - 40, 'y': motor.y + 10, 'alpha': 255})
update_particles()
def change_image():
motor.next_image()
clock.schedule_interval(change_image, 0.3)
music.play('速度与激情')
pgzrun.go()

View File

@ -1,17 +1,27 @@
import pgzrun import pgzrun
#pygame zero模块 python game zero
import random import random
from pgzhelper import * from pgzhelper import *
#pygame zero模块 辅助模块
# 游戏窗口大小 # images 图片文件夹
# sounds 音效文件夹
# music 音乐文件夹
# fonts 字体文件夹
#参数名称不可变
WIDTH = 1500 WIDTH = 1500
HEIGHT = 800 HEIGHT = 800
# 创建背景角色 #Actor 面向对象编程思想
bg1 = Actor('连续道路', topleft = (0,0)) #角色类 基类
bg2 = Actor('连续道路', topleft = (1500,0)) #创建背景角色
#创建了两个背景角色
bg1 = Actor('连续道路.png', topleft = (0,0))
bg2 = Actor('连续道路.png', topleft = (1500,0))
# 创建障碍物 # 创建障碍物
obstacles = [] obstacles = [] #障碍物列表
for _ in range(3): # 障碍物数量 for _ in range(3): # 障碍物数量
x = random.randint(WIDTH, WIDTH + 1500) # 障碍物出现在屏幕右侧外 x = random.randint(WIDTH, WIDTH + 1500) # 障碍物出现在屏幕右侧外
y = random.randint(100, HEIGHT - 50) y = random.randint(100, HEIGHT - 50)
@ -26,9 +36,10 @@ motor.images = ['摩托1','摩托2']
line = Actor('终点线', topleft = (20000, 163)) line = Actor('终点线', topleft = (20000, 163))
# 初始化设置 # 初始化设置
speed = 15 speed = 15 #速度
flag = 0 flag = 0 #0为正常行驶1为成功2为失败
# 绘制
def draw(): def draw():
# 绘制连续背景 # 绘制连续背景
bg1.draw() bg1.draw()

View File

@ -1,9 +0,0 @@
```bash
#阿里云镜像源
pip install -i https://mirrors.aliyun.com/pypi/simple/ opencv-python mediapipe
```
```bash
#阿里云镜像源
pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt
```

View File

@ -1,76 +0,0 @@
import cv2 # 导入 OpenCV用于视频捕获和图像处理
import mediapipe as mp # 导入 MediaPipe用于手部关键点检测
# =================== 初始化 MediaPipe 模块 ===================
# 获取 MediaPipe 手部识别模块
mp_hands = mp.solutions.hands
# 创建 Hands 对象,用于处理图像中的手部关键点
# 默认参数含义:
# - static_image_mode=False处理连续视频流非静态图片
# - max_num_hands=2最多检测2只手
# - min_detection_confidence=0.5置信度低于0.5的检测结果将被忽略
hands = mp_hands.Hands()
# 获取用于绘制关键点和连接线的工具
mp_draw = mp.solutions.drawing_utils
# =================== 初始化摄像头 ===================
# 打开默认摄像头设备编号为0
cap = cv2.VideoCapture(0)
# =================== 主循环,逐帧处理 ===================
while True:
# 捕获一帧图像
ret, frame = cap.read()
# 如果捕获失败,退出循环
if not ret:
break
# 水平翻转图像(摄像头默认是镜像视角,翻转后更自然)
frame = cv2.flip(frame, 1)
# OpenCV 默认使用 BGR 格式,而 MediaPipe 要求输入 RGB 格式图像
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 使用 MediaPipe 的 hand 模块处理当前帧,检测手部关键点
results = hands.process(rgb_frame)
# =================== 处理检测结果 ===================
# 如果检测到了手部
if results.multi_hand_landmarks:
# 遍历每只手
for landmarks in results.multi_hand_landmarks:
# 在原图上绘制21个关键点及其连接骨骼HAND_CONNECTIONS
mp_draw.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
# 提取21个关键点的 Landmark 对象(含 x, y, z 三维坐标)
landmark_points = [landmarks.landmark[i] for i in range(21)]
# =================== 手势识别部分(可拓展) ===================
# 例如:判断是否为“比赞”手势(此处未实现具体函数)
# if detect_thumb_up(landmark_points):
# cv2.putText(frame, "Thumb Up!", (50, 100),
# cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# =================== 显示图像 ===================
# 显示处理后的视频帧(窗口标题为 Hand Gesture Detection
cv2.imshow('Hand Gesture Detection', frame)
# 等待键盘事件,如果按下 'q' 键则退出程序
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# =================== 清理资源 ===================
# 释放摄像头
cap.release()
# 销毁所有 OpenCV 窗口
cv2.destroyAllWindows()

View File

@ -1,154 +0,0 @@
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 = {} # 存放每个按钮控件的字典 {(r, c): Button}
self.board = [[0 for _ in range(cols)] for _ in range(rows)] # 雷区,-1 表示地雷,其他为周围雷的数量
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
# 更新周围8格的数字
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):
# 每个格子是一个 Button绑定左键点击翻格子、右键标旗
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("<Button-3>", 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()
# 类似于 BFS 的空白格子展开
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:
# 如果是 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*4 >= rows * cols or rows >= 20 or cols >= 20:
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, "20") # 默认行数
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()

View File

@ -0,0 +1,96 @@
import pgzrun
import random
from pgzhelper import *
# 游戏常量配置
WIDTH = 400 # 游戏窗口宽度
HEIGHT = 700 # 游戏窗口高度
TITLE = '逃离地心引力' # 游戏窗口标题
speed = 5
gap = 120 # 平台垂直间距
g = 0.8 # 重力加速度
jump = -15 # 跳跃力(负值表示向上)
# 初始化游戏状态
def start():
global plats, p, score, game_over
p = Actor("player", (WIDTH // 2, HEIGHT - 70)) # 初始位置底部
plats = [] # 清空平台列表
p.vy = 0 # 垂直速度重置为0
score = 0 # 分数重置为0
game_over = False # 游戏状态重置为未结束
# 生成底部固定平台(屏幕底部中央)
base = Actor("platform", (WIDTH // 2, HEIGHT - 30))
plats.append(base)
# 生成初始5个平台从底部向上固定间距随机分布
for i in range(5):
x = random.randint(75, WIDTH - 75) # 平台水平位置随机
y = base.y - (gap * (i + 1)) # 平台垂直位置按间距计算
plats.append(Actor("platform", (x, y)))
def draw():
screen.blit("starbg.png", (0, 0)) # 绘制背景
# 绘制所有平台
for plat in plats:
plat.draw()
p.draw() # 绘制玩家
# 显示分数和结束提示
screen.draw.text(f"分数: {score}", (40, 50), color="white", fontname='simkai', fontsize=24)
if game_over:
screen.draw.text("游戏结束!按 R 重新开始", center=(WIDTH // 2, HEIGHT // 2 - 40),
color="white", fontname='simkai', fontsize=24, background="black")
def update():
global score, game_over
# 游戏结束状态处理按R键重新开始
if game_over:
if keyboard.r:
start()
# 玩家移动(左右键)
p.x += (keyboard.right - keyboard.left) * speed
# 边界限制(确保玩家不超出屏幕左右边界)
p.x = max(p.width // 2, min(p.x, WIDTH - p.width // 2))
# 物理系统:应用重力(垂直速度递增)
p.vy += g
p.y += p.vy
# 平台碰撞检测(仅当玩家下落时触发)
if p.vy > 0:
# 像素级碰撞检测(返回碰撞的平台索引,-1表示无碰撞
hit = p.collidelist_pixel(plats)
if hit != -1:
# 修正玩家位置到平台上方,避免穿模
p.bottom = plats[hit].y - 10
p.vy = jump # 跳跃
sounds.跳1.play() # 跳跃音效
# 屏幕滚动逻辑(玩家上升到中部时下移平台)
if p.y < HEIGHT // 2:
dis = HEIGHT // 2 - p.y # 计算需要滚动的距离
p.y += dis # 玩家回到屏幕中部
# 所有平台下移
for plat in plats:
plat.y += dis
# 生成新平台(当顶部平台进入屏幕时,在其上方生成新平台)
if plats[-1].y > 0:
x = random.randint(75, WIDTH - 75)
y = plats[-1].y - gap
new = Actor("platform", (x, y))
plats.append(new)
# 移除屏幕下方的平台(保留最小数量)
while len(plats) > 7: # 限制平台总数
plats.pop(0)
score += 10 # 得分
# 游戏结束条件(玩家掉落底部平台之外)
if p.y > HEIGHT + p.height:
game_over = True
start() # 初始化游戏
pgzrun.go() # 运行Pygame Zero游戏循环

View File

@ -1,71 +0,0 @@
"""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()

View File

@ -0,0 +1,46 @@
import tkinter as tk
import random
from PIL import Image, ImageTk
# 弹幕类
class MovingLabel:
def __init__(self, window, text):
self.text = text
self.label = tk.Label(window, image=kuang, text=self.text, compound="center", font=("黑体", 20), fg='white',width=190, height=45)
self.label.place(x=800, y=random.randint(50, 400))
self.x = 800 # 初始x坐标800
self.move()
def move(self):
if self.x > -200:
self.x -= 2
self.label.place(x=self.x) # 更新标签的x坐标实现向左移动
# 每隔20毫秒调用一次move方法实现连续移动效果
self.label.after(20, self.move)
else:
self.label.destroy() # 销毁移出窗口的标签
# 发送弹幕
def send():
text = e1.get() # 获取弹幕文本
ml = MovingLabel(window, text)
# 创建窗口
window = tk.Tk()
window.geometry('1000x670')
window.resizable(0, 0)
kuang = ImageTk.PhotoImage(file='kuang.png') # 创建可以在Tkinter中使用的图像对象
bg_image = Image.open("tv.png") # 导入背景图片
bg_image = ImageTk.PhotoImage(bg_image) # 转换为PhotoImage对象才可以在tkinter中显示图形
bg_label = tk.Label(window, image=bg_image) # 创建标签显示背景图
bg_label.pack() # 放置标签
# 弹幕输入框
e1 = tk.Entry(window, font=("黑体", 20))
e1.place(x=280, y=620)
# 发送按钮
b1 = tk.Button(window, text="发送弹幕", font=("黑体", 20), command=send)
b1.place(x=580, y=613)
window.mainloop()

View File

@ -1,4 +1,5 @@
import turtle import turtle
from tkinter import messagebox
# 设置屏幕和画笔 # 设置屏幕和画笔
screen = turtle.Screen() screen = turtle.Screen()
@ -8,36 +9,49 @@ pen.hideturtle()
pen.speed(0) pen.speed(0)
pen.pensize(3) pen.pensize(3)
# 当前玩家X 先手)
current_player = "X" current_player = "X"
# 保存每格的内容3x3
board = [["" for _ in range(3)] for _ in range(3)] board = [["" for _ in range(3)] for _ in range(3)]
# 画井字棋的格子
def draw_board(): def draw_board():
for i in range(0, 3): # 画2条竖线 for i in range(1, 3): # 竖线
pen.penup() pen.penup()
pen.goto(-100 + i * 100, -150) pen.goto(-150 + i * 100, -150)
pen.pendown() pen.pendown()
pen.goto(-100 + i * 100, 150) pen.goto(-150 + i * 100, 150)
for i in range(0, 3): # 画2条横线 for i in range(1, 3): # 横线
pen.penup() pen.penup()
pen.goto(-150, -100 + i * 100) pen.goto(-150, -150 + i * 100)
pen.pendown() pen.pendown()
pen.goto(150, -100 + i * 100) pen.goto(150, -150 + i * 100)
# 在某个格子画 X 或 O
def draw_symbol(row, col, symbol): def draw_symbol(row, col, symbol):
x = -150 + col * 100 + 50 x = -100 + col * 100
y = 150 - row * 100 - 90 y = 70 - row * 100
pen.penup() pen.penup()
pen.goto(x, y) pen.goto(x, y)
pen.write(symbol, align="center", font=("Arial", 36, "bold")) pen.write(symbol, align="center", font=("Arial", 36, "bold"))
# 处理点击事件 def check_win(player):
# 检查行
for r in range(3):
if all(board[r][c] == player for c in range(3)):
return True
# 检查列
for c in range(3):
if all(board[r][c] == player for r in range(3)):
return True
# 检查对角线
if all(board[i][i] == player for i in range(3)):
return True
if all(board[i][2 - i] == player for i in range(3)):
return True
return False
def check_draw():
return all(board[r][c] != "" for r in range(3) for c in range(3))
def click(x, y): def click(x, y):
global current_player global current_player
# 将坐标转换为格子位置
if not (-150 < x < 150 and -150 < y < 150): if not (-150 < x < 150 and -150 < y < 150):
return return
col = int((x + 150) // 100) col = int((x + 150) // 100)
@ -45,10 +59,18 @@ def click(x, y):
if board[row][col] == "": if board[row][col] == "":
board[row][col] = current_player board[row][col] = current_player
draw_symbol(row, col, current_player) draw_symbol(row, col, current_player)
# 切换玩家
if check_win(current_player):
messagebox.showinfo("游戏结束", f"玩家 {current_player} 获胜!")
screen.bye() # 关闭窗口
return
elif check_draw():
messagebox.showinfo("游戏结束", "平局!")
screen.bye()
return
current_player = "O" if current_player == "X" else "X" current_player = "O" if current_player == "X" else "X"
# 运行程序
draw_board() draw_board()
screen.onclick(click) screen.onclick(click)
screen.mainloop() screen.mainloop()