Last active
September 19, 2021 09:30
-
-
Save mdecourse/3db55f124185def5aac8e5d51579c484 to your computer and use it in GitHub Desktop.
brython_snake
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
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Brython Snake</title> | |
| <!-- 導入 brython javascript 程式庫 --> | |
| <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.8.9/brython.min.js"> | |
| </script> | |
| <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> | |
| <style> /* Removed to keep the snippet short. Find the full file here: */ </style> | |
| </head> | |
| <!-- 啟動 brython --> | |
| <body onload="brython()"> | |
| <h1 class="text-center">Snake built with <a href="https://brython.info">Python!</a></h1> | |
| <!-- 這裡需要一個 canvas 標註放入 canvas 內容 --> | |
| <canvas id="game-board" width="400" height="400"></canvas> | |
| <br> | |
| <!-- 這裡需要一個 id=score 的標註, 插入得分資料 --> | |
| <h3 id="score" class="text-center">Score: 0</h3> | |
| <br> | |
| <!-- high score --> | |
| <h6 id="high-score" class="text-center">High Score: 0</h6> | |
| <br> | |
| <div class="text-center"> | |
| <!-- 這裡需要一個 id=instructions-btn 的 button --> | |
| <button id="instructions-btn" class="btn btn-info">Instructions</button> | |
| </div> | |
| <script type="text/python"> | |
| from browser import document, html, window | |
| from javascript import Math | |
| score = 0 | |
| high_score = 0 | |
| px = py = 10 | |
| gs = tc = 20 | |
| ax = ay = 15 | |
| xv = yv = 0 | |
| trail = [] | |
| tail = 5 | |
| pre_pause = [0,0] | |
| paused = False | |
| def game(): | |
| global px, py, tc, gs, ax, ay, trail, tail, score | |
| px += xv | |
| py += yv | |
| if px < 0: | |
| px = tc-1 | |
| if px > tc-1: | |
| px = 0 | |
| if py < 0: | |
| py = tc-1 | |
| if py > tc-1: | |
| py = 0 | |
| ctx.fillStyle = "black" | |
| ctx.fillRect(0, 0, canvas.width, canvas.height) | |
| ctx.fillStyle = "lime" | |
| for i in range(len(trail)): | |
| ctx.fillRect(trail[i][0]*gs, trail[i][1]*gs, gs-2, gs-2) | |
| if trail[i][0] == px and trail[i][1] == py: | |
| score = score if paused else 0 | |
| tail = 5 | |
| trail.insert(0, [px, py]) | |
| while len(trail) > tail: | |
| trail.pop() | |
| if ax == px and ay == py: | |
| tail += 1 | |
| ax = Math.floor(Math.random()*tc) | |
| ay = Math.floor(Math.random()*tc) | |
| score += 1 | |
| update_score(score) | |
| ctx.fillStyle = "red" | |
| ctx.fillRect(ax*gs, ay*gs, gs-2, gs-2) | |
| def update_score(new_score): | |
| global high_score | |
| document["score"].innerHTML = "Score: " + str(new_score) | |
| if new_score > high_score: | |
| document["high-score"].innerHTML = "High Score: " + str(new_score) | |
| high_score = new_score | |
| def key_push(evt): | |
| global xv, yv, pre_pause, paused | |
| key = evt.keyCode | |
| if key == 37 and not paused: | |
| xv = -1 | |
| yv = 0 | |
| elif key == 38 and not paused: | |
| xv = 0 | |
| yv = -1 | |
| elif key == 39 and not paused: | |
| xv = 1 | |
| yv = 0 | |
| elif key == 40 and not paused: | |
| xv = 0 | |
| yv = 1 | |
| elif key == 32: | |
| temp = [xv, yv] | |
| xv = pre_pause[0] | |
| yv = pre_pause[1] | |
| pre_pause = [*temp] | |
| paused = not paused | |
| def show_instructions(evt): | |
| window.alert("Use the arrow keys to move and press spacebar to pause the game.") | |
| canvas = document["game-board"] | |
| ctx = canvas.getContext("2d") | |
| document.addEventListener("keydown", key_push) | |
| game_loop = window.setInterval(game, 1000/15) | |
| instructions_btn = document["instructions-btn"] | |
| instructions_btn.addEventListener("click", show_instructions) | |
| </script> | |
| </body> | |
| </html> |
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
| from browser import document as doc | |
| from browser import html | |
| import math | |
| from browser.timer import request_animation_frame as raf | |
| from browser.timer import cancel_animation_frame as caf | |
| # 建立 game-board canvas | |
| canvas = html.CANVAS(width = 300, height = 300) | |
| canvas.id = "game-board" | |
| brython_div = doc["brython_div"] | |
| brython_div <= canvas | |
| ctx = canvas.getContext("2d") | |
| px = 50 | |
| py = 50 | |
| speed = 10 | |
| radius = 10 | |
| # 建立 button | |
| brython_div <= html.BUTTON("啟動", id="power") | |
| def game(): | |
| global px, py, speed, radius | |
| ctx.fillStyle = "white" | |
| # 將畫布範圍全部以白色刷新 | |
| ctx.clearRect(0, 0, canvas.width, canvas.height) | |
| if px < canvas.width/2: | |
| px += speed | |
| else: | |
| py -= speed | |
| if px < 0 or (px + radius) > canvas.width: | |
| speed = -speed | |
| if py < 0 or (py + radius) > canvas.height: | |
| speed = -speed | |
| # create new path | |
| ctx.beginPath() | |
| ctx.fillStyle = "red" | |
| ctx.arc(px, py, radius, 0, 2*math.pi, False) | |
| ctx.fill() | |
| # add a straight line to the path, go to the start of the sub-path | |
| ctx.closePath() | |
| # 將 anim 設為 None | |
| anim = None | |
| def launchAnimation(ev): | |
| global anim | |
| anim = raf(launchAnimation) | |
| doc['power'].text = '暫停' | |
| game() | |
| def key_push(evt): | |
| global anim, px, py, move | |
| key = evt.keyCode | |
| # p keyCode is 80 | |
| # s keyCode is 83 | |
| # l keyCode is 76 | |
| # r keyCode is 82 | |
| # u keyCode is 85 | |
| # d keyCode is 68 | |
| # p keyCode is 80 | |
| if key == 80: | |
| caf(anim) | |
| anim = None | |
| # 按下 pause, 按鈕文字轉為繼續 | |
| doc['power'].text = '繼續' | |
| # s keyCode is 83 | |
| elif key == 83: | |
| anim = raf(launchAnimation) | |
| #按下 start 按鈕文字轉為繼續 | |
| #doc['power'].text = '暫停' | |
| def button_press(ev): | |
| global anim | |
| # 初始啟動, anim 為 None | |
| if anim is None: | |
| anim = raf(launchAnimation) | |
| # 初始啟動後, 按鈕文字轉為"暫停" | |
| #doc['power'].text = '暫停' | |
| elif anim == 'hold': | |
| # 當 anim 為 'hold' 表示曾經暫停後的啟動, 因此持續以 set_interval() 持續旋轉, 且將 power 文字轉為"暫停" | |
| anim = raf(launchAnimation) | |
| #doc['power'].text = '暫停' | |
| else: | |
| # 初始啟動後, 使用者再按 power, 此時 anim 非 None 也不是 'hold', 因此會執行 clear_interval() 暫停 | |
| # 且將 anim 變數設為 'hold', 且 power 文字轉為"繼續" | |
| caf(anim) | |
| anim = 'hold' | |
| doc['power'].text = '繼續' | |
| # 將 key_push 函式納入 EventListener | |
| doc.addEventListener("keydown", key_push) | |
| doc["power"].bind("click", button_press) |
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
| from browser import html | |
| from browser import document as doc | |
| import browser.timer | |
| def game(): | |
| global px, py, speed | |
| ctx.clearRect(px, py, width, height) | |
| ctx.fillStyle = "red" | |
| if px < canvas.width/2: | |
| px += speed | |
| else: | |
| py -= speed | |
| if px < 0 or (px + width) > canvas.width: | |
| speed = -speed | |
| if py < 0 or (py + height) > canvas.height: | |
| speed = -speed | |
| ctx.fillRect(px, py, width, height) | |
| canvas = html.CANVAS(width = 600, height = 600) | |
| canvas.id = "game-board" | |
| brython_div = doc["brython_div"] | |
| brython_div <= canvas | |
| ctx = canvas.getContext("2d") | |
| px = 0 | |
| py = 50 | |
| width = 20 | |
| height = 20 | |
| speed = 2 | |
| browser.timer.set_interval(game, 10) |
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
| # 從 browser 導入 document 並設為 doc | |
| from browser import document as doc | |
| # 使用者可以透過 window 當作介面使用其他 Javascript 功能 | |
| from browser import html, window | |
| # 用於定時執行特定函式 | |
| import browser.timer | |
| # 導入數學模組 | |
| import math | |
| # 導入亂數模組 | |
| from random import random, randint | |
| def update_score(new_score): | |
| # 將 high_score 設為 global | |
| # 表示在函式中改變的值有效範圍擴及函式外圍 | |
| global high_score | |
| score_doc.innerHTML = "Score: " + str(new_score) | |
| if new_score > high_score: | |
| high_score_doc.innerHTML = "High Score: " + str(new_score) | |
| high_score = new_score | |
| def eat(px, py, ax, ay): | |
| # global 函式的有效範圍可及於函式外 | |
| global xv, yv, pre_pause, paused | |
| # (px, py) go to (ax, ay) through incremented xv, yv | |
| if ax != px or ay != py: | |
| # 走直線座標判斷式 | |
| ''' | |
| if ax > px and not paused: | |
| xv = 1 | |
| yv = 0 | |
| if ax < px and not paused: | |
| xv = -1 | |
| yv = 0 | |
| if ay > py and not paused: | |
| xv = 0 | |
| yv = 1 | |
| if ay < py and not paused: | |
| xv = 0 | |
| yv = -1 | |
| ''' | |
| # 先走一段斜線判斷式 | |
| if ax > px and not paused: | |
| xv = 1 | |
| yv = 0 | |
| if ay > py and not paused: | |
| yv = 1 | |
| if ay < py and not paused: | |
| yv = -1 | |
| if ax < px and not paused: | |
| xv = -1 | |
| yv = 0 | |
| if ay > py and not paused: | |
| yv = 1 | |
| if ay < py and not paused: | |
| yv = -1 | |
| if ay > py and not paused: | |
| xv = 0 | |
| yv = 1 | |
| if ax > px and not paused: | |
| xv = 1 | |
| if ax < px and not paused: | |
| xv = -1 | |
| if ay < py and not paused: | |
| xv = 0 | |
| yv = -1 | |
| if ax > px and not paused: | |
| xv = 1 | |
| if ax < px and not paused: | |
| xv = -1 | |
| def game(): | |
| global px, py, tc, gs, ax, ay, trail, tail, score | |
| # px 為 snake 第一個點的 x 座標, 增量值為 xv | |
| px += xv | |
| py += yv | |
| # 允許穿越四面牆, 以 tc 表示牆面座標極限 | |
| # 若 px 為負值則設定為 tc -1, 表示 tc 為 x 方向 limit | |
| # x 座標方向的穿牆設定 | |
| if px < 0: | |
| px = tc-1 | |
| if px > tc-1: | |
| px = 0 | |
| # y 座標方向的穿牆設定 | |
| if py < 0: | |
| py = tc-1 | |
| if py > tc-1: | |
| py = 0 | |
| ctx.fillStyle = "black" | |
| # 畫布填入黑色 | |
| ctx.fillRect(0, 0, canvas.width, canvas.height) | |
| # snake 為 lime 色 | |
| ctx.fillStyle = "lime" | |
| # trail 為數列, 代表 snake 各節 [x,y] 座標 | |
| # trail = [[x0,y0], [x1, y1], [x2, y2]...] | |
| # gs 為方塊邊長 pixel 數 | |
| for i in range(len(trail)): | |
| # https://developer.mozilla.org/zh-TW/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes | |
| # fillRect(x, y, width, height) | |
| ctx.fillRect(trail[i][0]*gs, trail[i][1]*gs, gs-2, gs-2) | |
| # 若 snake 第一節座標 (px, py) 穿過身體任一節, 則 score 歸零 | |
| if trail[i][0] == px and trail[i][1] == py: | |
| score = score if paused else 0 | |
| # snake reset 為五節 | |
| tail = 5 | |
| # trail 數列以碰到的 [px, py] 座標數列插入作為第一節 | |
| trail.insert(0, [px, py]) | |
| while len(trail) > tail: | |
| # pop() 內建移除數列最後一個 element | |
| trail.pop() | |
| # ax, ay 為紅點座標 | |
| # 當 snake 第一節座標[px, py] 與紅色食物座標 [ax, ay] 重合 | |
| # 則 tail 增量, 即多一節且得分加 1, 然後食物座標 [ax, ay] 重新以亂數定位 | |
| if ax == px and ay == py: | |
| tail += 1 | |
| ax = math.floor(random()*tc) | |
| ay = math.floor(random()*tc) | |
| score += 1 | |
| # [ax, ay] is known here | |
| # [px, py] is where the head of the snake | |
| # xv needed to be incremented from px to ax first | |
| # and yv needed to be incremented from py to ay | |
| eat(px, py, ax, ay) | |
| # 更新計分顯示 | |
| update_score(score) | |
| ctx.fillStyle = "red" | |
| ctx.fillRect(ax*gs, ay*gs, gs-2, gs-2) | |
| def key_push(evt): | |
| global xv, yv, pre_pause, paused | |
| key = evt.keyCode | |
| # 37 is left arrow key | |
| # 74 is j key | |
| if key == 74 and not paused: | |
| xv = -1 | |
| yv = 0 | |
| # 38 is up arrow key | |
| # 73 is i key | |
| elif key == 73 and not paused: | |
| xv = 0 | |
| yv = -1 | |
| # 39 is right arrow key | |
| # 76 is l key | |
| elif key == 76 and not paused: | |
| xv = 1 | |
| yv = 0 | |
| # 40 is down arrow key | |
| # 77 is m key | |
| elif key == 77 and not paused: | |
| xv = 0 | |
| yv = 1 | |
| # 32 is pause key | |
| # 80 is p key | |
| elif key == 80: | |
| temp = [xv, yv] | |
| xv = pre_pause[0] | |
| yv = pre_pause[1] | |
| pre_pause = [*temp] | |
| paused = not paused | |
| def show_instructions(evt): | |
| window.alert("keys to control: i=up, m=down, j=left, l=right, p=pause") | |
| # 利用 html 建立 canvas 超文件物件 | |
| canvas = html.CANVAS(width = 600, height = 600) | |
| canvas.id = "game-board" | |
| brython_div = doc["brython_div"] | |
| brython_div <= canvas | |
| score_doc = html.DIV("score") | |
| score_doc.id = "score" | |
| brython_div <= score_doc | |
| high_score_doc = html.DIV("high-score") | |
| high_score_doc.id = "high-score" | |
| brython_div <= high_score_doc | |
| button = html.BUTTON("Keys to control") | |
| button.id = "instructions-btn" | |
| brython_div <= button | |
| score = 0 | |
| high_score = 0 | |
| px = py = 10 | |
| # gs*tc = canvas width and height | |
| gs = 20 | |
| tc = 30 | |
| ax = ay = 15 | |
| xv = yv = 0 | |
| trail = [] | |
| tail = 5 | |
| pre_pause = [0,0] | |
| paused = False | |
| ctx = canvas.getContext("2d") | |
| doc.addEventListener("keydown", key_push) | |
| instructions_btn = doc["instructions-btn"] | |
| instructions_btn.addEventListener("click", show_instructions) | |
| browser.timer.set_interval(game, 1000/15) |
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
| # 從 browser 導入 document 並設為 doc | |
| from browser import document as doc | |
| # 使用者可以透過 window 當作介面使用其他 Javascript 功能 | |
| from browser import html, window | |
| # 用於定時執行特定函式 | |
| import browser.timer | |
| # 導入數學模組 | |
| import math | |
| # 導入亂數模組 | |
| from random import random, randint | |
| def update_score(new_score): | |
| # 將 high_score 設為 global | |
| # 表示在函式中改變的值有效範圍擴及函式外圍 | |
| global high_score | |
| score_doc.innerHTML = "Score: " + str(new_score) | |
| if new_score > high_score: | |
| high_score_doc.innerHTML = "High Score: " + str(new_score) | |
| high_score = new_score | |
| def eat(px, py, ax, ay): | |
| # global 函式的有效範圍可及於函式外 | |
| global xv, yv, pre_pause, paused | |
| # (px, py) go to (ax, ay) through incremented xv, yv | |
| if ax != px or ay != py: | |
| # 走直線座標判斷式 | |
| if ax > px and not paused: | |
| xv = 1 | |
| yv = 0 | |
| if ax < px and not paused: | |
| xv = -1 | |
| yv = 0 | |
| if ay > py and not paused: | |
| xv = 0 | |
| yv = 1 | |
| if ay < py and not paused: | |
| xv = 0 | |
| yv = -1 | |
| ''' | |
| # 先走一段斜線判斷式 | |
| if ax > px and not paused: | |
| xv = 1 | |
| yv = 0 | |
| if ay > py and not paused: | |
| yv = 1 | |
| if ay < py and not paused: | |
| yv = -1 | |
| if ax < px and not paused: | |
| xv = -1 | |
| yv = 0 | |
| if ay > py and not paused: | |
| yv = 1 | |
| if ay < py and not paused: | |
| yv = -1 | |
| if ay > py and not paused: | |
| xv = 0 | |
| yv = 1 | |
| if ax > px and not paused: | |
| xv = 1 | |
| if ax < px and not paused: | |
| xv = -1 | |
| if ay < py and not paused: | |
| xv = 0 | |
| yv = -1 | |
| if ax > px and not paused: | |
| xv = 1 | |
| if ax < px and not paused: | |
| xv = -1 | |
| ''' | |
| def game(): | |
| global px, py, tc, gs, ax, ay, trail, tail, score | |
| # px 為 snake 第一個點的 x 座標, 增量值為 xv | |
| px += xv | |
| py += yv | |
| # 允許穿越四面牆, 以 tc 表示牆面座標極限 | |
| # 若 px 為負值則設定為 tc -1, 表示 tc 為 x 方向 limit | |
| # x 座標方向的穿牆設定 | |
| if px < 0: | |
| px = tc-1 | |
| if px > tc-1: | |
| px = 0 | |
| # y 座標方向的穿牆設定 | |
| if py < 0: | |
| py = tc-1 | |
| if py > tc-1: | |
| py = 0 | |
| ctx.fillStyle = "black" | |
| # 畫布填入黑色 | |
| ctx.fillRect(0, 0, canvas.width, canvas.height) | |
| # snake 為 lime 色 | |
| ctx.fillStyle = "lime" | |
| # trail 為數列, 代表 snake 各節 [x,y] 座標 | |
| # trail = [[x0,y0], [x1, y1], [x2, y2]...] | |
| # gs 為方塊邊長 pixel 數 | |
| for i in range(len(trail)): | |
| # https://developer.mozilla.org/zh-TW/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes | |
| # fillRect(x, y, width, height) | |
| ctx.fillRect(trail[i][0]*gs, trail[i][1]*gs, gs-2, gs-2) | |
| # 若 snake 第一節座標 (px, py) 穿過身體任一節, 則 score 歸零 | |
| if trail[i][0] == px and trail[i][1] == py: | |
| score = score if paused else 0 | |
| # snake reset 為五節 | |
| tail = 5 | |
| # trail 數列以碰到的 [px, py] 座標數列插入作為第一節 | |
| trail.insert(0, [px, py]) | |
| while len(trail) > tail: | |
| # pop() 內建移除數列最後一個 element | |
| trail.pop() | |
| # ax, ay 為紅點座標 | |
| # 當 snake 第一節座標[px, py] 與紅色食物座標 [ax, ay] 重合 | |
| # 則 tail 增量, 即多一節且得分加 1, 然後食物座標 [ax, ay] 重新以亂數定位 | |
| if ax == px and ay == py: | |
| tail += 1 | |
| ax = math.floor(random()*tc) | |
| ay = math.floor(random()*tc) | |
| score += 1 | |
| # [ax, ay] is known here | |
| # [px, py] is where the head of the snake | |
| # xv needed to be incremented from px to ax first | |
| # and yv needed to be incremented from py to ay | |
| eat(px, py, ax, ay) | |
| # 更新計分顯示 | |
| update_score(score) | |
| ctx.fillStyle = "red" | |
| ctx.fillRect(ax*gs, ay*gs, gs-2, gs-2) | |
| def key_push(evt): | |
| global xv, yv, pre_pause, paused | |
| key = evt.keyCode | |
| # 37 is left arrow key | |
| # 74 is j key | |
| if key == 74 and not paused: | |
| xv = -1 | |
| yv = 0 | |
| # 38 is up arrow key | |
| # 73 is i key | |
| elif key == 73 and not paused: | |
| xv = 0 | |
| yv = -1 | |
| # 39 is right arrow key | |
| # 76 is l key | |
| elif key == 76 and not paused: | |
| xv = 1 | |
| yv = 0 | |
| # 40 is down arrow key | |
| # 77 is m key | |
| elif key == 77 and not paused: | |
| xv = 0 | |
| yv = 1 | |
| # 32 is pause key | |
| # 80 is p key | |
| elif key == 80: | |
| temp = [xv, yv] | |
| xv = pre_pause[0] | |
| yv = pre_pause[1] | |
| pre_pause = [*temp] | |
| paused = not paused | |
| def show_instructions(evt): | |
| window.alert("keys to control: i=up, m=down, j=left, l=right, p=pause") | |
| # 利用 html 建立 canvas 超文件物件 | |
| canvas = html.CANVAS(width = 600, height = 600) | |
| canvas.id = "game-board" | |
| brython_div = doc["brython_div"] | |
| brython_div <= canvas | |
| score_doc = html.DIV("score") | |
| score_doc.id = "score" | |
| brython_div <= score_doc | |
| high_score_doc = html.DIV("high-score") | |
| high_score_doc.id = "high-score" | |
| brython_div <= high_score_doc | |
| button = html.BUTTON("Keys to control") | |
| button.id = "instructions-btn" | |
| brython_div <= button | |
| score = 0 | |
| high_score = 0 | |
| px = py = 10 | |
| # gs*tc = canvas width and height | |
| gs = 20 | |
| tc = 30 | |
| ax = ay = 15 | |
| xv = yv = 0 | |
| trail = [] | |
| tail = 5 | |
| pre_pause = [0,0] | |
| paused = False | |
| ctx = canvas.getContext("2d") | |
| doc.addEventListener("keydown", key_push) | |
| instructions_btn = doc["instructions-btn"] | |
| instructions_btn.addEventListener("click", show_instructions) | |
| browser.timer.set_interval(game, 1000/15) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment