用Python搞點事

文字介面貪食蛇

講者: 楊平

SIRLA

https://slides.com/sirla_potato/curses_snake/fullscreen
https://slides.com/sirla_potato/curses_snake/live

大綱

  • 課程介紹
  • curses套件
  • 遊戲程式邏輯
  • 實作

課程介紹

本日目標

能學到什麼

  • curses套件
  • 貪食蛇遊戲程式設計

curses套件

pip install windows-curses

curses? 詛咒?

才不是

curses 是 cursor optimization 的雙關語

是一個用來製作TUI(Text User Interface)的套件

curses套件

import curses

curses.initscr() # 初始化視窗
win = curses.newwin(20, 60, 0, 0) # 設定視窗大小(nlines, ncols, x, y)
win.keypad(True) # 讓curses可以讀到方向鍵
curses.noecho() # 讓使用者輸入的字元不會顯示在終端機上
curses.curs_set(False) # 讓光標不會跳出
win.border(0) # 使用default樣式繪製邊框

# 如果按下esc則跳出程式
key = None
while key != 27:
    key = win.getch()  # 取得使用者輸入字元

curses.endwin()

curses套件

基本設定

# ...

win.addstr(0, 27, ' Hello! ') # (nlines, ncols, str)
key = None
while key != 27:
    key = win.getch()
    win.addch(10, 50, key) # (nlines, ncols, str)

curses.endwin()

curses套件

在視窗上顯示字元

遊戲程式邏輯

遊戲基本架構

  • 初始化
  • 主程式(無限迴圈)
    • 使用者輸入
    • 計算座標
    • 碰撞判斷
    • 顯示結果
  • 結束遊戲

移動

每次移動的僅有最前端

POP

[0, 3]

[0, 2]

[0, 1]

[0, 4]

[0, 5]

POP

[1, 5]

POP

[2, 5]

POP

吃食物

POP

[0, 3]

[0, 2]

[0, 1]

[0, 4]

[0, 5]

POP

[1, 5]

POP

[2, 5]

POP

[3, 5]

[4, 5]

POP

穿牆

POP

[1, 8]

[1, 7]

[1, 6]

[1, 9]

[1, 10]

POP

[1, 6]

POP

[1, 7]

POP

[1, 8]

POP

實作

完整程式碼

https://gist.github.com/NetJagaimo/58cfc5eb013539e8cb99b98fbd44f796

修改自: https://gist.github.com/sanchitgangwar/2158089

注意cmd的視窗大小要比你遊戲的win還大,不然會出不來喔

初始化

import curses
from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN
from random import randint


curses.initscr()                      # 初始化視窗
win = curses.newwin(20, 60, 0, 0)     # 設定視窗大小(nlines, ncols, x, y)
win.keypad(True)                      # 讓curses可以讀到方向鍵
curses.noecho()                       # 讓使用者輸入的字元不會顯示在終端機上
curses.curs_set(False)                # 讓光標不會跳出
win.border(0)                         # 使用default樣式繪製邊框

key = KEY_RIGHT                                                    
score = 0

snake = [[4,10], [4,9], [4,8]]        # 初始化貪食蛇
food = [10,20]                        # 第一顆食物的位置

win.addstr(0, 27, ' SNAKE ')          # 程式title
win.addch(food[0], food[1], '*')      # 將食物顯示出來

主程式-使用者輸入

# **初始化**

while key != 27:                                                   # while esc沒有被按下
    win.addstr(0, 2, 'Score : ' + str(score) + ' ')                # 印出分數
    win.timeout(140)
    
    prevKey = key                                                  # 前一個按下的鍵
    event = win.getch()                                            # 如果使用者有按下按鍵就不會是-1
    if event != -1:
        key = event

    if key not in [KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, 27]:     # 確認為有效輸入
        key = prevKey

主程式-計算座標

# **初始化**

while key != 27:
    # **使用者輸入**

    # 計算貪食蛇頭部新座標
    snake.insert(0, [snake[0][0] + (key == KEY_DOWN and 1) + (key == KEY_UP and -1), \
                     snake[0][1] + (key == KEY_LEFT and -1) + (key == KEY_RIGHT and 1)])

    # 如果碰到邊界則讓它到另一邊
    if snake[0][0] == 0: snake[0][0] = 18
    if snake[0][1] == 0: snake[0][1] = 58
    if snake[0][0] == 19: snake[0][0] = 1
    if snake[0][1] == 59: snake[0][1] = 1

    # 如果碰到自己則結束遊戲
    if snake[0] in snake[1:]: 
        break

主程式-碰撞判斷&顯示結果

# **初始化**

while key != 27:
    # **使用者輸入**
    # **計算座標**  

    # 如果碰到自己
    if snake[0] in snake[1:]: 
        break
    
    if snake[0] == food:                                # 吃到食物時
        food = []
        score += 1
        while food == []:
            food = [randint(1, 18), randint(1, 58)]     # 出現下一個食物
            if food in snake: food = []
        win.addch(food[0], food[1], '*')
    else:    
        last = snake.pop()                              # 如果沒吃到食物則將最後一個pop掉
        win.addch(last[0], last[1], ' ')                # pop掉的座標顯示為空白
    win.addch(snake[0][0], snake[0][1], '#')

結束遊戲

# **初始化**

while key != 27:
    # **使用者輸入**
    # **計算座標**   
    # **碰撞判斷&顯示結果**

curses.endwin()
print("\nScore - " + str(score))

Thank for listening

填個問卷,讓我們更好

https://forms.gle/FUVVtfLiZGggvnkTA

按讚粉專,獲得最新消息!

https://www.facebook.com/sirla.fjulis

Made with Slides.com