Created
May 9, 2026 14:51
-
-
Save lnit/8ab63ec5a2287608a35ab1a6e336f277 to your computer and use it in GitHub Desktop.
Spinelで動くSnake Game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class SnakeGame | |
| WIDTH = 20 | |
| HEIGHT = 10 | |
| MAX_SNAKE_LENGTH = 200 | |
| def initialize | |
| @field = [] | |
| @snake_x = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] | |
| @snake_y = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] | |
| @snake_length = 3 | |
| @direction = 2 # 0=上, 1=下, 2=右, 3=左 | |
| @food_x = 0 | |
| @food_y = 0 | |
| @score = 0 | |
| @game_over = 0 | |
| @running = 1 | |
| # フィールド初期化(0=空, 1=壁, 2=ヘビ, 3=餌) | |
| y = 0 | |
| while y < HEIGHT | |
| x = 0 | |
| while x < WIDTH | |
| value = (y == 0 || y == HEIGHT - 1 || x == 0 || x == WIDTH - 1) ? 1 : 0 | |
| @field << value | |
| x += 1 | |
| end | |
| y += 1 | |
| end | |
| # ヘビの初期位置(中央) | |
| @snake_x[0] = 10 | |
| @snake_y[0] = 5 | |
| @snake_x[1] = 9 | |
| @snake_y[1] = 5 | |
| @snake_x[2] = 8 | |
| @snake_y[2] = 5 | |
| place_food | |
| end | |
| def run | |
| setup_terminal | |
| clear_screen | |
| # 初期描画 | |
| draw_game | |
| while @running == 1 | |
| # 非ブロッキングでキー入力チェック | |
| key = read_key_nonblocking | |
| if key == "q" | |
| @running = 0 | |
| elsif key == "w" | |
| @direction = 0 if @direction != 1 # 下向きでなければ上に変更可 | |
| elsif key == "s" | |
| @direction = 1 if @direction != 0 # 上向きでなければ下に変更可 | |
| elsif key == "d" | |
| @direction = 2 if @direction != 3 # 左向きでなければ右に変更可 | |
| elsif key == "a" | |
| @direction = 3 if @direction != 2 # 右向きでなければ左に変更可 | |
| end | |
| if @game_over == 0 | |
| move_snake | |
| draw_game | |
| else | |
| @running = 0 | |
| end | |
| flush_sleep(0.2) | |
| end | |
| restore_terminal | |
| print "\r\n\r\nゲーム終了!\r\n" | |
| print "最終スコア: " | |
| print @score | |
| print "\r\n" | |
| end | |
| private | |
| def setup_terminal | |
| system("stty raw -echo") | |
| end | |
| def restore_terminal | |
| system("stty -raw echo") | |
| end | |
| def clear_screen | |
| system("clear") | |
| end | |
| def flush_sleep(seconds) | |
| system("") | |
| system("sleep 0.2") | |
| end | |
| def read_key_nonblocking | |
| # bashのread -tコマンドでタイムアウト付き読み取り | |
| # -t 0.01 = 0.01秒でタイムアウト、-n 1 = 1文字読み取り | |
| result = `bash -c 'read -t 0.01 -n 1 key 2>/dev/null && echo $key'` | |
| result.chomp | |
| end | |
| def get_field(x, y) | |
| @field[y * WIDTH + x] | |
| end | |
| def set_field(x, y, value) | |
| @field[y * WIDTH + x] = value | |
| end | |
| def place_food | |
| # ランダムな位置に餌を配置 | |
| loop do | |
| # spinelではrand(n)で0〜n-1の整数を返す | |
| @food_x = rand(WIDTH - 2) + 1 | |
| @food_y = rand(HEIGHT - 2) + 1 | |
| # ヘビと重ならない位置を探す | |
| occupied = 0 | |
| i = 0 | |
| while i < @snake_length | |
| if @snake_x[i] == @food_x && @snake_y[i] == @food_y | |
| occupied = 1 | |
| end | |
| i += 1 | |
| end | |
| if occupied == 0 | |
| set_field(@food_x, @food_y, 3) | |
| break | |
| end | |
| end | |
| end | |
| def move_snake | |
| # 新しい頭の位置を計算 | |
| new_x = @snake_x[0] | |
| new_y = @snake_y[0] | |
| if @direction == 0 | |
| new_y -= 1 | |
| elsif @direction == 1 | |
| new_y += 1 | |
| elsif @direction == 2 | |
| new_x += 1 | |
| elsif @direction == 3 | |
| new_x -= 1 | |
| end | |
| # 壁との衝突チェック | |
| if get_field(new_x, new_y) == 1 | |
| @game_over = 1 | |
| return | |
| end | |
| # 自分との衝突チェック | |
| i = 0 | |
| while i < @snake_length | |
| if @snake_x[i] == new_x && @snake_y[i] == new_y | |
| @game_over = 1 | |
| return | |
| end | |
| i += 1 | |
| end | |
| # 餌を食べたかチェック | |
| ate_food = 0 | |
| if new_x == @food_x && new_y == @food_y | |
| ate_food = 1 | |
| @score += 10 | |
| @snake_length += 1 | |
| end | |
| # ヘビを移動(後ろから前に) | |
| i = @snake_length - 1 | |
| while i > 0 | |
| @snake_x[i] = @snake_x[i - 1] | |
| @snake_y[i] = @snake_y[i - 1] | |
| i -= 1 | |
| end | |
| @snake_x[0] = new_x | |
| @snake_y[0] = new_y | |
| # フィールド更新 | |
| update_field | |
| # 餌を食べたら新しい餌を配置 | |
| if ate_food == 1 | |
| place_food | |
| end | |
| end | |
| def update_field | |
| # フィールドをクリア(壁以外) | |
| i = 0 | |
| while i < WIDTH * HEIGHT | |
| if @field[i] != 1 | |
| @field[i] = 0 | |
| end | |
| i += 1 | |
| end | |
| # ヘビを配置 | |
| i = 0 | |
| while i < @snake_length | |
| set_field(@snake_x[i], @snake_y[i], 2) | |
| i += 1 | |
| end | |
| # 餌を配置 | |
| set_field(@food_x, @food_y, 3) | |
| end | |
| def draw_game | |
| # 画面クリアとカーソルをホームに移動 | |
| print "\033[2J\033[H" | |
| print "=== スネークゲーム ===\r\n" | |
| print "WASD: 移動, Q: 終了\r\n" | |
| print "スコア: " | |
| print @score | |
| print "\r\n" | |
| print "\r\n" | |
| # フィールド描画 | |
| y = 0 | |
| while y < HEIGHT | |
| x = 0 | |
| while x < WIDTH | |
| cell = get_field(x, y) | |
| if cell == 1 | |
| print "ロ" | |
| elsif cell == 2 | |
| print "巳" | |
| elsif cell == 3 | |
| print "飯" | |
| else | |
| print " " | |
| end | |
| x += 1 | |
| end | |
| print "\r\n" | |
| y += 1 | |
| end | |
| if @game_over == 1 | |
| print "\r\n" | |
| print "ゲームオーバー!\r\n" | |
| end | |
| system("") # フラッシュ | |
| end | |
| end | |
| # ゲーム実行 | |
| begin | |
| game = SnakeGame.new | |
| game.run | |
| rescue Interrupt | |
| system("stty -raw echo") | |
| print "\r\n\r\n割り込みで終了\r\n" | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment