diff --git a/main.py b/main.py new file mode 100644 index 0000000..f8b4fbc --- /dev/null +++ b/main.py @@ -0,0 +1,409 @@ +import sys +import random +import pygame +import pandas as pd + + +# 学生类,表示学生的信息 +class Student: + def __init__(self, id: str, count=0, score=0, consecutive_correct=0): + self.id = id + self.count = count + self.score = score + self.consecutive_correct = consecutive_correct # 连续正确次数 + + def __repr__(self): + return f"{self.id}, Count: {self.count}, Score: {self.score}, Consecutive Correct: {self.consecutive_correct}" + + +class RandomNamePicker: + def __init__(self): + pygame.init() + self.screen_width = 1238 + self.screen_height = 720 + self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) + pygame.display.set_caption("随机点名程序") + + self.background = pygame.image.load("./assets/01.png").convert() + self.background = pygame.transform.scale(self.background, (self.screen_width, self.screen_height)) + self.previous_background = None # 用于保存之前的背景图 + + self.students = self.load_students() + self.current_student = None + + self.state = "initial" + self.first_pick = True + self.is_first_pick = True + + # 设置按钮(圆形按钮) + self.settings_button = ("设置", pygame.Rect(20, 20, 50, 50), self.open_settings) + # 帮助按钮(圆形按钮) + self.help_button = ("帮助", pygame.Rect(self.screen_width - 120, 20, 50, 50), self.open_help) + # 关闭设置和帮助界面按钮 + self.close_settings_button = ("X", pygame.Rect(self.screen_width - 60, 20, 40, 40), self.close_settings) + + # 音乐和音效的状态 + self.music_on = True + self.sound_on = True + + # 音乐控制 + self.music_files = [f"./assets/{i}.mp3" for i in range(1, 10)] + pygame.mixer.init() + self.play_music() + + # 加载按钮音效 + self.button_sound = pygame.mixer.Sound("./assets/按钮音效.mp3") + + # 设置界面按钮 + self.settings_menu_buttons = [ + ("关闭音乐", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 90, 200, 50), + self.toggle_music), + ("关闭音效", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 30, 200, 50), + self.toggle_sound) + ] + + # 初始按钮 + self.initial_buttons = [ + ("开始点名", pygame.Rect(self.screen_width // 2 - 100, 50, 200, 50), self.start_picking), + ("退出点名", pygame.Rect(self.screen_width // 2 - 100, 120, 200, 50), sys.exit) + ] + + self.confirm_buttons = [ + ("到", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 90, 200, 50), + self.student_present), + ("没到", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 30, 200, 50), + self.student_absent) + ] + + self.accuracy_buttons = [ + ("能准确重复", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 90, 200, 50), + self.accurate_repeat), + ("不能重复", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 30, 200, 50), + self.inaccurate_repeat) + ] + + self.picking_buttons = [ + ("完全正确", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 120, 200, 50), + lambda: self.update_score(3, "完全正确")), + ("部分正确", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 - 60, 200, 50), + lambda: self.update_score(1.5, "部分正确")), + ("勉强正确", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2, 200, 50), + lambda: self.update_score(0.5, "勉强正确")), + ("错误", pygame.Rect(self.screen_width // 2 - 100, self.screen_height // 2 + 60, 200, 50), + lambda: self.update_score(-1, "错误")) + ] + + self.font = pygame.font.Font("C:\Windows\Fonts\simhei.ttf", 20) + self.result_label = "" + self.message_label = "" + self.pick_message = "" + + self.temp_result_label = "" + self.temp_message_label = "" + + def play_music(self): + if self.music_on: + self.current_music_index = random.randint(0, len(self.music_files) - 1) + pygame.mixer.music.load(self.music_files[self.current_music_index]) + pygame.mixer.music.set_volume(0.1) + pygame.mixer.music.play(-1) + + def toggle_music(self): + if self.music_on: + pygame.mixer.music.stop() + self.music_on = False + self.settings_menu_buttons[0] = ("打开音乐", self.settings_menu_buttons[0][1], self.toggle_music) + else: + self.play_music() + self.music_on = True + self.settings_menu_buttons[0] = ("关闭音乐", self.settings_menu_buttons[0][1], self.toggle_music) + + def toggle_sound(self): + if self.sound_on: + self.sound_on = False + self.settings_menu_buttons[1] = ("打开音效", self.settings_menu_buttons[1][1], self.toggle_sound) + else: + self.sound_on = True + self.settings_menu_buttons[1] = ("关闭音效", self.settings_menu_buttons[1][1], self.toggle_sound) + + def load_students(self): + students = [] + try: + df = pd.read_excel("./assets/1.xlsx") + for _, row in df.iterrows(): + id = str(row['id']) + count = int(row['count']) + score = int(row['score']) + consecutive_correct = int(row['temp']) + students.append(Student(id, count, score, consecutive_correct)) + except FileNotFoundError: + self.result_label = "学生信息文件未找到!" + except pd.errors.EmptyDataError: + self.result_label = "Excel文件格式错误或文件为空!" + return students + + def start_picking(self): + if not self.students: + self.result_label = "没有学生信息!" + return + + if self.is_first_pick: + self.is_first_pick = False + button_text = "继续点名" + self.initial_buttons[0] = (button_text, self.initial_buttons[0][1], self.start_picking) + + self.current_student = self.weighted_random_pick() + + if self.current_student: + self.result_label = f"(被点名: {self.current_student.id})" + else: + self.result_label = "点名失败,没有可选学生。" + + self.message_label = "" + self.state = "confirm" + self.background = pygame.image.load("./assets/01.1.png").convert() + self.background = pygame.transform.scale(self.background, (self.screen_width, self.screen_height)) + + def weighted_random_pick(self): + max_score = max(student.score for student in self.students) + weights = [max_score - student.score + 1 for student in self.students] + + total_weight = sum(weights) + pick = random.uniform(0, total_weight) + current = 0 + + for student, weight in zip(self.students, weights): + current += weight + if current >= pick: + return student + + def student_present(self): + if self.current_student: + self.current_student.score += 1 + self.result_label = f"({self.current_student.id} 到场,奖励一分!新分数:{self.current_student.score})" + self.save_students() + + self.pick_message = "啊,点到我了!" + self.background = pygame.image.load("./assets/02.png").convert() + self.background = pygame.transform.scale(self.background, (self.screen_width, self.screen_height)) + self.state = "accuracy_check" + + def accurate_repeat(self): + if self.current_student: + self.current_student.score += 0.5 + self.result_label = f"({self.current_student.id} 能准确重复,奖励0.5分!新分数:{self.current_student.score})" + self.save_students() + + self.state = "picking" + + def inaccurate_repeat(self): + if self.current_student: + self.current_student.score -= 1 + self.result_label = f"({self.current_student.id} 不能重复,扣1分!新分数:{self.current_student.score})" + self.save_students() + + self.state = "picking" + + def student_absent(self): + self.state = "initial" + self.background = pygame.image.load("./assets/01.png").convert() + self.background = pygame.transform.scale(self.background, (self.screen_width, self.screen_height)) + + def update_score(self, delta, answer_type): + self.result_label = "" + self.message_label = "" + multiplier_message = "" + + if self.current_student.score % 4 == 0 and answer_type == "错误": + delta *= 2 + multiplier_message = ",触发双倍扣分!" + elif self.current_student.score % 6 == 0 and answer_type == "完全正确": + delta *= 2 + multiplier_message = ",触发双倍加分!" + + self.current_student.score += delta + self.current_student.count += 1 + + if answer_type == "完全正确": + self.current_student.consecutive_correct += 1 + else: + self.current_student.consecutive_correct = 0 + + if self.current_student.consecutive_correct == 7: + self.current_student.score = 50 + multiplier_message += " 连续7次完全正确,分数直升至50分。" + + if self.current_student.score >= 50: + multiplier_message += " 已达到满分50分,将不会再被点到。" + + self.result_label = f"({self.current_student.id} 的新分数 :{self.current_student.score},点名次数:{self.current_student.count}{multiplier_message})" + self.save_students() + + if answer_type == "部分正确": + self.message_label = "可惜,下次再加油吧•﹏•" + elif answer_type == "完全正确": + self.message_label = "yeah~答对咯!( ^ _ ^ )" + elif answer_type == "勉强正确": + self.message_label = "还行,勉强算正确吧。" + elif answer_type == "错误": + self.message_label = "啊啊啊啊——答错啦╥﹏╥..." + + if answer_type == "完全正确": + self.background = pygame.image.load("./assets/04对.png").convert() + elif answer_type == "部分正确" or answer_type == "勉强正确": + self.background = pygame.image.load("./assets/04中.png").convert() + elif answer_type == "错误": + self.background = pygame.image.load("./assets/04错.png").convert() + + self.background = pygame.transform.scale(self.background, (self.screen_width, self.screen_height)) + self.pick_message = "" + self.state = "initial" + + def save_students(self): + df = pd.DataFrame([{'id': student.id, 'count': student.count, 'score': student.score, + 'temp': student.consecutive_correct} for student in self.students]) + df.to_excel("./assets/1.xlsx", index=False) + + def open_settings(self): + self.temp_result_label = self.result_label + self.temp_message_label = self.message_label + self.result_label = "" + self.message_label = "" + self.previous_background = self.background # 保存之前的背景图 + self.state = "settings" + self.background = pygame.Surface((self.screen_width, self.screen_height)) + self.background.fill((169, 169, 169)) + + def close_settings(self): + self.result_label = self.temp_result_label + self.message_label = self.temp_message_label + self.state = "initial" + if self.previous_background: + self.background = self.previous_background # 恢复之前的背景图 + self.previous_background = None + + def open_help(self): + self.temp_result_label = self.result_label + self.temp_message_label = self.message_label + self.result_label = "" + self.message_label = "" + self.previous_background = self.background # 保存之前的背景图 + self.state = "help" + self.background = pygame.image.load("./assets/介绍.png").convert() + self.background = pygame.transform.scale(self.background, (self.screen_width, self.screen_height)) + + # 绘制圆形按钮 + def draw_circle_button(self, text, rect, color): + center = rect.center + radius = rect.width // 2 + pygame.draw.circle(self.screen, color, center, radius) + + label = self.font.render(text, True, (0, 0, 0)) + label_rect = label.get_rect(center=rect.center) + self.screen.blit(label, label_rect) + + # 绘制圆角矩形按钮 + def draw_rounded_rect_button(self, text, rect, color): + pygame.draw.rect(self.screen, color, rect, border_radius=20) + + label = self.font.render(text, True, (0, 0, 0)) + label_rect = label.get_rect(center=rect.center) + self.screen.blit(label, label_rect) + + def draw_buttons(self): + mouse_pos = pygame.mouse.get_pos() + + if self.state == "initial": + buttons = self.initial_buttons + [self.settings_button, self.help_button] + elif self.state == "confirm": + buttons = self.confirm_buttons + elif self.state == "accuracy_check": + buttons = self.accuracy_buttons + elif self.state == "settings": # 设置界面显示 X 按钮和设置选项 + buttons = self.settings_menu_buttons + [self.close_settings_button] + elif self.state == "help": # 帮助界面只显示 X 按钮 + buttons = [self.close_settings_button] + else: + buttons = self.picking_buttons + + for text, rect, _ in buttons: + color = (100, 100, 255) if rect.collidepoint(mouse_pos) else (255, 255, 255) + + if text == "设置" or text == "X" or text == "帮助": # 圆形按钮 + self.draw_circle_button(text, rect, color) + else: + self.draw_rounded_rect_button(text, rect, color) + + def circle_button_clicked(self, rect, mouse_pos): + center = rect.center + radius = rect.width // 2 + dist = ((mouse_pos[0] - center[0]) ** 2 + (mouse_pos[1] - center[1]) ** 2) ** 0.5 + return dist <= radius + + def draw_label(self): + if self.result_label or self.message_label or self.pick_message: + label_x = 100 + + if self.pick_message: + pick_surface = self.font.render(self.pick_message, True, (0, 0, 0)) + pick_y = self.screen_height - 150 + self.screen.blit(pick_surface, (label_x, pick_y)) + + if self.message_label: + message_surface = self.font.render(self.message_label, True, (0, 0, 0)) + message_y = self.screen_height - 150 + self.screen.blit(message_surface, (label_x, message_y)) + + if self.result_label: + result_surface = self.font.render(self.result_label, True, (0, 0, 0)) + result_y = self.screen_height - 100 + self.screen.blit(result_surface, (label_x, result_y)) + + def run(self): + running = True + while running: + self.screen.blit(self.background, (0, 0)) + self.draw_buttons() + self.draw_label() + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + if event.type == pygame.MOUSEBUTTONDOWN: + if self.state == "initial": + buttons = self.initial_buttons + [self.settings_button, self.help_button] + elif self.state == "confirm": + buttons = self.confirm_buttons + elif self.state == "accuracy_check": + buttons = self.accuracy_buttons + elif self.state == "settings": + buttons = self.settings_menu_buttons + [self.close_settings_button] + elif self.state == "help": + buttons = [self.close_settings_button] + else: + buttons = self.picking_buttons + + mouse_pos = event.pos + for text, rect, action in buttons: + if text == "设置" or text == "X" or text == "帮助": + if self.circle_button_clicked(rect, mouse_pos): + if self.sound_on: + self.button_sound.play() + action() + elif rect.collidepoint(mouse_pos): + if self.sound_on: + self.button_sound.play() + action() + + if self.music_on and not pygame.mixer.music.get_busy(): + self.play_music() + + pygame.display.flip() + + pygame.quit() + + +if __name__ == '__main__': + picker = RandomNamePicker() + picker.run()