commit e51d514c84258d38a719fba37562016e1ca46787 Author: mario <307813031@qq.com> Date: Sun Sep 27 22:55:32 2026 +0800 上传文件至「/」 init diff --git a/test_snake.py b/test_snake.py new file mode 100644 index 0000000..f921870 --- /dev/null +++ b/test_snake.py @@ -0,0 +1,109 @@ +# 无头测试:桩替代 pygame,验证 snake 游戏核心逻辑 +import sys, types, os, shutil, tempfile + +# ---------------- pygame 桩 ---------------- +class Surf: + def fill(self, *a, **k): pass + def blit(self, *a, **k): pass + +class Font: + def __init__(self, *a, **k): pass + def render(self, text, aa, color): + s = Surf() + s.get_width = lambda: 10 * len(str(text)) + s.get_height = lambda: 20 + return s + +class Rect: + def __init__(self, x, y, w, h, *a, **k): + self.x, self.y, self.width, self.height = x, y, w, h + @property + def right(self): return self.x + self.width + +class Clock: + def tick(self, *a): return 0 + def get_time(self): return 16 + +fake = types.ModuleType("pygame") +fake.init = lambda: None +fake.quit = lambda: None +fake.joystick = types.SimpleNamespace(init=lambda: None, Joystick=lambda i: None, + get_count=lambda: 0) +fake.display = types.SimpleNamespace(set_mode=lambda s: Surf(), set_caption=lambda s: None, + flip=lambda: None) +fake.font = types.SimpleNamespace(Font=Font) +fake.Rect = Rect +fake.draw = types.SimpleNamespace(rect=lambda *a, **k: None, line=lambda *a, **k: None) +fake.time = types.SimpleNamespace(Clock=Clock) +for name, val in dict(K_ESCAPE=27, K_UP=273, K_DOWN=274, K_LEFT=276, K_RIGHT=275, + K_w=119, K_s=115, K_a=97, K_d=100, K_RETURN=13, K_SPACE=32, + QUIT=256, KEYDOWN=768, JOYBUTTONDOWN=769, JOYHATMOTION=770, + JOYAXISMOTION=771).items(): + setattr(fake, name, val) +sys.modules["pygame"] = fake + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + "games", "snake")) +import importlib.util +spec = importlib.util.spec_from_file_location( + "snake_main", os.path.join(os.path.dirname(os.path.abspath(__file__)), + "games", "snake", "main.py")) +m = importlib.util.module_from_spec(spec) +spec.loader.exec_module(m) + +# 把存档指到临时目录,避免污染 +tmp = tempfile.mkdtemp() +m.SAVE_DIR = tmp +m.SAVE_FILE = os.path.join(tmp, "snake_best.txt") + +g = m.SnakeGame() + +# 1. 初始为菜单态 +assert g.state == "menu" +g.confirm() +assert g.state == "play" +assert len(g.snake) == 3 +print("1. 菜单→开始 OK") + +# 2. 移动两步,蛇头前进 +head0 = g.snake[0] +g.step() +assert g.snake[0] == (head0[0] + 1, head0[1]), g.snake[0] +assert len(g.snake) == 3 +print("2. 移动 OK") + +# 3. 禁止 180° 掉头 +g.push_dir((-1, 0)) +g.step() +assert g.dir == (1, 0), g.dir +print("3. 禁止掉头 OK") + +# 4. 吃到苹果:加分 + 变长 +g.apple = (g.snake[0][0] + 1, g.snake[0][1]) +score0, len0 = g.score, len(g.snake) +g.step() +assert g.score == score0 + 1 and len(g.snake) == len0 + 1 +print("4. 吃苹果 OK") + +# 5. 撞墙 → 结束并保存最高分 +g.snake = [(m.COLS - 1, 5), (m.COLS - 2, 5), (m.COLS - 3, 5)] +g.dir, g.pending = (1, 0), [] +g.score = 12 +g.step() +assert g.state == "over" and g.best == 12 +assert open(m.SAVE_FILE).read() == "12" +print("5. 撞墙/最高分保存 OK") + +# 6. 重新开始 +g.confirm() +assert g.state == "play" and g.score == 0 and len(g.snake) == 3 +print("6. 重开 OK") + +# 7. 存档路径在仓库外(约定 games/_saves) +m.SAVE_DIR = os.path.normpath(os.path.join( + os.path.dirname(os.path.abspath(__file__)), "games", "snake", "..", "_saves")) +assert "_saves" in m.SAVE_DIR and "snake" not in m.SAVE_DIR.replace("_saves", "", 1).rstrip(os.sep).split(os.sep)[-1] +print("7. 存档位于仓库外 OK") + +shutil.rmtree(tmp, ignore_errors=True) +print("\n游戏逻辑全部测试通过 ✔")