diff --git a/terminal_snake.py b/terminal_snake.py new file mode 100644 index 0000000..d320c75 --- /dev/null +++ b/terminal_snake.py @@ -0,0 +1,95 @@ +import curses, time, random + +def main(stdscr): + curses.curs_set(0) + stdscr.nodelay(True) + + # 画面サイズは起動時に固定として扱う(iPadの微妙なリサイズ対策) + min_h, min_w = 20, 40 + max_y, max_x = stdscr.getmaxyx() + if max_y < min_h or max_x < min_w: + stdscr.clear() + stdscr.addstr(0, 0, f"Make terminal larger (>= {min_h}x{min_w}).") + stdscr.refresh() + stdscr.getch() + return + + # 枠(最初に一度だけ) + stdscr.clear() + stdscr.border() + + # プレイ可能範囲(枠の内側) + top, left = 1, 1 + bottom, right = max_y - 2, max_x - 2 + + # 初期配置 + center = (max_y // 2, max_x // 2) + snake = [center] + direction = (0, 1) # 右 + food = (random.randint(top, bottom), random.randint(left, right)) + while food == center: + food = (random.randint(top, bottom), random.randint(left, right)) + score = 0 + speed = 0.07 # 小さいほど速い + + while True: + # 入力(矢印 / WASD / q) + key = stdscr.getch() + if key in (curses.KEY_UP, ord('w'), ord('W')) and direction != (1, 0): + direction = (-1, 0) + elif key in (curses.KEY_DOWN, ord('s'), ord('S')) and direction != (-1, 0): + direction = (1, 0) + elif key in (curses.KEY_LEFT, ord('a'), ord('A')) and direction != (0, 1): + direction = (0, -1) + elif key in (curses.KEY_RIGHT, ord('d'), ord('D')) and direction != (0, -1): + direction = (0, 1) + elif key in (ord('q'), ord('Q')): + break + + # 次の頭 + head_y, head_x = snake[0] + ny, nx = head_y + direction[0], head_x + direction[1] + + # 壁 or 自己衝突 + if ny < top or ny > bottom or nx < left or nx > right or (ny, nx) in snake: + msg = f"GAME OVER (score={score}) Press any key..." + stdscr.addstr(max_y//2, max(1, (max_x-len(msg))//2), msg) + stdscr.nodelay(False) + stdscr.getch() + break + + # 前進 + snake.insert(0, (ny, nx)) + if (ny, nx) == food: + score += 1 + # 新しいエサ(スネーク上は避ける) + while True: + food = (random.randint(top, bottom), random.randint(left, right)) + if food not in snake: + break + # 少しずつ速く + if speed > 0.03: + speed -= 0.002 + else: + snake.pop() # しっぽを短く(個別消しはしない) + + # 再描画(盤面をクリアしない。枠は残す) + # スコア行だけ上書き(はみ出し防止) + score_text = f"SNAKE score={score} (q=quit)" + score_text = score_text[:max(0, right - 1)] + stdscr.addstr(0, 2, " " * (max_x - 4)) + stdscr.addstr(0, 2, score_text) + + # 食べ物 + stdscr.addch(food[0], food[1], '*') + + # スネーク全身を描画 + for i, (y, x) in enumerate(snake): + stdscr.addch(y, x, 'O' if i == 0 else 'o') + + stdscr.refresh() + time.sleep(speed) + +if __name__ == "__main__": + curses.wrapper(main) + # TODO: add high score featur \ No newline at end of file