-
-
Save mdecycu/d9082d678096bd58378d6afe2c7fa05d to your computer and use it in GitHub Desktop.
cp2022_python
This file contains 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, html, alert | |
import random | |
id4 = document["brython_div"] | |
執行次數 = 100 | |
總猜測次數 = 0 | |
for i in range(執行次數): | |
id4 <= "第" + str(i+1) + "次玩:" + html.BR() | |
下限 = 1 | |
上限 = 100 | |
標準答案 = random.randint(下限, 上限) | |
pc猜的數字 = random.randint(下限, 上限) | |
#print(標準答案, pc猜的數字) | |
#integer int() | |
#string str() | |
#float float() | |
#你猜的數字 = int(input("請輸入您所猜的整數:")) | |
猜測次數 = 1 | |
while 標準答案 != pc猜的數字: | |
if 標準答案 < pc猜的數字: | |
#print("太大了,再猜一次 :)加油") | |
# 因此已經確定"pc猜的數字"不是答案, 因此 - 1 | |
id4 <= "電腦猜的數字:" + str(pc猜的數字) + " 太大了!" + html.BR() | |
上限 = pc猜的數字 - 1 | |
else: | |
#print("太小了,再猜一次 :)加油") | |
# 因此已經確定"pc猜的數字"不是答案, 因此 + 1 | |
id4 <= "電腦猜的數字:" + str(pc猜的數字) + " 太小了!" + html.BR() | |
下限 = pc猜的數字 + 1 | |
#pc猜的數字 = int(input("請輸入您所猜的整數:")) | |
pc猜的數字 = random.randint(下限, 上限) | |
猜測次數 += 1 | |
#print("猜對了!總共猜了", 猜測次數, "次") | |
id4 <= "電腦猜對了, 答案為: " + str(標準答案) + ", 總共猜了 "+ str(猜測次數) + "次" + html.BR() | |
總猜測次數 += 猜測次數 | |
平均猜測次數 = int(總猜測次數/執行次數) | |
#print("平均次數", 平均猜測次數) | |
id4 <= "平均次數: " + str(平均猜測次數) |
This file contains 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
# Ggame ball example | |
from ggame import ( | |
App, | |
Color, | |
LineStyle, | |
Sprite, | |
RectangleAsset, | |
ImageAsset, | |
CircleAsset, | |
EllipseAsset, | |
PolygonAsset, | |
) | |
from browser import document as doc | |
from browser import html | |
# 引入既有的id="graphics-column" 標註 | |
graphics_column = doc["graphics-column"] | |
# 建立內定名稱為 "ggame-canvas" 的 canvas 標註 | |
canvas = html.CANVAS(width = 600, height = 100) | |
canvas.id = "ggame-canvas" | |
# 將 canvas 插入 gc 標註中 | |
graphics_column <= canvas | |
# reverse - change the ball direction | |
def reverse(b): | |
b.direction *= -1 | |
# Set up function for handling screen refresh | |
def step(): | |
if ball.go: | |
ball.x += ball.direction | |
if ball.x + ball.width > myapp.width or ball.x < 0: | |
ball.x -= ball.direction | |
reverse(ball) | |
myapp = App() | |
# Three primary colors with no transparency (alpha = 1.0) | |
red = Color(0xff0000, 1.0) | |
green = Color(0x00ff00, 1.0) | |
blue = Color(0x0000ff, 1.0) | |
black = Color(0x000000, 1.0) | |
# define colors and line style | |
green = Color(0x00ff00, 1) | |
black = Color(0, 1) | |
noline = LineStyle(0, black) | |
# a rectangle asset and sprite to use as background | |
bg_asset = RectangleAsset(canvas.width, canvas.height, noline, green) | |
bg = Sprite(bg_asset, (0,0)) | |
ball_asset = ImageAsset("/images/orb-150545_640.png") | |
ball = Sprite(ball_asset, (0, 0)) | |
# Original image is too big. Scale it to 1/10 its original size | |
ball.scale = 0.1 | |
# custom attributes | |
ball.direction = 7 | |
ball.go = True | |
myapp.run(step) |
This file contains 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
# Cango 24v3 版本 Bezier 繪圖 | |
from browser import window, html | |
from browser import document as doc | |
canvas = html.CANVAS(width = 600, height = 400) | |
canvas.id = "canvas" | |
brython_div = doc["brython_div"] | |
brython_div <= canvas | |
# Javascript 物件 | |
cango = window.Cango.new | |
path = window.Path.new | |
shape = window.Shape.new | |
group = window.Group.new | |
# 24v3 circle 為 Javascript 物件 | |
circle = window.circle.new | |
g1 = cango("canvas") | |
g1.clearCanvas() | |
g1.gridboxPadding(10, 10, 5, 7) | |
g1.fillGridbox("lightgreen") | |
g1.setWorldCoordsRHC(-100, -100, 400) | |
x1 = 40 | |
y1 = 20 | |
cx1 = 90 | |
cy1 = 120 | |
x2 = 120 | |
y2 = 100 | |
cx2 = 130 | |
cy2 = 20 | |
cx3 = 150 | |
cy3 = 120 | |
x3 = 180 | |
y3 = 60 | |
def dragC1(mousePos): | |
global cx1, cy1 | |
cx1 = mousePos.x | |
cy1 = mousePos.y | |
drawCurve() | |
def dragC2(mousePos): | |
global cx2, cy2 | |
cx2 = mousePos.x | |
cy2 = mousePos.y | |
drawCurve() | |
def dragC3(mousePos): | |
global cx3, cy3 | |
cx3 = mousePos.x | |
cy3 = mousePos.y | |
drawCurve() | |
def drawCurve(): | |
qbez = path(['M', x1, y1, 'Q', cx1, cy1, x2, y2], { | |
'strokeColor':'blue'}) | |
cbez = path(['M', x2, y2, 'C', cx2, cy2, cx3, cy3, x3, y3], { | |
'strokeColor':'green'}) | |
L1 = path(['M', x1, y1, 'L', cx1, cy1, x2, y2], { | |
'strokeColor':"rgba(0, 0, 0, 0.2)", | |
'dashed':[4]}) | |
L2 = path(['M', x2, y2, 'L', cx2, cy2], { | |
'strokeColor':"rgba(0, 0, 0, 0.2)", | |
'dashed':[4]}) | |
L3 = path(['M', x3, y3, 'L', cx3, cy3], { | |
'strokeColor':"rgba(0, 0, 0, 0.2)", | |
'dashed':[4]}) | |
# 24v3 直接 translate 無需經過 tranform | |
c1.translate(cx1, cy1) | |
c2.translate(cx2, cy2) | |
c3.translate(cx3, cy3) | |
grp = group(qbez, cbez, L1, L2, L3, c1, c2, c3) | |
g1.render(grp, True) | |
g1.clearCanvas("lightyellow") | |
g1.deleteAllLayers() | |
g1.setWorldCoordsRHC(0, 0, 200) | |
c1 = shape(circle(6), {'fillColor':'red'}) | |
c1.enableDrag(None, dragC1, None) | |
c2 = shape(circle(6), {'fillColor':'red'}) | |
c2.enableDrag(None, dragC2, None) | |
c3 = shape(circle(6), {'fillColor':'red'}) | |
c3.enableDrag(None, dragC3, None); | |
drawCurve() |
This file contains 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
# 大樂透電腦選號 | |
# lottery | |
from browser import document, html, alert | |
import random | |
try: | |
total = int(input("請問要出幾張大樂透彩卷號碼?")) | |
except: | |
alert("請輸入要選擇大樂透電腦選號數量的'整數'") | |
total = int(input("請問要出幾張大樂透彩卷號碼?")) | |
# 準備將電腦選出的號碼, 輸出到內定 id="brython_div" 的標註區域 | |
output_div = document["brython_div"] | |
output_div <= "以下將出 " + str(total) + " 張電腦選號彩卷:" + html.BR() | |
for i in range(1, total + 1): | |
# 利用 list(range()) 產生 1 到 49 的 population list | |
# 然後再透過 random.sample(population, k) | |
# 從 population, 產生 k 個不同的數字 | |
numbers = random.sample(list(range(1, 49)), 6) | |
output_div <= str(i) + ". 電腦選號為: " + str(numbers) + html.BR() |
This file contains 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
# 導入 sys 模組 | |
import sys | |
# 導入 keyword 模組 | |
import keyword | |
# 利用 sys 模組中的 version_info 印出 Python 版次 | |
print("Python version: ", sys.version_info) | |
# 利用 keyword 模組中的 kwlist 印出關鍵字 | |
print("Python keywords: ", keyword.kwlist) |
This file contains 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_score2(new_score): | |
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 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 | |
def game2(): | |
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_score2(score) | |
ctx.fillStyle = "red" | |
ctx.fillRect(ax*gs, ay*gs, gs-2, gs-2) | |
def key_push2(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_instructions2(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-board2" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
score_doc = html.DIV("score2") | |
score_doc.id = "score2" | |
brython_div <= score_doc | |
high_score_doc = html.DIV("high-score2") | |
high_score_doc.id = "high-score2" | |
brython_div <= high_score_doc | |
button = html.BUTTON("Keys to control") | |
button.id = "instructions-btn2" | |
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_push2) | |
instructions_btn = doc["instructions-btn2"] | |
instructions_btn.addEventListener("click", show_instructions2) | |
browser.timer.set_interval(game2, 1000/15) |
This file contains 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 https://bmsleight.github.io/brython-blocks/ brython 3.3.5 to 3.9.0 | |
# original source codes: https://github.com/bmsleight/brython-blocks/ | |
# not complete | |
from browser import document, alert, html | |
from random import randint | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 grid 對應 | |
grid = html.CANVAS(width = 0, height = 0) | |
next = html.CANVAS(width = 0, height = 0) | |
stop = html.BUTTON("Stop", id="stop") | |
detail = html.DIV(id="detail") | |
total = html.SPAN(id="total") | |
# 將 canvas 標註的 id 設為 "canvas" | |
grid.id = "grid" | |
next.id = "next" | |
# 將 document 中 id 為 "brython_div" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = document["brython_div"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div"></div> 標註 | |
brython_div <= grid + html.BR() | |
brython_div <= "Next Block" + html.BR() + next + html.BR() | |
brython_div <= detail + html.BR() | |
brython_div <= "Total Lines:" + total + html.BR() | |
brython_div <= stop +html.BR() | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
#canvas = document["grid"] | |
# Globals | |
boarder_size = 5 | |
w = 10 + boarder_size*2 | |
h = 15 + boarder_size*2 | |
block_size = 20 | |
tick_timer = None | |
lines = 0 | |
# Clash is when block + block >31 | |
CLASH = 31 | |
# Empty Blocks | |
WHITE = 0 | |
GRAY = 1 | |
BLOCK_HARD = 15 | |
# Hard Blocks | |
BOARDER = 16 | |
BOARDER_TOP = 17 | |
RED = 18 | |
BLUE = 19 | |
ORANGE = 20 | |
YELLOW = 21 | |
MAGENTA = 22 | |
CYAN = 23 | |
GREEN = 24 | |
def debug_to_browser(text): | |
debug = document["debug"] | |
debug.text = debug.text + str(text) | |
def update_lines_complete(removed): | |
global lines | |
lines = lines + removed | |
document["total"].text = lines | |
def paint_block(canvas_name, x, y, n): | |
if n == WHITE: | |
fill_style = "White" | |
elif n == GRAY: | |
fill_style = "Gray" | |
elif n == RED: | |
fill_style = "Red" | |
elif n == BLUE: | |
fill_style = "Blue" | |
elif n == ORANGE: | |
fill_style = "Orange" | |
elif n == YELLOW: | |
fill_style = "Yellow" | |
elif n == MAGENTA: | |
fill_style = "Magenta" | |
elif n == CYAN: | |
fill_style = "Cyan" | |
elif n == GREEN: | |
fill_style = "Green" | |
elif n == BOARDER: | |
fill_style = "Black" | |
elif n == BOARDER_TOP: | |
fill_style = "Pink" | |
else: | |
fill_style = "Pink" | |
if fill_style: | |
canvas=document[canvas_name].getContext("2d") | |
canvas.beginPath() | |
# Canvas 0,0 it top,left not bottom, left | |
# So we get the height and subtract our value | |
canvas.rect(x*block_size, document[canvas_name].height-y*block_size, block_size, block_size) | |
canvas.fillStyle = fill_style | |
canvas.fill() | |
class PlayingGrid(): | |
def __init__(self): | |
# This looks the wrong way around but it create an x,y array | |
self.grid = [[GRAY for x in range(w)] for y in range(h)] | |
self.set_boarder() | |
def set_boarder(self): | |
for x in range(0, w): | |
for y in range(0, boarder_size): | |
self.grid[y][x] = BOARDER | |
for x in range(0, boarder_size): | |
for y in range(0, h): | |
self.grid[y][x] = BOARDER | |
for x in range(w-boarder_size, w): | |
for y in range(0, h): | |
self.grid[y][x] = BOARDER | |
def remove_complete_lines(self, by): | |
removed = 0 | |
for row in self.grid[by:by+4]: | |
if not WHITE in row and \ | |
not GRAY in row and \ | |
not all(x == row[0] for x in row): | |
self.grid.remove(row) | |
removed = removed + 1 | |
if removed: | |
for r in range(0, removed): | |
# Need a real new row - not the same one added | |
new_row = [GRAY for x in range(w)] | |
self.grid.append(new_row) | |
update_lines_complete(removed) | |
self.set_boarder() | |
return removed | |
def draw_grid(self): | |
for x in range(0,w): | |
for y in range(0,h): | |
paint_block("grid", x, y, self.grid[y][x]) | |
class Block(): | |
def __init__(self): | |
self.style = 0 | |
self.total_styles = 7 | |
self.rotation = 0 | |
# Select a sytle by random, no rotation | |
self.style = randint(0,self.total_styles-1) | |
# Prep grid for rotating blocks | |
self.grid = [[0 for x in range(4)] for y in range(4)] | |
self.sytle_grid() | |
self.x = w/2 - 2 | |
self.y = h - boarder_size | |
def sytle_grid(self): | |
# Rotation is counter-intuitive | |
# if rotate "true", its skips left to right | |
# So we precalculate the rotations so make them better | |
#I | |
if self.style == 0 and \ | |
(self.rotation == 0 or self.rotation == 2): | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, 0, 0], | |
[ RED, RED, RED, RED], | |
[ 0, 0, 0, 0]] | |
if self.style == 0 and \ | |
(self.rotation == 1 or self.rotation == 3): | |
self.grid = [[ 0, RED, 0, 0], | |
[ 0, RED, 0, 0], | |
[ 0, RED, 0, 0], | |
[ 0, RED, 0, 0]] | |
# O | |
if self.style == 1: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, BLUE, BLUE, 0], | |
[ 0, BLUE, BLUE, 0], | |
[ 0, 0, 0, 0]] | |
#S | |
if self.style == 2 and \ | |
(self.rotation == 0 or self.rotation == 2): | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, ORANGE, ORANGE], | |
[ 0, ORANGE, ORANGE, 0], | |
[ 0, 0, 0, 0]] | |
if self.style == 2 and \ | |
(self.rotation == 1 or self.rotation == 3): | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, ORANGE, 0, 0], | |
[ 0, ORANGE, ORANGE, 0], | |
[ 0, 0, ORANGE, 0]] | |
#Z | |
if self.style == 3 and \ | |
(self.rotation == 0 or self.rotation == 2): | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, 0, 0], | |
[ 0, YELLOW, YELLOW, 0], | |
[ 0, 0, YELLOW, YELLOW]] | |
if self.style == 3 and \ | |
(self.rotation == 1 or self.rotation == 3): | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, YELLOW, 0], | |
[ 0, YELLOW, YELLOW, 0], | |
[ 0, YELLOW, 0, 0]] | |
if self.style == 4 and self.rotation == 0: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, MAGENTA, 0, 0], | |
[MAGENTA, MAGENTA, MAGENTA, 0], | |
[ 0, 0, 0, 0]] | |
if self.style == 4 and self.rotation == 1: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, MAGENTA, 0, 0], | |
[MAGENTA, MAGENTA, 0, 0], | |
[ 0, MAGENTA, 0, 0]] | |
if self.style == 4 and self.rotation == 2: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, 0, 0], | |
[MAGENTA, MAGENTA, MAGENTA, 0], | |
[ 0, MAGENTA, 0, 0]] | |
if self.style == 4 and self.rotation == 3: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, MAGENTA, 0, 0], | |
[ 0, MAGENTA, MAGENTA, 0], | |
[ 0, MAGENTA, 0, 0]] | |
# L | |
if self.style == 5 and self.rotation == 0: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, CYAN, 0], | |
[CYAN, CYAN, CYAN, 0], | |
[ 0, 0, 0, 0]] | |
if self.style == 5 and self.rotation == 1: | |
self.grid = [[ 0, 0, 0, 0], | |
[CYAN, CYAN, 0, 0], | |
[ 0, CYAN, 0, 0], | |
[ 0, CYAN, 0, 0]] | |
if self.style == 5 and self.rotation == 2: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, 0, 0], | |
[CYAN, CYAN, CYAN, 0], | |
[CYAN, 0, 0, 0]] | |
if self.style == 5 and self.rotation == 3: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, CYAN, 0, 0], | |
[ 0, CYAN, 0, 0], | |
[ 0, CYAN, CYAN, 0]] | |
if self.style == 6 and self.rotation == 0: | |
self.grid = [[ 0, 0, 0, 0], | |
[GREEN, 0, 0, 0], | |
[GREEN, GREEN, GREEN, 0], | |
[ 0, 0, 0, 0]] | |
if self.style == 6 and self.rotation == 1: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, GREEN, 0, 0], | |
[ 0, GREEN, 0, 0], | |
[GREEN, GREEN, 0, 0]] | |
if self.style == 6 and self.rotation == 2: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, 0, 0, 0], | |
[GREEN, GREEN, GREEN, 0], | |
[ 0, 0, GREEN, 0]] | |
if self.style == 6 and self.rotation == 3: | |
self.grid = [[ 0, 0, 0, 0], | |
[ 0, GREEN, GREEN, 0], | |
[ 0, GREEN, 0, 0], | |
[ 0, GREEN, 0, 0]] | |
def rotate_anticlock(self): | |
self.rotation = self.rotation -1 | |
if self.rotation == -1: | |
self.rotation = 3 | |
self.sytle_grid() | |
def rotate_clock(self): | |
self.rotation = self.rotation +1 | |
if self.rotation == 4: | |
self.rotation = 0 | |
self.sytle_grid() | |
def paint(self, canvas_name): | |
for x in range(0,4): | |
for y in range(0,4): | |
paint_block(canvas_name, x, y, self.grid[y][x]) | |
# Replace with function ? | |
def paint_block_on_grid(): | |
print("paint_block_on_grid") | |
for x in range(-1,5): | |
for y in range(-1,5): | |
if ((x == -1 or x == 4) \ | |
or (y == -1 or y == 4)) \ | |
or (current_block.grid[y][x]<=BLOCK_HARD): | |
n = play_grid.grid[int(current_block.y + y)][int(current_block.x + x)] | |
else: | |
n = current_block.grid[y][x] | |
paint_block("grid", int(x+current_block.x), int(y+current_block.y), n) | |
def clash_blocks(): | |
print("clash_blocks") | |
clash = False | |
for x in range(0,4): | |
for y in range(0,4): | |
b = current_block.grid[y][x] + play_grid.grid[int(y+current_block.y)][int(x+current_block.x)] | |
if b>CLASH: | |
clash = True | |
return clash | |
def freeze_current_block(): | |
print("freeze_current_block") | |
for x in range(0,4): | |
for y in range(0,4): | |
if current_block.grid[y][x] > BLOCK_HARD: | |
play_grid.grid[int(y+current_block.y)][int(x+current_block.x)] = current_block.grid[y][x] | |
def test_new_position(movement): | |
if movement == "left": | |
current_block.x = current_block.x -1 | |
elif movement == "right": | |
current_block.x = current_block.x +1 | |
elif movement == "down": | |
current_block.y = current_block.y -1 | |
elif movement == "rotate_c": | |
current_block.rotate_clock() | |
else: | |
pass | |
if clash_blocks(): | |
if movement == "left": | |
current_block.x = current_block.x +1 | |
return False | |
if movement == "right": | |
current_block.x = current_block.x -1 | |
return False | |
if movement == "down": | |
current_block.y = current_block.y +1 | |
return False | |
if movement == "rotate_c": | |
current_block.rotate_anticlock() | |
return False | |
else: | |
paint_block_on_grid() | |
return True | |
def key_code(ev): | |
# Debug | |
#trace = document["traceKeyCode"] | |
#trace.text = f'event {ev.type}, keyCode : {ev.keyCode}' | |
ev.stopPropagation() | |
# Key codes for Up, Down, Left, Right, wasd | |
if ev.keyCode == 37 or ev.keyCode == 65: | |
test_new_position("left") | |
if ev.keyCode == 39 or ev.keyCode == 68: | |
test_new_position("right") | |
if ev.keyCode == 40 or ev.keyCode == 83: | |
test_new_position("down") | |
if ev.keyCode == 38 or ev.keyCode == 87: | |
test_new_position("rotate_c") | |
def stop_timer(ev): | |
browser.timer.clear_interval(tick_timer) | |
def start_timer(ev): | |
tick_timer = browser.timer.set_interval(tick, 500) | |
def tick(): | |
global current_block, next_block, play_grid | |
if not test_new_position("down"): | |
freeze_current_block() | |
play_grid.remove_complete_lines(current_block.y) | |
if not play_grid.remove_complete_lines(current_block.y) and \ | |
not current_block.y < (h - boarder_size): | |
browser.timer.clear_interval(tick_timer) | |
alert("Game Over") | |
current_block = next_block | |
next_block = Block() | |
next_block.paint("next") | |
play_grid.draw_grid() | |
def init(): | |
global tick_timer | |
element = document["grid"] | |
element.width = w*block_size | |
element.height = h*block_size | |
element = document["next"] | |
element.width = block_size*6 | |
element.height = block_size*4 | |
if not document["grid"].getContext: | |
alert('An error occured creating a Canvas 2D context. ' | |
'This may be because you are using an old browser') | |
play_grid.draw_grid() | |
update_lines_complete(0) | |
next_block.paint("next") | |
tick_timer = browser.timer.set_interval(tick, 500) | |
document["stop"].bind('click',stop_timer) | |
document.onkeydown = key_code | |
play_grid = PlayingGrid() | |
play_grid.draw_grid() | |
current_block = Block() | |
next_block = Block() | |
init() |
This file contains 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
# Ggame | |
from ggame import App, ImageAsset, Sprite, MouseEvent | |
from random import random, randint | |
from browser import document as doc | |
from browser import html | |
import math | |
# 引入既有的id="graphics-column" 標註 | |
graphics_column = doc["graphics-column"] | |
# 建立內定名稱為 "ggame-canvas" 的 canvas 標註 | |
canvas = html.CANVAS(width = 600, height = 400) | |
canvas.id = "ggame-canvas" | |
# 將 canvas 插入 gc 標註中 | |
graphics_column <= canvas | |
class Bunny(Sprite): | |
asset = ImageAsset("/images/bunny.png") | |
def __init__(self, position): | |
super().__init__(Bunny.asset, position) | |
# register mouse events | |
App.listenMouseEvent(MouseEvent.mousedown, self.mousedown) | |
App.listenMouseEvent(MouseEvent.mouseup, self.mouseup) | |
App.listenMouseEvent(MouseEvent.mousemove, self.mousemove) | |
self.dragging = True | |
self.deltax = 0 | |
self.deltay = 0 | |
def step(self): | |
# Every now and then a bunny hops... | |
if random() < 0.01: | |
self.x += randint(-20,20) | |
self.y += randint(-20,20) | |
def mousedown(self, event): | |
# capture any mouse down within 50 pixels | |
self.deltax = event.x - (self.x + self.width//2) | |
self.deltay = event.y - (self.y + self.height//2) | |
if abs(self.deltax) < 50 and abs(self.deltay) < 50: | |
self.dragging = True | |
# only drag one bunny at a time - consume the event | |
event.consumed = True | |
def mousemove(self, event): | |
if self.dragging: | |
self.x = event.x - self.deltax - self.width//2 | |
self.y = event.y - self.deltay - self.height//2 | |
event.consumed = True | |
def mouseup(self, event): | |
if self.dragging: | |
self.dragging = False | |
event.consumed = True | |
class DemoApp(App): | |
def __init__(self): | |
super().__init__() | |
for i in range(5): | |
Bunny((randint(50, 600), randint(50, 400))) | |
def step(self): | |
# Override step to perform action on each frame update | |
for bunny in self.spritelist: | |
bunny.step() | |
# Create the app | |
app = DemoApp() | |
# Run the app | |
app.run() |
This file contains 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
# Spur Gear in Cango and gearUtils-09.js | |
from browser import document as doc | |
from browser import html | |
from browser import window | |
import browser.timer | |
import math | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 400) | |
# 將 canvas 標註的 id 設為 "cango_gear" | |
canvas.id = "cango_gear" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div2 所在位置 | |
brython_div <= canvas | |
# 將頁面中 id 為 cango_gear 的 CANVAS 設為與 canvas 對應 | |
canvas = doc["cango_gear"] | |
# convert Javascript objects to Brython variables | |
cango = window.Cango.new | |
circle = window.circle.new | |
shape = window.Shape.new | |
path = window.Path.new | |
creategeartooth = window.createGearTooth.new | |
svgsegs = window.SVGsegs.new | |
# 經由 Cango 轉換成 Brython 的 cango | |
# 指定將圖畫在 id="cango_gear" 的 canvas 上 | |
cgo = cango("cango_gear") | |
# 以下將要使用 gearUtils-09.js 畫出正齒輪外形 | |
# 假設齒數為 25 | |
num = 25 | |
# 利用 gearUtils-09 產生單一齒輪外形資料 | |
tooth = creategeartooth(10, num, 20) | |
# 在 Cango 中, 只有 SVG 才能 rotate, appendPath 或 joinPath | |
# 將齒輪外形轉為 SVG segment | |
toothSVG = svgsegs(tooth) | |
path1 = path(toothSVG.scale(1), {"degs": 45, "x": 100, "y": 100, "strokeColor": "#606060"}) | |
#print(path1) | |
# SVG list | |
circle = circle(50) | |
#print(circle) | |
circleSVG = svgsegs(circle) | |
#print(circleSVG) | |
# 若將 circleSVG 轉為 Cango path, 則可以用 cgo.render() | |
#circlePath = path(circleSVG, {"x": 100, "y": 100, "strokeColor": "#606060"}) | |
#cgo.render(circlePath) | |
# svgsegs 資料可以 joinPath 或 appendPath | |
# joinPath 按照頭尾順序銜接 | |
# appendPath 則無順序銜接 | |
# 從 toothSVG 複製出單齒 SVG 資料 | |
one = toothSVG.dup() | |
# 以照齒數, 逐一複製並附加在原單齒資料中 | |
# 第一齒的資料已經在 toothSVG 中, 因此重複迴圈從 1 開始 | |
for i in range(1, num): | |
newSVG = one.rotate(360*i/num) | |
toothSVG = toothSVG.appendPath(newSVG) | |
# 將 SVG 轉為 path 資料 | |
#gear = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"}) | |
# path 資料可以透過 cgo.render()顯示繪圖物件 | |
#cgo.render(gear) | |
# 當 circle 接外齒使用 appendPath | |
toothSVG = toothSVG.appendPath(circleSVG) | |
#print(toothSVG) | |
spurPath = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"}) | |
cgo.render(spurPath) |
This file contains 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
# Spur Gear in Cango and gearUtils-09.js | |
from browser import document as doc | |
from browser import html | |
from browser import window | |
import browser.timer | |
import math | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 400) | |
# 將 canvas 標註的 id 設為 "cango_gear" | |
canvas.id = "cango_gear" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div2 所在位置 | |
brython_div <= canvas | |
# 將頁面中 id 為 cango_gear 的 CANVAS 設為與 canvas 對應 | |
canvas = doc["cango_gear"] | |
# convert Javascript objects to Brython variables | |
cango = window.Cango.new | |
circle = window.circle.new | |
shape = window.Shape.new | |
path = window.Path.new | |
creategeartooth = window.createGearTooth.new | |
svgsegs = window.SVGsegs.new | |
# 經由 Cango 轉換成 Brython 的 cango | |
# 指定將圖畫在 id="cango_gear" 的 canvas 上 | |
cgo = cango("cango_gear") | |
# 以下將要使用 gearUtils-09.js 畫出正齒輪外形 | |
# 假設齒數為 25 | |
num = 25 | |
# 利用 gearUtils-09 產生單一齒輪外形資料 | |
tooth = creategeartooth(10, num, 20) | |
# 在 Cango 中, 只有 SVG 才能 rotate, appendPath 或 joinPath | |
# 將齒輪外形轉為 SVG segment | |
toothSVG = svgsegs(tooth) | |
path1 = path(toothSVG.scale(1), {"degs": 45, "x": 100, "y": 100, "strokeColor": "#606060"}) | |
#print(path1) | |
# SVG list | |
circle = circle(50) | |
#print(circle) | |
circleSVG = svgsegs(circle) | |
#print(circleSVG) | |
# 若將 circleSVG 轉為 Cango path, 則可以用 cgo.render() | |
#circlePath = path(circleSVG, {"x": 100, "y": 100, "strokeColor": "#606060"}) | |
#cgo.render(circlePath) | |
# svgsegs 資料可以 joinPath 或 appendPath | |
# joinPath 按照頭尾順序銜接 | |
# appendPath 則無順序銜接 | |
# 從 toothSVG 複製出單齒 SVG 資料 | |
one = toothSVG.dup() | |
# 以照齒數, 逐一複製並附加在原單齒資料中 | |
# 第一齒的資料已經在 toothSVG 中, 因此重複迴圈從 1 開始 | |
for i in range(1, num): | |
newSVG = one.rotate(360*i/num) | |
toothSVG = toothSVG.appendPath(newSVG) | |
# 將 SVG 轉為 path 資料 | |
#gear = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"}) | |
# path 資料可以透過 cgo.render()顯示繪圖物件 | |
#cgo.render(gear) | |
# 當 circle 接外齒使用 appendPath | |
toothSVG = toothSVG.appendPath(circleSVG) | |
#print(toothSVG) | |
spurPath = path(toothSVG, {"x": 150, "y": 150, "strokeColor": "#606060"}) | |
cgo.render(spurPath) |
This file contains 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
# Cango gearUtils-0.9.js Spur Gears | |
from browser import document as doc | |
from browser import html | |
from browser import window | |
import browser.timer | |
import math | |
canvas = html.CANVAS(width = 600, height = 400) | |
canvas.id = "cango_gear" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
canvas = doc["cango_gear"] | |
# 此程式採用 Cango Javascript 程式庫繪圖, 因此無需 ctx | |
#ctx = canvas.getContext("2d") | |
cango = window.Cango.new | |
path = window.Path.new | |
creategeartooth = window.createGearTooth.new | |
circle = window.circle.new | |
svgsegs = window.SVGsegs.new | |
# 經由 Cango 轉換成 Brython 的 cango, 指定將圖畫在 id="cango_gear" 的 canvas 上 | |
cgo = cango("cango_gear") | |
###################################### | |
# 畫正齒輪輪廓 | |
##################################### | |
def cangoGear(n, m, pa, x=0, y=0, color="#606060"): | |
# n 為齒數 | |
#n = 17 | |
# pa 為壓力角 | |
#pa = 25 | |
# m 為模數, 根據畫布的寬度, 計算適合的模數大小 | |
# Module = mm of pitch diameter per tooth | |
#m = 0.8*canvas.width/n | |
# pr 為節圓半徑 | |
pr = n*m/2 # gear Pitch radius | |
# generate gear data | |
data = creategeartooth(m, n, pa) | |
toothSVG = svgsegs(data) | |
toothSVG.rotate(180/n) # rotate gear 1/2 tooth to mesh | |
# 單齒的齒形資料經過旋轉後, 將資料複製到 gear 物件中 | |
one = toothSVG.dup() | |
# 利用單齒輪廓旋轉, 產生整個正齒輪外形 | |
for i in range(1, n): | |
newSVG = one.rotate(360*i/n) | |
toothSVG = toothSVG.appendPath(newSVG) | |
# 建立軸孔 | |
# add axle hole, hr 為 hole radius | |
hr = 0.6*pr # diameter of gear shaft | |
shaft = circle(hr) | |
shaftSVG = svgsegs(shaft) | |
spurSVG = toothSVG.appendPath(shaftSVG) | |
gear = path(spurSVG, {"x": x, "y": y, "strokeColor": color}) | |
return gear | |
# 設定兩齒齒數 | |
n1 = 84 | |
n2 = 18 | |
n3 = 99 | |
# 使用 80% 的畫布寬度 | |
m = 0.8*canvas.width/((n1+n2+n3)) | |
# 設定共同的壓力角 | |
pa = 25 | |
# n 齒輪的節圓半徑 | |
pr1 = n1*m/2 | |
# n2 齒輪的節圓半徑 | |
pr2 = n2*m/2 | |
pr3 = n3*m/2 | |
cx = canvas.width/2 | |
cy = canvas.height/2 | |
# Determine the coord of the middle gears | |
mcx = cx + (pr1-pr3) | |
mcy = cy | |
# 建立 gears | |
gear1 = cangoGear(n1, m, pa, color="red") | |
gear2 = cangoGear(n2, m, pa, color="green") | |
gear3 = cangoGear(n3, m, pa, color="blue") | |
deg = math.pi/180 | |
rotate_speed = 0 | |
def draw(): | |
global rotate_speed | |
rotate_speed += 5*deg | |
cgo.clearCanvas() | |
theta1 = 0+rotate_speed | |
gear1.rotate(theta1) | |
gear1.translate(mcx-(pr1+pr2), mcy) | |
cgo.render(gear1) | |
theta2 = 180+(360/n2/2)-(rotate_speed)*n1/n2 | |
gear2.rotate(theta2) | |
gear2.translate(mcx, mcy) | |
cgo.render(gear2) | |
theta3 = 180+(360/n3/2)+(180+(360/n2/2))*n2/n3+(rotate_speed*n1/n2)*(n2/n3) | |
gear3.rotate(theta3) | |
gear3.translate(mcx+(pr2+pr3), mcy) | |
cgo.render(gear3) | |
browser.timer.set_interval(draw, 2) |
This file contains 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
# 這個程式用於 demo 綠色方塊往隨機產生的紅色方塊位置移動 | |
# 此程式並未計算各紅色方塊與綠色方塊的距離, 僅按照隨機排序移動 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 導入亂數模組 | |
from random import random, randint | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c default 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
# 從兩個座標點求中心點座標 | |
def center(lx, ly, rx, ry): | |
# lx is x coord of the left up corner | |
# rx is the x coord of th right down corner | |
x = (lx + rx)/2 | |
y = (ly + ry)/2 | |
return x, y | |
# 畫出填色方塊 | |
def draw_rect(gx, gy, gw, gh, color="lime"): | |
# gx is the grid coord at x direction | |
# gy is the grid coord at y direction | |
# gw is the width of the green rect | |
# gh is the height of the green rect | |
lx = origx + (gx-1)*w | |
ly = origy + (gy-1)*h | |
rx = origx + gx*w | |
ry = origy + gy*h | |
cx, cy = center(lx, ly, rx, ry) | |
# glx is the x coord of the left corner | |
# gly is the y coord of the left corner | |
glx = cx - gw/2 | |
gly = cy - gh/2 | |
# 利用設定的顏色值畫出 rectangle | |
ctx.fillStyle = color | |
ctx.fillRect(glx, gly, gw, gh) | |
# 以白色覆蓋位於 (nowx, nowy) | |
# 且比目標方塊長寬各大於 1 pixel的方塊 | |
def wipe(): | |
draw_rect(nowx, nowy, 30+1, 30+1, color="white") | |
# 畫出位於 (nowx, nowy) 的綠色方塊 | |
def draw(): | |
draw_rect(nowx, nowy, 30, 30, color="lime") | |
# 以隨機方式在格點座標中畫出紅色方塊 | |
def draw_red(x, y): | |
draw_rect(x, y, wrect_size, hrect_size, color="red") | |
# 綠色方塊往紅色方塊位置移動, 抵達目標位置後停止移動 | |
def walk(): | |
global stepx, stepy | |
if nowx > redx: | |
stepx = -1 | |
stepy = 0 | |
if nowx < redx: | |
stepx = 1 | |
stepy = 0 | |
if nowy > redy: | |
stepx = 0 | |
stepy = -1 | |
if nowy < redy: | |
stepx = 0 | |
stepy = 1 | |
if nowx == redx and nowy == redy: | |
stepx = 0 | |
stepy = 0 | |
# 每隔短暫時間即呼叫執行一次的函式 | |
def game(): | |
# 因 nowx, nowy, redx, redy 在函式外宣告 | |
# 且在函式內改變對應值, 因此需宣告為 global | |
global nowx, nowy, redx, redy | |
# 當綠色方塊移動至紅色方塊座標後, 逐一取出另一個紅色目標座標值 | |
if nowx == redx and nowy == redy: | |
# 利用 pop() 逐一取出 coord 中的座標值 pos | |
# coord 取至最後一個數列後, 即跳至 pass | |
try: | |
pos = coord.pop() | |
# 索引 0 為 x 座標, 1 為 y 座標 | |
redx = pos[0] | |
redy = pos[1] | |
wText(int((redx-0.5)*w), int((redy-0.5)*h), "O") | |
except: | |
# 以隨機方產生 5 個座標值 | |
for i in range(5): | |
# wnum 為 width 方向的格子數 | |
# hnum 為 height 方向的格子數 | |
x = randint(1, wnum) | |
y = randint(1, hnum) | |
# 逐一在座標位置畫出紅色方塊 | |
draw_red(x, y) | |
# 將座標值以數列方式放入 coord 數列 | |
coord.append([x, y]) | |
walk() | |
wipe() | |
nowx += stepx | |
nowy += stepy | |
draw() | |
# 綠色方塊起點座標與 x 及 y 方向的座標增量 | |
nowx = 1 | |
nowy = 1 | |
stepx = 0 | |
stepy = 0 | |
go = True | |
# 設定格數 | |
# width 方向格子數 | |
wnum = 15 | |
# height 方向格子數 | |
hnum = 15 | |
# 紅色方塊座標起始座標位於右下角 | |
redx = wnum-1 | |
redy = hnum-1 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/wnum) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/hnum) - pixel | |
# 設定紅色方塊寬度與高度, 分別設為格子大小的 70% | |
wrect_size = int(w*0.7) | |
hrect_size = int(h*0.7) | |
# 設定繪圖座標點起點位置 | |
origx = 1 | |
origy = 1 | |
# 利用 grid 函式畫出格線 | |
grid(origx, origy, w, h, wnum, hnum, pixel=1, color="black") | |
# 宣告 coord 為數列 | |
coord = [] | |
# 以隨機方產生 5 個座標值 | |
for i in range(5): | |
# wnum 為 width 方向的格子數 | |
# hnum 為 height 方向的格子數 | |
x = randint(1, wnum) | |
y = randint(1, hnum) | |
# 逐一在座標位置畫出紅色方塊 | |
draw_red(x, y) | |
# 將座標值以數列方式放入 coord 數列 | |
coord.append([x, y]) | |
browser.timer.set_interval(game, 100) |
This file contains 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
# 利用 input() 取使用者輸入, 而且利用輸入字串說明要求 | |
input_str = input("請輸入至少8個字元字串, 包含大小寫英文字母, 數字以及特殊符號") | |
# from https://stackoverflow.com/questions/17140408/if-statement-to-check-whether-a-string-has-a-capital-letter-a-lower-case-letter/17140466 | |
# and https://stackoverflow.com/questions/57062794/how-to-check-if-a-string-has-any-special-characters/57062899 | |
# 宣告 sp_char 為必須包含的特殊字元 | |
sp_char = "!@#$%^&*()-+?_=,<>/" | |
# 定義一個查驗函式, 必須分別符合輸入要求的字串才會傳回 True | |
def check_str(s): | |
return (any(x.isupper() for x in s) and any(x.islower() for x in s) and any(x.isdigit() for x in s) and len(s) >= 8 and any(x in sp_char for x in s)) | |
# 當使用者輸入不符合要求時, 無法跳出輸入迴圈 | |
while check_str(input_str) != True: | |
input_str = input("請輸入至少8個字元字串, 包含大小寫英文字母, 數字以及特殊符號") | |
# 一旦使用者輸入符合要求的字串後 (即 check_str(input_str) 傳回 True, 則印出使用者的輸入字串 | |
print("你輸入符合\"至少8個字元字串, 包含大小寫英文字母, 數字以及特殊符號\"要求的字串為:", input_str) |
This file contains 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
# clock1 in Brython - 這是單行註解 | |
# 以下則是多行註解 | |
'''Code for the clock''' | |
# Line drawing | |
# 導入 doc | |
from browser import document as doc # 從模組 browser 導入 document 並且命名為 doc | |
from browser import html # 導入 html | |
import math # 導入數學模組 | |
import time # 導入時間模組 | |
import browser.timer # 導入用來製作 animation 動態模擬用的計時器 | |
# 以下有兩種方式在頁面建立 id 為 line_drawing 的標註 | |
# 假如網頁中已經存在 id 為 line_drawing 的 canvas, 則可以直接透過 document 指定 id 名稱取用 | |
# 若網頁中沒有事先建立此一 canvas, 則可以透過 Brython 的 html 類別建立 | |
canvas = html.CANVAS(width = 300, height = 200) | |
canvas.id = "line_drawing" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
# 準備繪圖畫布 | |
canvas = doc["line_drawing"] | |
ctx = canvas.getContext("2d") | |
sin, cos = math.sin, math.cos | |
width, height = 200, 200 # canvas dimensions | |
ray = 100 # clock ray | |
background = "SteelBlue" | |
digits = "#fff" | |
border = "blue" | |
def needle(angle, r1, r2): | |
'''Draw a needle at specified angle in specified color. | |
r1 and r2 are percentages of clock ray. | |
''' | |
x1 = width / 2 - ray * cos(angle) * r1 | |
y1 = height / 2 - ray * sin(angle) * r1 | |
x2 = width / 2 + ray * cos(angle) * r2 | |
y2 = height / 2 + ray * sin(angle) * r2 | |
ctx.beginPath() | |
ctx.strokeStyle = "#fff" | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.stroke() | |
def set_clock(): | |
# erase clock | |
ctx.beginPath() | |
ctx.fillStyle = background | |
ctx.arc(width / 2, height / 2, ray * 0.89, 0, 2 * math.pi) | |
ctx.fill() | |
# redraw hours | |
show_hours() | |
# print day | |
now_time = time.time() | |
now = time.localtime(now_time) | |
microsecs = now_time - int(now_time) | |
day = now.tm_mday | |
ctx.font = "bold 14px Arial" | |
ctx.textAlign = "center" | |
ctx.textBaseline = "middle" | |
ctx.fillStyle = "#000" | |
ctx.fillText(day, width * 0.7, height * 0.5) | |
# draw needles for hour, minute, seconds | |
ctx.lineWidth = 2 | |
hour = now.tm_hour % 12 + now.tm_min / 60 | |
angle = hour * 2 * math.pi / 12 - math.pi / 2 | |
needle(angle, 0.05, 0.45) | |
minute = now.tm_min | |
angle = minute * 2 *math.pi / 60 - math.pi / 2 | |
needle(angle, 0.05, 0.7) | |
ctx.lineWidth = 1 | |
second = now.tm_sec + microsecs | |
angle = second * 2 * math.pi / 60 - math.pi / 2 | |
needle(angle, 0.05, 0.8) | |
def show_hours(): | |
ctx.beginPath() | |
ctx.arc(width / 2, height / 2, ray * 0.05, 0, 2 * math.pi) | |
ctx.fillStyle = digits | |
ctx.fill() | |
for i in range(1, 13): | |
angle = i * math.pi / 6 - math.pi / 2 | |
x3 = width / 2 + ray * cos(angle) * 0.82 | |
y3 = height / 2 + ray * sin(angle) * 0.82 | |
ctx.font = "18px Arial" | |
ctx.textAlign = "center" | |
ctx.textBaseline = "middle" | |
ctx.fillText(i, x3, y3) | |
# cell for day | |
ctx.fillStyle = "#fff" | |
ctx.fillRect(width * 0.65, height * 0.47, width * 0.1, height * 0.06) | |
ctx.beginPath() | |
ctx.arc(width / 2, height / 2, ray, 0, 2 * math.pi) | |
ctx.fillStyle = background | |
ctx.fill() | |
# 每 100 micro seconds (0.1 second) 執行一次 set_clock 函式 | |
browser.timer.set_interval(set_clock, 100) | |
show_hours() |
This file contains 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
# Temperature Conversion | |
# Temperature Conversion | |
''' | |
C to F: Celsius to Fahrenheit Conversion Formula. To convert temperatures in degrees Celsius to Fahrenheit, multiply by 1.8 (or 9/5) and add 32. | |
To convert temperatures in degrees Fahrenheit to Celsius, subtract 32 and multiply by .5556 (or 5/9). | |
Fahrenheit = Celsius*9/5 + 32 | |
Celsium = (Fahrenheit - 32)*5/9 | |
''' | |
from browser import document, html | |
brython_div = document["brython_div"] | |
def c2f(c): | |
f = round(c*9/5 + 32, 3) | |
return "Celsiusc: "+ str(c) + " degrees = Fahrenheit: " + str(f) + " degrees" | |
def f2c(f): | |
c = round((f - 32)*5/9, 3) | |
return "Fahrenheit: "+ str(f) + " degrees = Celsiusc: " + str(c) + " degrees" | |
choice = input("c2f or f2c") | |
if choice == "c2f": | |
''' | |
c = float(input("input Celsius in degrees")) | |
print(c2f(c)) | |
''' | |
for deg in range(100): | |
brython_div <= c2f(deg) | |
brython_div <= html.BR() | |
else: | |
''' | |
f = float(input("input Fahrenheit in degrees")) | |
print(f2c(f)) | |
''' | |
for deg in range(100): | |
brython_div <= f2c(deg) | |
brython_div <= html.BR() |
This file contains 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
# source: https://github.com/clarketm/mergedeep | |
from collections import Counter | |
from collections.abc import Mapping | |
from copy import deepcopy | |
from enum import Enum | |
from functools import reduce, partial | |
from typing import MutableMapping | |
class Strategy(Enum): | |
# Replace `destination` item with one from `source` (default). | |
REPLACE = 0 | |
# Combine `list`, `tuple`, `set`, or `Counter` types into one collection. | |
ADDITIVE = 1 | |
# Alias to: `TYPESAFE_REPLACE` | |
TYPESAFE = 2 | |
# Raise `TypeError` when `destination` and `source` types differ. Otherwise, perform a `REPLACE` merge. | |
TYPESAFE_REPLACE = 3 | |
# Raise `TypeError` when `destination` and `source` types differ. Otherwise, perform a `ADDITIVE` merge. | |
TYPESAFE_ADDITIVE = 4 | |
def _handle_merge_replace(destination, source, key): | |
if isinstance(destination[key], Counter) and isinstance(source[key], Counter): | |
# Merge both destination and source `Counter` as if they were a standard dict. | |
_deepmerge(destination[key], source[key]) | |
else: | |
# If a key exists in both objects and the values are `different`, the value from the `source` object will be used. | |
destination[key] = deepcopy(source[key]) | |
def _handle_merge_additive(destination, source, key): | |
# Values are combined into one long collection. | |
if isinstance(destination[key], list) and isinstance(source[key], list): | |
# Extend destination if both destination and source are `list` type. | |
destination[key].extend(deepcopy(source[key])) | |
elif isinstance(destination[key], set) and isinstance(source[key], set): | |
# Update destination if both destination and source are `set` type. | |
destination[key].update(deepcopy(source[key])) | |
elif isinstance(destination[key], tuple) and isinstance(source[key], tuple): | |
# Update destination if both destination and source are `tuple` type. | |
destination[key] = destination[key] + deepcopy(source[key]) | |
elif isinstance(destination[key], Counter) and isinstance(source[key], Counter): | |
# Update destination if both destination and source are `Counter` type. | |
destination[key].update(deepcopy(source[key])) | |
else: | |
_handle_merge[Strategy.REPLACE](destination, source, key) | |
def _handle_merge_typesafe(destination, source, key, strategy): | |
# Raise a TypeError if the destination and source types differ. | |
if type(destination[key]) is not type(source[key]): | |
raise TypeError( | |
f'destination type: {type(destination[key])} differs from source type: {type(source[key])} for key: "{key}"' | |
) | |
else: | |
_handle_merge[strategy](destination, source, key) | |
_handle_merge = { | |
Strategy.REPLACE: _handle_merge_replace, | |
Strategy.ADDITIVE: _handle_merge_additive, | |
Strategy.TYPESAFE: partial(_handle_merge_typesafe, strategy=Strategy.REPLACE), | |
Strategy.TYPESAFE_REPLACE: partial(_handle_merge_typesafe, strategy=Strategy.REPLACE), | |
Strategy.TYPESAFE_ADDITIVE: partial(_handle_merge_typesafe, strategy=Strategy.ADDITIVE), | |
} | |
def _is_recursive_merge(a, b): | |
both_mapping = isinstance(a, Mapping) and isinstance(b, Mapping) | |
both_counter = isinstance(a, Counter) and isinstance(b, Counter) | |
return both_mapping and not both_counter | |
def _deepmerge(dst, src, strategy=Strategy.REPLACE): | |
for key in src: | |
if key in dst: | |
if _is_recursive_merge(dst[key], src[key]): | |
# If the key for both `dst` and `src` are both Mapping types (e.g. dict), then recurse. | |
_deepmerge(dst[key], src[key], strategy) | |
elif dst[key] is src[key]: | |
# If a key exists in both objects and the values are `same`, the value from the `dst` object will be used. | |
pass | |
else: | |
_handle_merge.get(strategy)(dst, src, key) | |
else: | |
# If the key exists only in `src`, the value from the `src` object will be used. | |
dst[key] = deepcopy(src[key]) | |
return dst | |
def merge(destination: MutableMapping, *sources: Mapping, strategy: Strategy = Strategy.REPLACE) -> MutableMapping: | |
""" | |
A deep merge function for 🐍. | |
:param destination: The destination mapping. | |
:param sources: The source mappings. | |
:param strategy: The merge strategy. | |
:return: | |
""" | |
return reduce(partial(_deepmerge, strategy=strategy), sources, destination) | |
# test the merge function | |
a = {"keyA": 1} | |
b = {"keyB": {"sub1": 10}} | |
c = {"keyB": {"sub2": 20}} | |
merged = merge({}, a, b, c) | |
print(merged) |
This file contains 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
# not complete yet | |
# STL viewer 原始檔案來自 | |
# University of Wuppertal - http://mbi-wiki.uni-wuppertal.de/wordpress/ | |
# Modified by Uli Eggersmann | |
# Binary STL 資料讀取原始作者 Oliver Marks - http://www.linux.com | |
# 原始檔案僅讀取 Text STL 零件檔案 | |
# 2011 Fall 由 KMOL 新增 Binary STL 零件檔案讀取 | |
from visual import scene, color, materials, faces, points | |
import os, struct | |
#file ="ritzel.stl" | |
file ="binary.stl" | |
scene.width = 400 | |
scene.height = 400 | |
scene.background = color.white # black | |
# 視窗標題取自 cvisual.pyd, 不可使用中文 | |
scene.title = "STLViewer in VPython" | |
print ("利用滑鼠右鍵旋轉") | |
print ("滑鼠左右鍵同時按下後移動, 可以縮放畫面") | |
# Read STL file, only use vertex-line with xyz coordinates | |
list = [] | |
#load stl file detects if the file is a text file or binary file | |
def load_stl(filename): | |
#read start of file to determine if its a binay stl file or a ascii stl file | |
fp=open(filename,'rb') | |
header=fp.read(80) | |
filetype=header[0:5] | |
# 這裡必須要能夠分辨二位元字串與文字字串 | |
#print (type(filetype)) | |
#print (filetype) | |
fp.close() | |
# for Python 3 | |
if filetype==b'solid': | |
# for Python 2 | |
#if filetype=='solid': | |
print ("讀取文字檔案格式:"+str(filename)) | |
load_text_stl(filename) | |
else: | |
print ("讀取二位元檔案格式:"+str(filename,)) | |
load_binary_stl(filename) | |
#load binary stl file check wikipedia for the binary layout of the file | |
#we use the struct library to read in and convert binary data into a format we can use | |
def load_binary_stl(filename): | |
''' | |
二位元 STL 檔案格式如下: | |
檔案標頭共有 80 個字元(bytes), 內容通常省略, 但是內容不可使用 solid, 以免與文字檔案 STL 混淆 | |
UINT8[80] – Header | |
UINT32 – Number of triangles (I:佔 4 bytes 的 unsigned integer) | |
foreach triangle | |
REAL32[3] – Normal vector (f:每一座標分量為一佔 4 bytes 的 float, 共佔 12 bytes) | |
REAL32[3] – Vertex 1 | |
REAL32[3] – Vertex 2 | |
REAL32[3] – Vertex 3 | |
UINT16 – Attribute byte count (H:兩個 bytes 的 unsigned short, 表示 attribute byte count) | |
end | |
''' | |
global list | |
fp=open(filename,'rb') | |
header=fp.read(80) | |
triangle_number = struct.unpack('I',fp.read(4))[0] | |
count=0 | |
while True: | |
try: | |
p=fp.read(12) | |
if len(p)==12: | |
n=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
p=fp.read(12) | |
if len(p)==12: | |
p1=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
list.append(p1) | |
p=fp.read(12) | |
if len(p)==12: | |
p2=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
list.append(p2) | |
p=fp.read(12) | |
if len(p)==12: | |
p3=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
list.append(p3) | |
# 使用 count 來計算三角形平面個數 | |
# triangle_number 為 STL 檔案中的三角形個數 | |
count += 1 | |
# 在前面 12*4 個 bytes 的 normal 與三個點資料後, 為 | |
# 一個 2 bytes 長的 unsigned short, 其值為零, 為 attribute | |
fp.read(2) | |
# 讀完所有三角平面後, 即跳出 while | |
if count > triangle_number: | |
break | |
except EOFError: | |
break | |
fp.close() | |
def load_text_stl(filename): | |
global list | |
for dataline in open(filename,"r").readlines(): | |
if not dataline.strip(): # skip blank lines | |
continue | |
field = dataline.split() # split with no argument makes the right place! | |
if field[0] == "vertex": | |
list.append([float(x) for x in field[1:4]]) | |
#print (list) | |
#break | |
#for x in field[1:4]: | |
#print(x) | |
load_stl(os.path.abspath('')+'/'+file) | |
# Graphics | |
model = faces(pos=list, color=(0.8,0.8,0.8), | |
material=materials.plastic) # creates triangles | |
# 請注意, 這裡並沒有使用 STL 檔案中的平面 normal, 而是利用 VPython make_normals() 產生 | |
model.make_normals() # creates plane normals | |
model.smooth(0.93) # smooths the edges | |
# = AllepunkteSTL points (pos = list, size = 3, color = Color.Black) # generates points |
This file contains 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
# Line drawing | |
# 導入 doc | |
from browser import document as doc | |
from browser import html | |
import math | |
canvas = html.CANVAS(width = 300, height = 200) | |
canvas.id = "line_drawing" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
# 準備繪圖畫布 | |
canvas = doc["line_drawing"] | |
ctx = canvas.getContext("2d") | |
# 進行座標轉換, x 軸不變, y 軸反向且移動 canvas.height 單位光點 | |
# ctx.setTransform(1, 0, 0, -1, 0, canvas.height) | |
# 以下採用 canvas 原始座標繪圖 | |
# 設定填圖顏色 | |
ctx.fillStyle = "rgb(200,0,0)" | |
# 設定畫筆顏色 | |
ctx.strokeStyle = "rgb(0,0,200)" | |
# 乘上 deg 可轉為徑度單位 | |
deg = math.pi / 180 | |
# 建立多邊形定點位置畫線函式 | |
def star(radius, xc, yc, n): | |
#radius = 100 | |
#xc = 200 | |
#yc = 200 | |
xi = xc + radius*math.cos((360/n)*deg+90*deg) | |
yi = yc - radius*math.sin((360/n)*deg+90*deg) | |
ctx.beginPath() | |
ctx.moveTo(xi,yi) | |
for i in range(2, n+1): | |
x = xc + radius*math.cos((360/n)*deg*i+90*deg) | |
y = yc - radius*math.sin((360/n)*deg*i+90*deg) | |
ctx.lineTo(x,y) | |
# 以下利用多邊形畫線函式呼叫執行畫框線或填入顏色 | |
# 畫五邊形框線 | |
star(50, 50, 50, 5) | |
ctx.closePath() | |
ctx.stroke() | |
# 填三角形色塊 | |
star(50, 150, 50, 3) | |
ctx.closePath() | |
ctx.fill() | |
# 改變畫線顏色後, 畫七邊形框線 | |
ctx.strokeStyle = "rgb(0,200,20)" | |
star(50, 250, 50, 7) | |
ctx.closePath() | |
ctx.stroke() |
This file contains 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
# 這個程式用於 demo 以 canvas 畫線以及寫字 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c defaul 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
# 設定格數 | |
num = 15 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/num) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/num) - pixel | |
# 設定繪圖座標點起點位置 | |
x = 1 | |
y = 1 | |
# 利用 grid 函式畫出格線 | |
grid(x, y, w, h, num, num, pixel=1, color="black") |
This file contains 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
# 這個程式用於 demo 以 canvas 畫線以及寫字 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 導入亂數模組 | |
from random import random, randint | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c defaul 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
def center(lx, ly, rx, ry): | |
# lx is x coord of the left up corner | |
# rx is the x coord of th right down corner | |
x = (lx + rx)/2 | |
y = (ly + ry)/2 | |
return x, y | |
def draw_rect(gx, gy, gw, gh, color="lime"): | |
# gx is the grid coord at x direction | |
# gy is the grid coord at y direction | |
# gw is the with of the green rect | |
# gh is the height of the green rect | |
lx = origx + (gx-1)*w | |
ly = origy + (gy-1)*h | |
rx = origx + gx*w | |
ry = origy + gy*h | |
cx, cy = center(lx, ly, rx, ry) | |
# glx is the x coord of the left corner | |
# gly is the y coord of the left corner | |
glx = cx - gw/2 | |
gly = cy - gh/2 | |
# 利用設定的顏色值畫出 rectangle | |
ctx.fillStyle = color | |
ctx.fillRect(glx, gly, gw, gh) | |
# 設定格數 | |
# width 方向格子數 | |
wnum = 15 | |
# height 方向格子數 | |
hnum = 15 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/wnum) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/hnum) - pixel | |
# 設定繪圖座標點起點位置 | |
origx = 1 | |
origy = 1 | |
# 利用 grid 函式畫出格線 | |
grid(origx, origy, w, h, wnum, hnum, pixel=1, color="black") | |
# 利用 draw_rect 以 grid 座標畫出正方形, 內建為 lime | |
draw_rect(3, 3, 30, 30) | |
# 利用隨機亂數產生五個紅色方塊 | |
# wnum 與 hnum 為 x 與 y 方向的格子個數 | |
# w 與 h 方向上的方塊 pixel 數 | |
wrect_size = 30 | |
hrect_size = 30 | |
# 利用 for 迴圈產生五個紅色方塊 | |
for i in range(5): | |
xcoord = randint(1, wnum) | |
ycoord = randint(1, hnum) | |
draw_rect(xcoord, ycoord, wrect_size, hrect_size, color="red") |
This file contains 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
# import from downloads/py/fibo.py | |
import fibo | |
fibo.fib(5) | |
print(fibo.fib2(5)) |
This file contains 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
# make canvas 600x400 | |
from browser import document as doc | |
from browser import window | |
from browser import timer | |
from browser import html | |
import math | |
# 建立 fourbar canvas | |
canvas = html.CANVAS(width = 600, height = 400) | |
canvas.id = "fourbar" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
# 準備繪圖畫布 | |
canvas = doc["fourbar"] | |
# 建立 buttons | |
brython_div <= html.BUTTON("啟動", id="power") | |
brython_div <= html.BUTTON("反向", id="reverse") | |
# 利用 window 擷取 PrairieDraw 程式庫變數物件, 然後以 JSConstructor 函式轉為 Brython 變數 | |
pdraw = window.PrairieDraw.new | |
# 利用 window 擷取 PrairieDrawAnim 程式庫變數物件, 然後以 JSConstructor 函式轉為 Brython 變數 | |
PrairieDrawAnim = window.PrairieDrawAnim.new | |
# 利用 window 擷取 sylvester 程式庫變數物件 Vector, 並將其 create 方法直接轉為 Brython 變數 | |
# 在 sylvester 中的 $V 簡化變數無法直接在 Brython 程式中引用 | |
vector = window.Vector.create.new | |
# 在 "fourbar" 畫布中建立 panim 動態模擬案例 | |
panim = PrairieDrawAnim("fourbar") | |
# 平面連桿繪圖以 t = 0 起始 | |
t = 0 | |
# 控制轉動方向變數 | |
direction = True | |
# 繪製不同 t 時間下的平面連桿 | |
def draw(): | |
global t, direction, fast | |
# 設定模擬繪圖範圍 | |
panim.setUnits(6, 6) | |
# 設定箭頭線寬 | |
panim.setProp("arrowLineWidthPx",2) | |
# 起始變數設定 | |
omega = 1 | |
length_bar1 = 1 | |
length_bar2 = 26/18 | |
length_bar3 = 2 | |
length_base = 40/18 | |
time = 0 | |
# 畫出地面直線 | |
G = vector([0, -0.5]) | |
panim.ground(G, vector([0, 1]), 10) | |
# 連桿長度與角度計算 | |
A = t*omega # "theta" | |
AD = length_bar1 #length of left bar | |
AB = length_base #distance between two stationary pivots | |
BC = length_bar3 #length of right bar | |
CD = length_bar2 #length of middle bar | |
BD = math.sqrt(AD*AD + AB*AB - 2*AD*AB*math.cos(A)) | |
C = math.acos((BC*BC + CD*CD - BD*BD)/(2*BC*CD)) | |
ABD = math.asin(CD * math.sin(C) / BD) | |
DBC = math.asin(AD * math.sin(A) / BD) | |
B = ABD + DBC | |
D = math.pi - B - C | |
# draw pivot | |
pivot_left = vector([AB/-2, 0]) | |
pivot_right = vector([AB/2, 0]) | |
panim.pivot(vector([pivot_left.e(1), -0.5]), pivot_left, 0.5) | |
panim.pivot(vector([pivot_right.e(1), -0.5]), pivot_right, 0.5) | |
# 儲存轉換矩陣 | |
panim.save() | |
#FIRST BAR | |
panim.translate(pivot_left) | |
panim.rotate(A) | |
panim.rod(vector([0,0]), vector([AD,0]), 0.25) | |
panim.point(vector([0,0])) | |
#SECOND BAR | |
panim.translate(vector([AD,0])) | |
panim.rotate(A*-1) #"undo" the original A rotation | |
panim.rotate(D) #rotate by D only | |
panim.rod(vector([0,0]), vector([CD,0]), 0.25) | |
panim.point(vector([0,0])) | |
#THIRD BAR | |
panim.translate(vector([CD,0])) | |
panim.rotate(math.pi+C) | |
panim.rod(vector([0,0]), vector([BC,0]), 0.25) | |
panim.point(vector([0,0])) | |
# 回復原先的轉換矩陣 | |
panim.restore() | |
panim.point(vector([pivot_right.e(1), 0])) | |
# 時間增量 | |
if direction == True: | |
t += 0.08 | |
else: | |
t += -0.08 | |
# 先畫出 t = 0 的連桿機構 | |
draw() | |
# 將 anim 設為 None | |
anim = None | |
def launchAnimation(ev): | |
global anim | |
# 初始啟動, anim 為 None | |
if anim is None: | |
# 每 0.08 秒執行一次 draw 函式繪圖 | |
anim = timer.set_interval(draw, 80) | |
# 初始啟動後, 按鈕文字轉為"暫停" | |
doc['power'].text = '暫停' | |
elif anim == 'hold': | |
# 當 anim 為 'hold' 表示曾經暫停後的啟動, 因此持續以 set_interval() 持續旋轉, 且將 power 文字轉為"暫停" | |
anim = timer.set_interval(draw, 80) | |
doc['power'].text = '暫停' | |
else: | |
# 初始啟動後, 使用者再按 power, 此時 anim 非 None 也不是 'hold', 因此會執行 clear_interval() 暫停 | |
# 且將 anim 變數設為 'hold', 且 power 文字轉為"繼續" | |
timer.clear_interval(anim) | |
anim = 'hold' | |
doc['power'].text = '繼續' | |
def reverse(ev): | |
global anim, direction | |
# 當 anim 為 hold 時, 按鈕無效 | |
if anim != "hold": | |
if direction == True: | |
direction = False | |
else: | |
direction = True | |
doc["power"].bind("click", launchAnimation) | |
doc["reverse"].bind("click", reverse) |
This file contains 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
# import point-line-triangle module | |
import plt | |
import math | |
from browser import document as doc | |
from browser import timer | |
from browser import html | |
midpt = plt.Point(0, 0) | |
tippt = plt.Point(0, 0) | |
contour = [] | |
# 執行繪圖流程, 注意 x, y 為 global variables | |
def draw(): | |
global theta, midpt, oldpt | |
context.clearRect(0, 0, canvas.width, canvas.height) | |
line1.drawMe(context) | |
line2.drawMe(context) | |
line3.drawMe(context) | |
line4.drawMe(context) | |
#triangle1.drawMe(context) | |
#triangle2.drawMe(context) | |
theta += dx | |
#PLAP | |
p2.x = p1.x + line1.length*math.cos(theta*degree) | |
p2.y = p1.y - line1.length*math.sin(theta*degree) | |
#PLLP | |
p3.x, p3.y = triangle2.setPPSS(p2,p4,line2.length,line3.length) | |
# 計算垂直單位向量 | |
a = plt.Coord(p3.x, p3.y) | |
b = plt.Coord(p2.x, p2.y) | |
normal = plt.perpendicular(plt.normalize(a-b)) | |
midpt.x = (p2.x + p3.x)/2 | |
midpt.y = (p2.y + p3.y)/2 | |
tippt.x = midpt.x + 150*normal.x | |
tippt.y = midpt.y + 150*normal.y | |
if theta < 360: | |
contour.append((tippt.x, tippt.y)) | |
context.beginPath() | |
context.moveTo(midpt.x, midpt.y) | |
context.lineTo(tippt.x, tippt.y) | |
# 利用 fillRect 繪製一個長寬各 1 單位的正方形 | |
for i in range(len(contour)): | |
context.fillRect(contour[i][0], contour[i][1], 1, 1) | |
context.stroke() | |
#p1.tag(context) | |
# 以上為相關函式物件的定義區 | |
# 全域變數 | |
# 幾何位置輸入變數 | |
x=10 | |
y=10 | |
r=10 | |
# 畫布與繪圖內容 | |
# 其他輸入變數 | |
theta = 0 | |
degree = math.pi/180.0 | |
dx = 2 | |
dy = 4 | |
#set p1.p2.p3.p4 position | |
lift = 10 | |
# 各起始座標點必須精確 | |
p1 = plt.Point(150,100+lift) | |
p2 = plt.Point(150,200+lift) | |
p3 = plt.Point(300,300+lift) | |
p4 = plt.Point(350,100+lift) | |
#共有五條線 | |
line1 = plt.Link(p1,p2) | |
line2 = plt.Link(p2,p3) | |
line3 = plt.Link(p3,p4) | |
line4 = plt.Link(p1,p4) | |
line5 = plt.Link(p2,p4) | |
#link2_len = p2.distance(p3) | |
#link3_len = p3.distance(p4) | |
#link2_len = line1.getR() | |
#link3_len = line3.getR() | |
#alert(str(link2_len)+','+str(link3_len)) | |
triangle1 = plt.Triangle(p1,p2,p4) | |
triangle2 = plt.Triangle(p2,p3,p4) | |
# 視窗載入時執行內容 | |
# 繪圖畫布設定 | |
canvas = html.CANVAS(width = 500, height = 500) | |
canvas.id = "game-board" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
#canvas = document["plotarea2"] | |
context = canvas.getContext("2d") | |
# 座標轉換, 移動 canvas.height 並且 y 座標變號, 也就是將原點座標移到畫面左下角 | |
context.translate(0,canvas.height) | |
context.scale(1,-1) | |
#以間隔 20 micro seconds 重複呼叫 draw() | |
timer.set_interval(draw,20) |
This file contains 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
# sum 初始值設為 0 | |
sum = 0 | |
init = 1 | |
upto = 100 | |
# 利用 for 重複迴圈與變數加法進行累加 | |
for i in range(init, upto+1): | |
sum = sum + i | |
print("從" + str(init) + "累加到" + str(upto) + "=" + str(sum)) |
This file contains 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
# source from https://hawstein.com/2013/04/15/snake-ai/ | |
# snake head is black | |
# add lightgrey grid | |
# 從 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): | |
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 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 = 400, height = 400) | |
canvas.id = "game-board" | |
brython_div = doc["brython_div2"] | |
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 | |
# 建立 buttons | |
brython_div <= html.BUTTON("啟動", id="power") | |
score = 0 | |
high_score = 0 | |
# gs*tc = canvas width and height | |
# 每一格的 pixel 數 | |
gs = 20 | |
# 長寬各有 20 格 | |
tc = 20 | |
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) | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
def grid(): | |
# gs, HEIGHT, WIDTH | |
# 利用迴圈與座標增量繪圖 | |
for i in range(WIDTH): | |
for j in range(HEIGHT): | |
dRect(i*gs, j*gs, gs, gs, 1, "lightgrey") | |
# 貪食蛇運動的場地長寬 | |
HEIGHT = 20 | |
WIDTH = 20 | |
# FIELD_SIZE 為場地長乘以寬表示格子 (CELL) 總數 | |
FIELD_SIZE = HEIGHT * WIDTH | |
# 貪食蛇頭總是位於snake數列的第一個元素 | |
HEAD = 0 | |
# 用來代表不同意義的數字,由於矩陣上每個格子會處理成到達食物的路徑長度, | |
# 因此這三個變數間需要有足夠大的間隔(>HEIGHT*WIDTH) | |
# 以整數 0 代表 FOOD, 意即若 board 數列中某一元素 | |
# 將隨機取得座標後將該數列索引值設為 0 就表示該格子為 FOOD | |
# UNDEFINED 值之所以必須大於 HEIGHT*WIDTH, 因為該值將在 BFS 後 | |
# 表示蛇頭距離 FOOD 的路徑步數, 而最大距離步數將會是 HEIGHT*WIDTH | |
# SNAKE 以整數代表, 由於必須有別於 UNDEFINED 與 FOOD 的值, 因此選擇 | |
# 以 2 * UNDEFINED 這個數字代表該格子被 snake 身體佔住 | |
FOOD = 0 | |
UNDEFINED = (HEIGHT + 1) * (WIDTH + 1) | |
SNAKE = 2 * UNDEFINED | |
# 由於 snake 是一維數列,所以對應元素直接加上以下數值就表示向四個方向移動 | |
# 應該是說, 原本該以二維座標表示 board 中各格子的座標, 但在此選擇以一維 | |
# 數列的資料結構來代表二維座標, (1, 1) 可表示為 1*WIDTH+1, | |
# (x, y) 則表示為 x*WIDTH+y | |
# 因此往上或往下的移動, 就一維數列值而言, 必須減或加上 WIDTH | |
LEFT = -1 | |
RIGHT = 1 | |
UP = -WIDTH | |
DOWN = WIDTH | |
# 錯誤碼 | |
ERR = -1111 | |
# 用一維數列來表示二維的座標, 使用此資料結構的原因是: | |
# 貪食蛇每行進一步, 只需要配合蛇頭移動位置新增一個方格座標, | |
# 且更改蛇尾對應值 (即從原先的蛇身因行進移動一步而變更設定) | |
# 且讓 snake[HEAD] = x*WIDTH+y, 假設蛇長 snake_size=n | |
# 則蛇尾 snake[HEAD+n-1] 假設位於 xn*WIDTH+yn | |
# board[x*WIDTH+y]=SNAKE=2 * UNDEFINED | |
# board[xn*WIDTH+yn] 表示為蛇尾, 蛇頭走一步後, 蛇尾從 2 * UNDEFINED | |
# 轉為空白可在搜尋流程中加上距離食物的總步數 | |
# board 表示蛇運動的矩形場地 | |
# 初始化蛇頭在(1,1)的地方,第0行,HEIGHT行,第0列,WIDTH列為圍牆,不可用 | |
# 初始蛇長度為1 | |
# board 與 snake 均為總元素為格點數大小的一維數列 | |
board = [0] * FIELD_SIZE | |
#snake = [0] * (FIELD_SIZE+1) | |
# 原程式加 1 | |
snake = [0] * (FIELD_SIZE) | |
# 座標 (1, 1) 在一維數列中, 表示為 1*WIDTH+1 | |
snake[HEAD] = 1*WIDTH+1 | |
snake_size = 1 | |
# 與上面變量對應的臨時變量,蛇試探性地移動時使用 | |
tmpboard = [0] * FIELD_SIZE | |
#tmpsnake = [0] * (FIELD_SIZE+1) | |
# 原程式加 1 | |
tmpsnake = [0] * (FIELD_SIZE) | |
tmpsnake[HEAD] = 1*WIDTH+1 | |
tmpsnake_size = 1 | |
# food:食物位置(0~FIELD_SIZE-1),初始在(3, 3) | |
# best_move: 運動方向 | |
food = 3 * WIDTH + 3 | |
best_move = ERR | |
# 運動方向數組 | |
mov = [LEFT, RIGHT, UP, DOWN] | |
# 接收到的鍵和分數 | |
#key = KEY_RIGHT | |
# 初始蛇為一節 | |
score = 1 #分數也表示蛇長 | |
# 檢查一個 cell 有沒有被蛇身覆蓋,沒有覆蓋則為 free,返回 true | |
def is_cell_free(idx, psize, psnake): | |
return not (idx in psnake[:psize]) | |
# 檢查某個位置idx是否可向move方向運動 | |
# idx = x*WIDTH + y | |
def is_move_possible(idx, move): | |
flag = False | |
# LEFT = -1 | |
if move == LEFT: | |
# idx%WIDTH is the column order number need to be > 1 | |
# if y > 1, snake can move LEFT to -1 | |
flag = True if idx%WIDTH > 1 else False | |
# RIGHT = 1 | |
elif move == RIGHT: | |
# to move RIGHT, column order number need to be < WIDTH-2 | |
flag = True if idx%WIDTH < (WIDTH-2) else False | |
# UP = -WIDTH | |
elif move == UP: | |
# to move UP row order number need to be > 1 | |
flag = True if idx > (2*WIDTH-1) else False # 即idx/WIDTH > 1 | |
# DOWN = WIDTH | |
# FIELD_SIZE = WIDTH* HEIGHT | |
elif move == DOWN: | |
# to move DOWN row order number need to be < HEIGHT-2 | |
flag = True if idx < (FIELD_SIZE-2*WIDTH) else False # 即idx//WIDTH < HEIGHT-2 | |
return flag | |
# 重置 board | |
# board_refresh 後,UNDEFINED 值都變為了到達食物的路徑長度 | |
# 如需要還原,則要重置它 | |
def board_reset(psnake, psize, pboard): | |
# 查驗所有格點內容 | |
for i in range(FIELD_SIZE): | |
if i == food: | |
pboard[i] = FOOD | |
elif is_cell_free(i, psize, psnake): # 該位置為空 | |
pboard[i] = UNDEFINED | |
else: # 該位置為蛇身 | |
pboard[i] = SNAKE | |
# 廣度優先搜索遍歷整個 board, | |
# 計算出 board 中每個非 SNAKE 元素到達食物的路徑長度 | |
def board_refresh(pfood, psnake, pboard): | |
queue = [] | |
queue.append(pfood) | |
inqueue = [0] * FIELD_SIZE | |
found = False | |
# while 循環結束後,除了蛇的身體, | |
# 其它每個方格中的數字代碼從它到食物的路徑長度 | |
while len(queue)!=0: | |
idx = queue.pop(0) | |
if inqueue[idx] == 1: continue | |
inqueue[idx] = 1 | |
for i in range(4): | |
if is_move_possible(idx, mov[i]): | |
if idx + mov[i] == psnake[HEAD]: | |
found = True | |
if pboard[idx+mov[i]] < SNAKE: # 如果該點不是蛇的身體 | |
if pboard[idx+mov[i]] > pboard[idx]+1: | |
pboard[idx+mov[i]] = pboard[idx] + 1 | |
if inqueue[idx+mov[i]] == 0: | |
queue.append(idx+mov[i]) | |
return found | |
# 從蛇頭開始,根據 board 中元素值, | |
# 從蛇頭周圍 4 個領域點中選擇最短路徑 | |
def choose_shortest_safe_move(psnake, pboard): | |
best_move = ERR | |
min = SNAKE | |
for i in range(4): | |
if is_move_possible(psnake[HEAD], mov[i]) and pboard[psnake[HEAD]+mov[i]]<min: | |
min = pboard[psnake[HEAD]+mov[i]] | |
best_move = mov[i] | |
return best_move | |
# 從蛇頭開始,根據board中元素值, | |
# 從蛇頭周圍 4 個領域點中選擇最遠路徑 | |
def choose_longest_safe_move(psnake, pboard): | |
best_move = ERR | |
max = -1 | |
for i in range(4): | |
if is_move_possible(psnake[HEAD], mov[i]) and pboard[psnake[HEAD]+mov[i]]<UNDEFINED and pboard[psnake[HEAD]+mov[i]]>max: | |
max = pboard[psnake[HEAD]+mov[i]] | |
best_move = mov[i] | |
return best_move | |
# 檢查是否可以追著蛇尾運動, 即蛇頭和蛇尾間是有路徑的 | |
# 為的是避免蛇頭陷入死路 | |
# 虛擬操作, 在 tmpboard,tmpsnake 中進行 | |
def is_tail_inside(): | |
global tmpboard, tmpsnake, food, tmpsnake_size | |
tmpboard[tmpsnake[tmpsnake_size-1]] = 0 # 虛擬地將蛇尾變為食物(因為是虛擬的,所以在tmpsnake,tmpboard中進行) | |
tmpboard[food] = SNAKE # 放置食物的地方,看成蛇身 | |
result = board_refresh(tmpsnake[tmpsnake_size-1], tmpsnake, tmpboard) # 求得每個位置到蛇尾的路徑長度 | |
for i in range(4): # 如果蛇頭和蛇尾緊挨著,則返回 False。即不能 follow_tail,追著蛇尾運動了 | |
if is_move_possible(tmpsnake[HEAD], mov[i]) and tmpsnake[HEAD]+mov[i]==tmpsnake[tmpsnake_size-1] and tmpsnake_size>3: | |
result = False | |
return result | |
# 讓蛇頭朝著蛇尾運行一步 | |
# 不管蛇身阻擋,朝蛇尾方向運行 | |
def follow_tail(): | |
global tmpboard, tmpsnake, food, tmpsnake_size | |
tmpsnake_size = snake_size | |
tmpsnake = snake[:] | |
board_reset(tmpsnake, tmpsnake_size, tmpboard) # 重置虛擬board | |
tmpboard[tmpsnake[tmpsnake_size-1]] = FOOD # 讓蛇尾成為食物 | |
tmpboard[food] = SNAKE # 讓食物的地方變成蛇身 | |
board_refresh(tmpsnake[tmpsnake_size-1], tmpsnake, tmpboard) # 求得各個位置到達蛇尾的路徑長度 | |
tmpboard[tmpsnake[tmpsnake_size-1]] = SNAKE # 還原蛇尾 | |
return choose_longest_safe_move(tmpsnake, tmpboard) # 返回運行方向(讓蛇頭運動 1 步) | |
# 在各種方案都不行時,隨便找一個可行的方向來走(1 步), | |
def any_possible_move(): | |
global food , snake, snake_size, board | |
best_move = ERR | |
board_reset(snake, snake_size, board) | |
board_refresh(food, snake, board) | |
min = SNAKE | |
for i in range(4): | |
if is_move_possible(snake[HEAD], mov[i]) and board[snake[HEAD]+mov[i]]<min: | |
min = board[snake[HEAD]+mov[i]] | |
best_move = mov[i] | |
return best_move | |
def shift_array(arr, size): | |
for i in range(size, 0, -1): | |
arr[i] = arr[i-1] | |
def new_food(): | |
global food, snake_size | |
cell_free = False | |
while not cell_free: | |
w = randint(1, WIDTH-2) | |
h = randint(1, HEIGHT-2) | |
# food coordinate | |
food = h * WIDTH + w | |
cell_free = is_cell_free(food, snake_size, snake) | |
#win.addch(food//WIDTH, food%WIDTH, '@') | |
# 畫出 food | |
ctx.fillStyle = "red" | |
ctx.fillRect((food//WIDTH)*gs, (food%WIDTH)*gs, gs-2, gs-2) | |
# 真正的蛇在這個函數中, 朝 pbest_move 走 1 步 | |
def make_move(pbest_move): | |
global key, snake, board, snake_size, score | |
shift_array(snake, snake_size) | |
snake[HEAD] += pbest_move | |
# 按 esc 退出,getch 同時保證繪圖的流暢性, 沒有它只會看到最終結果 | |
#win.timeout(10) | |
#event = win.getch() | |
#key = key if event == -1 else event | |
#if key == 27: return | |
p = snake[HEAD] | |
#win.addch(p//WIDTH, p%WIDTH, '*') | |
# 畫出 snake head | |
ctx.fillStyle = "black" | |
ctx.fillRect((p//WIDTH)*gs, (p%WIDTH)*gs, gs-2, gs-2) | |
# 如果新加入的蛇頭就是食物的位置 | |
# 蛇長加 1,產生新的食物,重置 board (因為原來那些路徑長度已經用不上了) | |
# snake[HEAD] is the coordinate of the snake head | |
# food is the coordinate of the food | |
if snake[HEAD] == food: | |
# mark on the board where the snake head is | |
board[snake[HEAD]] = SNAKE # 新的蛇頭 | |
snake_size += 1 | |
score += 1 | |
if snake_size < FIELD_SIZE: new_food() | |
else: # 如果新加入的蛇頭不是食物的位置 | |
board[snake[HEAD]] = SNAKE # 新的蛇頭 | |
board[snake[snake_size]] = UNDEFINED # 蛇尾變為空格 | |
#win.addch(snake[snake_size]//WIDTH, snake[snake_size]%WIDTH, ' ') | |
# 去除 snake tail | |
ctx.fillStyle = "white" | |
ctx.fillRect((snake[snake_size]//WIDTH)*gs, (snake[snake_size]%WIDTH)*gs, gs-2, gs-2) | |
# 虛擬地運行一次,然後在調用處檢查這次運行可否可行 | |
# 可行才真實運行。 | |
# 虛擬運行吃到食物後,得到虛擬下蛇在 board 的位置 | |
def virtual_shortest_move(): | |
global snake, board, snake_size, tmpsnake, tmpboard, tmpsnake_size, food | |
tmpsnake_size = snake_size | |
tmpsnake = snake[:] # 如果直接tmpsnake=snake,則兩者指向同一處內存 | |
tmpboard = board[:] # board中已經是各位置到達食物的路徑長度了,不用再計算 | |
board_reset(tmpsnake, tmpsnake_size, tmpboard) | |
food_eated = False | |
while not food_eated: | |
board_refresh(food, tmpsnake, tmpboard) | |
move = choose_shortest_safe_move(tmpsnake, tmpboard) | |
shift_array(tmpsnake, tmpsnake_size) | |
tmpsnake[HEAD] += move # 在蛇頭前加入一個新的位置 | |
# 如果新加入的蛇頭的位置正好是食物的位置 | |
# 則長度加1,重置board,食物那個位置變為蛇的一部分(SNAKE) | |
if tmpsnake[HEAD] == food: | |
tmpsnake_size += 1 | |
board_reset(tmpsnake, tmpsnake_size, tmpboard) # 虛擬運行後,蛇在board的位置(label101010) | |
tmpboard[food] = SNAKE | |
food_eated = True | |
else: # 如果蛇頭不是食物的位置,則新加入的位置為蛇頭,最後一個變為空格 | |
tmpboard[tmpsnake[HEAD]] = SNAKE | |
tmpboard[tmpsnake[tmpsnake_size]] = UNDEFINED | |
# 如果蛇與食物間有路徑,則調用本函數 | |
def find_safe_way(): | |
global snake, board | |
safe_move = ERR | |
# 虛擬地運行一次, 因為已經確保蛇與食物間有路徑,所以執行有效 | |
# 運行後得到虛擬下蛇在board中的位置, 即 tmpboard,見 label101010 | |
virtual_shortest_move() # 該函數唯一調用處 | |
if is_tail_inside(): # 如果虛擬運行後,蛇頭蛇尾間有通路,則選最短路運行(1步) | |
return choose_shortest_safe_move(snake, board) | |
safe_move = follow_tail() # 否則虛擬地follow_tail 1步,如果可以做到,返回 true | |
return safe_move | |
#curses.initscr() | |
#win = curses.newwin(HEIGHT, WIDTH, 0, 0) | |
#win.keypad(1) | |
#curses.noecho() | |
#curses.curs_set(0) | |
#win.border(0) | |
#win.nodelay(1) | |
#win.addch(food//WIDTH, food%WIDTH, '@') | |
# 畫出 food | |
ctx.fillStyle = "red" | |
ctx.fillRect((food//WIDTH)*gs, (food%WIDTH)*gs, gs-2, gs-2) | |
def game2(): | |
global score | |
grid() | |
ctx.fillStyle = "lime" | |
ctx.fillRect((snake[HEAD]//WIDTH)*gs, (snake[HEAD]%WIDTH)*gs, gs-2, gs-2) | |
update_score(score) | |
board_reset(snake, snake_size, board) | |
if board_refresh(food, snake, board): | |
best_move = find_safe_way() # find_safe_way 的唯一調用處 | |
else: | |
best_move = follow_tail() | |
if best_move == ERR: | |
best_move = any_possible_move() | |
# 上面一次思考,只得出一個方向,運行一步 | |
if best_move != ERR: | |
make_move(best_move) | |
# 加入暫停機制 | |
# 將 anim 設為 None | |
anim = None | |
def launchAnimation(ev): | |
global anim | |
# 初始啟動, anim 為 None | |
if anim is None: | |
# 每 0.08 秒執行一次 draw 函式繪圖 | |
#anim = timer.set_interval(draw, 80) | |
anim = browser.timer.set_interval(game2, 100/15) | |
# 初始啟動後, 按鈕文字轉為"暫停" | |
doc['power'].text = '暫停' | |
elif anim == 'hold': | |
# 當 anim 為 'hold' 表示曾經暫停後的啟動, 因此持續以 set_interval() 持續旋轉, 且將 power 文字轉為"暫停" | |
#anim = timer.set_interval(draw, 80) | |
anim = browser.timer.set_interval(game2, 100/15) | |
doc['power'].text = '暫停' | |
else: | |
# 初始啟動後, 使用者再按 power, 此時 anim 非 None 也不是 'hold', 因此會執行 clear_interval() 暫停 | |
# 且將 anim 變數設為 'hold', 且 power 文字轉為"繼續" | |
#timer.clear_interval(anim) | |
browser.timer.clear_interval(anim) | |
anim = 'hold' | |
doc['power'].text = '繼續' | |
def reverse(ev): | |
global anim, direction | |
# 當 anim 為 hold 時, 按鈕無效 | |
if anim != "hold": | |
if direction == True: | |
direction = False | |
else: | |
direction = True | |
doc["power"].bind("click", launchAnimation) | |
#browser.timer.set_interval(game2, 100/15) |
This file contains 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
# source: https://github.com/breezy-team/merge3 | |
class CantReprocessAndShowBase(Exception): | |
"""Can't reprocess and show base.""" | |
def intersect(ra, rb): | |
"""Given two ranges return the range where they intersect or None. | |
>>> intersect((0, 10), (0, 6)) | |
(0, 6) | |
>>> intersect((0, 10), (5, 15)) | |
(5, 10) | |
>>> intersect((0, 10), (10, 15)) | |
>>> intersect((0, 9), (10, 15)) | |
>>> intersect((0, 9), (7, 15)) | |
(7, 9) | |
""" | |
# preconditions: (ra[0] <= ra[1]) and (rb[0] <= rb[1]) | |
sa = max(ra[0], rb[0]) | |
sb = min(ra[1], rb[1]) | |
if sa < sb: | |
return sa, sb | |
else: | |
return None | |
def compare_range(a, astart, aend, b, bstart, bend): | |
"""Compare a[astart:aend] == b[bstart:bend], without slicing. | |
""" | |
if (aend-astart) != (bend-bstart): | |
return False | |
for ia, ib in zip(range(astart, aend), range(bstart, bend)): | |
if a[ia] != b[ib]: | |
return False | |
else: | |
return True | |
class Merge3(object): | |
"""3-way merge of texts. | |
Given BASE, OTHER, THIS, tries to produce a combined text | |
incorporating the changes from both BASE->OTHER and BASE->THIS. | |
All three will typically be sequences of lines.""" | |
def __init__(self, base, a, b, is_cherrypick=False, sequence_matcher=None): | |
"""Constructor. | |
:param base: lines in BASE | |
:param a: lines in A | |
:param b: lines in B | |
:param is_cherrypick: flag indicating if this merge is a cherrypick. | |
When cherrypicking b => a, matches with b and base do not conflict. | |
:param sequence_matcher: Sequence matcher to use (defaults to | |
difflib.SequenceMatcher) | |
""" | |
if sequence_matcher is None: | |
import difflib | |
sequence_matcher = difflib.SequenceMatcher | |
self.base = base | |
self.a = a | |
self.b = b | |
self.is_cherrypick = is_cherrypick | |
self.sequence_matcher = sequence_matcher | |
def _uses_bytes(self): | |
if len(self.a) > 0: | |
return isinstance(self.a[0], bytes) | |
elif len(self.base) > 0: | |
return isinstance(self.base[0], bytes) | |
elif len(self.b) > 0: | |
return isinstance(self.b[0], bytes) | |
else: | |
return False | |
def merge_lines(self, | |
name_a=None, | |
name_b=None, | |
name_base=None, | |
start_marker='<<<<<<<', | |
mid_marker='=======', | |
end_marker='>>>>>>>', | |
base_marker=None, | |
reprocess=False): | |
"""Return merge in cvs-like form. | |
""" | |
if base_marker and reprocess: | |
raise CantReprocessAndShowBase() | |
if self._uses_bytes(): | |
if len(self.a) > 0: | |
if self.a[0].endswith(b'\r\n'): | |
newline = b'\r\n' | |
elif self.a[0].endswith(b'\r'): | |
newline = b'\r' | |
else: | |
newline = b'\n' | |
else: | |
newline = b'\n' | |
if isinstance(start_marker, str): | |
start_marker = start_marker.encode() | |
if isinstance(end_marker, str): | |
end_marker = end_marker.encode() | |
if isinstance(mid_marker, str): | |
mid_marker = mid_marker.encode() | |
if base_marker is not None and isinstance(base_marker, str): | |
base_marker = base_marker.encode() | |
if name_a: | |
if isinstance(name_a, str): | |
name_a = name_a.encode() | |
start_marker = start_marker + b' ' + name_a | |
if name_b: | |
if isinstance(name_b, str): | |
name_b = name_b.encode() | |
end_marker = end_marker + b' ' + name_b | |
if name_base and base_marker: | |
if isinstance(name_base, str): | |
name_base = name_base.encode() | |
base_marker = base_marker + b' ' + name_base | |
else: | |
if len(self.a) > 0: | |
if self.a[0].endswith('\r\n'): | |
newline = '\r\n' | |
elif self.a[0].endswith('\r'): | |
newline = '\r' | |
else: | |
newline = '\n' | |
else: | |
newline = '\n' | |
if name_a: | |
start_marker = start_marker + ' ' + name_a | |
if name_b: | |
end_marker = end_marker + ' ' + name_b | |
if name_base and base_marker: | |
base_marker = base_marker + ' ' + name_base | |
merge_regions = self.merge_regions() | |
if reprocess is True: | |
merge_regions = self.reprocess_merge_regions(merge_regions) | |
for t in merge_regions: | |
what = t[0] | |
if what == 'unchanged': | |
for i in range(t[1], t[2]): | |
yield self.base[i] | |
elif what == 'a' or what == 'same': | |
for i in range(t[1], t[2]): | |
yield self.a[i] | |
elif what == 'b': | |
for i in range(t[1], t[2]): | |
yield self.b[i] | |
elif what == 'conflict': | |
yield start_marker + newline | |
for i in range(t[3], t[4]): | |
yield self.a[i] | |
if base_marker is not None: | |
yield base_marker + newline | |
for i in range(t[1], t[2]): | |
yield self.base[i] | |
yield mid_marker + newline | |
for i in range(t[5], t[6]): | |
yield self.b[i] | |
yield end_marker + newline | |
else: | |
raise ValueError(what) | |
def merge_annotated(self): | |
"""Return merge with conflicts, showing origin of lines. | |
Most useful for debugging merge. | |
""" | |
UNCHANGED = 'u' | |
SEP = ' | ' | |
CONFLICT_START = '<<<<\n' | |
CONFLICT_MID = '----\n' | |
CONFLICT_END = '>>>>\n' | |
WIN_A = 'a' | |
WIN_B = 'b' | |
if self._uses_bytes(): | |
UNCHANGED = UNCHANGED.encode() | |
SEP = SEP.encode() | |
CONFLICT_START = CONFLICT_START.encode() | |
CONFLICT_MID = CONFLICT_MID.encode() | |
CONFLICT_END = CONFLICT_END.encode() | |
WIN_A = WIN_A.encode() | |
WIN_B = WIN_B.encode() | |
for t in self.merge_regions(): | |
what = t[0] | |
if what == 'unchanged': | |
for i in range(t[1], t[2]): | |
yield UNCHANGED + SEP + self.base[i] | |
elif what == 'a' or what == 'same': | |
for i in range(t[1], t[2]): | |
yield WIN_A.lower() + SEP + self.a[i] | |
elif what == 'b': | |
for i in range(t[1], t[2]): | |
yield WIN_B.lower() + SEP + self.b[i] | |
elif what == 'conflict': | |
yield CONFLICT_START | |
for i in range(t[3], t[4]): | |
yield WIN_A.upper() + SEP + self.a[i] | |
yield CONFLICT_MID | |
for i in range(t[5], t[6]): | |
yield WIN_B.upper() + SEP + self.b[i] | |
yield CONFLICT_END | |
else: | |
raise ValueError(what) | |
def merge_groups(self): | |
"""Yield sequence of line groups. Each one is a tuple: | |
'unchanged', lines | |
Lines unchanged from base | |
'a', lines | |
Lines taken from a | |
'same', lines | |
Lines taken from a (and equal to b) | |
'b', lines | |
Lines taken from b | |
'conflict', base_lines, a_lines, b_lines | |
Lines from base were changed to either a or b and conflict. | |
""" | |
for t in self.merge_regions(): | |
what = t[0] | |
if what == 'unchanged': | |
yield what, self.base[t[1]:t[2]] | |
elif what == 'a' or what == 'same': | |
yield what, self.a[t[1]:t[2]] | |
elif what == 'b': | |
yield what, self.b[t[1]:t[2]] | |
elif what == 'conflict': | |
yield (what, | |
self.base[t[1]:t[2]], | |
self.a[t[3]:t[4]], | |
self.b[t[5]:t[6]]) | |
else: | |
raise ValueError(what) | |
def merge_regions(self): | |
"""Return sequences of matching and conflicting regions. | |
This returns tuples, where the first value says what kind we | |
have: | |
'unchanged', start, end | |
Take a region of base[start:end] | |
'same', astart, aend | |
b and a are different from base but give the same result | |
'a', start, end | |
Non-clashing insertion from a[start:end] | |
Method is as follows: | |
The two sequences align only on regions which match the base | |
and both descendents. These are found by doing a two-way diff | |
of each one against the base, and then finding the | |
intersections between those regions. These "sync regions" | |
are by definition unchanged in both and easily dealt with. | |
The regions in between can be in any of three cases: | |
conflicted, or changed on only one side. | |
""" | |
# section a[0:ia] has been disposed of, etc | |
iz = ia = ib = 0 | |
for (zmatch, zend, amatch, aend, bmatch, | |
bend) in self.find_sync_regions(): | |
matchlen = zend - zmatch | |
# invariants: | |
# matchlen >= 0 | |
# matchlen == (aend - amatch) | |
# matchlen == (bend - bmatch) | |
len_a = amatch - ia | |
len_b = bmatch - ib | |
# len_base = zmatch - iz | |
# invariants: | |
# assert len_a >= 0 | |
# assert len_b >= 0 | |
# assert len_base >= 0 | |
# print 'unmatched a=%d, b=%d' % (len_a, len_b) | |
if len_a or len_b: | |
# try to avoid actually slicing the lists | |
same = compare_range(self.a, ia, amatch, | |
self.b, ib, bmatch) | |
if same: | |
yield 'same', ia, amatch | |
else: | |
equal_a = compare_range(self.a, ia, amatch, | |
self.base, iz, zmatch) | |
equal_b = compare_range(self.b, ib, bmatch, | |
self.base, iz, zmatch) | |
if equal_a and not equal_b: | |
yield 'b', ib, bmatch | |
elif equal_b and not equal_a: | |
yield 'a', ia, amatch | |
elif not equal_a and not equal_b: | |
if self.is_cherrypick: | |
for node in self._refine_cherrypick_conflict( | |
iz, zmatch, ia, amatch, | |
ib, bmatch): | |
yield node | |
else: | |
yield ( | |
'conflict', iz, zmatch, ia, amatch, ib, bmatch) | |
else: | |
raise AssertionError( | |
"can't handle a=b=base but unmatched") | |
ia = amatch | |
ib = bmatch | |
iz = zmatch | |
# if the same part of the base was deleted on both sides | |
# that's OK, we can just skip it. | |
if matchlen > 0: | |
# invariants: | |
# assert ia == amatch | |
# assert ib == bmatch | |
# assert iz == zmatch | |
yield 'unchanged', zmatch, zend | |
iz = zend | |
ia = aend | |
ib = bend | |
def _refine_cherrypick_conflict(self, zstart, zend, astart, aend, bstart, | |
bend): | |
"""When cherrypicking b => a, ignore matches with b and base.""" | |
# Do not emit regions which match, only regions which do not match | |
matcher = self.sequence_matcher( | |
None, self.base[zstart:zend], self.b[bstart:bend]) | |
matches = matcher.get_matching_blocks() | |
last_base_idx = 0 | |
last_b_idx = 0 | |
last_b_idx = 0 | |
yielded_a = False | |
for base_idx, b_idx, match_len in matches: | |
# conflict_z_len = base_idx - last_base_idx | |
conflict_b_len = b_idx - last_b_idx | |
# There are no lines in b which conflict, so skip it | |
if conflict_b_len == 0: | |
pass | |
else: | |
if yielded_a: | |
yield ('conflict', | |
zstart + last_base_idx, zstart + base_idx, | |
aend, aend, bstart + last_b_idx, bstart + b_idx) | |
else: | |
# The first conflict gets the a-range | |
yielded_a = True | |
yield ( | |
'conflict', zstart + last_base_idx, zstart + base_idx, | |
astart, aend, bstart + last_b_idx, bstart + b_idx) | |
last_base_idx = base_idx + match_len | |
last_b_idx = b_idx + match_len | |
if last_base_idx != zend - zstart or last_b_idx != bend - bstart: | |
if yielded_a: | |
yield ('conflict', zstart + last_base_idx, zstart + base_idx, | |
aend, aend, bstart + last_b_idx, bstart + b_idx) | |
else: | |
# The first conflict gets the a-range | |
yielded_a = True | |
yield ('conflict', zstart + last_base_idx, zstart + base_idx, | |
astart, aend, bstart + last_b_idx, bstart + b_idx) | |
if not yielded_a: | |
yield ('conflict', zstart, zend, astart, aend, bstart, bend) | |
def reprocess_merge_regions(self, merge_regions): | |
"""Where there are conflict regions, remove the agreed lines. | |
Lines where both A and B have made the same changes are | |
eliminated. | |
""" | |
for region in merge_regions: | |
if region[0] != "conflict": | |
yield region | |
continue | |
type, iz, zmatch, ia, amatch, ib, bmatch = region | |
a_region = self.a[ia:amatch] | |
b_region = self.b[ib:bmatch] | |
matches = self.sequence_matcher( | |
None, a_region, b_region).get_matching_blocks() | |
next_a = ia | |
next_b = ib | |
for region_ia, region_ib, region_len in matches[:-1]: | |
region_ia += ia | |
region_ib += ib | |
reg = self.mismatch_region(next_a, region_ia, next_b, | |
region_ib) | |
if reg is not None: | |
yield reg | |
yield 'same', region_ia, region_len+region_ia | |
next_a = region_ia + region_len | |
next_b = region_ib + region_len | |
reg = self.mismatch_region(next_a, amatch, next_b, bmatch) | |
if reg is not None: | |
yield reg | |
@staticmethod | |
def mismatch_region(next_a, region_ia, next_b, region_ib): | |
if next_a < region_ia or next_b < region_ib: | |
return 'conflict', None, None, next_a, region_ia, next_b, region_ib | |
def find_sync_regions(self): | |
"""Return a list of sync regions, where both descendents match the base. | |
Generates a list of (base1, base2, a1, a2, b1, b2). There is | |
always a zero-length sync region at the end of all the files. | |
""" | |
ia = ib = 0 | |
amatches = self.sequence_matcher( | |
None, self.base, self.a).get_matching_blocks() | |
bmatches = self.sequence_matcher( | |
None, self.base, self.b).get_matching_blocks() | |
len_a = len(amatches) | |
len_b = len(bmatches) | |
sl = [] | |
while ia < len_a and ib < len_b: | |
abase, amatch, alen = amatches[ia] | |
bbase, bmatch, blen = bmatches[ib] | |
# there is an unconflicted block at i; how long does it | |
# extend? until whichever one ends earlier. | |
i = intersect((abase, abase+alen), (bbase, bbase+blen)) | |
if i: | |
intbase = i[0] | |
intend = i[1] | |
intlen = intend - intbase | |
# found a match of base[i[0], i[1]]; this may be less than | |
# the region that matches in either one | |
# assert intlen <= alen | |
# assert intlen <= blen | |
# assert abase <= intbase | |
# assert bbase <= intbase | |
asub = amatch + (intbase - abase) | |
bsub = bmatch + (intbase - bbase) | |
aend = asub + intlen | |
bend = bsub + intlen | |
# assert self.base[intbase:intend] == self.a[asub:aend], \ | |
# (self.base[intbase:intend], self.a[asub:aend]) | |
# assert self.base[intbase:intend] == self.b[bsub:bend] | |
sl.append((intbase, intend, | |
asub, aend, | |
bsub, bend)) | |
# advance whichever one ends first in the base text | |
if (abase + alen) < (bbase + blen): | |
ia += 1 | |
else: | |
ib += 1 | |
intbase = len(self.base) | |
abase = len(self.a) | |
bbase = len(self.b) | |
sl.append((intbase, intbase, abase, abase, bbase, bbase)) | |
return sl | |
def find_unconflicted(self): | |
"""Return a list of ranges in base that are not conflicted.""" | |
am = self.sequence_matcher( | |
None, self.base, self.a).get_matching_blocks() | |
bm = self.sequence_matcher( | |
None, self.base, self.b).get_matching_blocks() | |
unc = [] | |
while am and bm: | |
# there is an unconflicted block at i; how long does it | |
# extend? until whichever one ends earlier. | |
a1 = am[0][0] | |
a2 = a1 + am[0][2] | |
b1 = bm[0][0] | |
b2 = b1 + bm[0][2] | |
i = intersect((a1, a2), (b1, b2)) | |
if i: | |
unc.append(i) | |
if a2 < b2: | |
del am[0] | |
else: | |
del bm[0] | |
return unc | |
m3 = Merge3(['common\n', 'base\n'],['common\n', 'a\n'],['common\n', 'b\n']) | |
print(list(m3.merge_annotated())) |
This file contains 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
# pont, line and triangle scripts | |
import math | |
class Coord(object): | |
def __init__(self,x,y): | |
self.x = x | |
self.y = y | |
def __sub__(self,other): | |
# This allows you to substract vectors | |
return Coord(self.x-other.x,self.y-other.y) | |
def __repr__(self): | |
# Used to get human readable coordinates when printing | |
return "Coord(%f,%f)"%(self.x,self.y) | |
def length(self): | |
# Returns the length of the vector | |
return math.sqrt(self.x**2 + self.y**2) | |
def angle(self): | |
# Returns the vector's angle | |
return math.atan2(self.y,self.x) | |
def normalize(coord): | |
return Coord( | |
coord.x/coord.length(), | |
coord.y/coord.length() | |
) | |
def perpendicular(coord): | |
# Shifts the angle by pi/2 and calculate the coordinates | |
# using the original vector length | |
return Coord( | |
coord.length()*math.cos(coord.angle()+math.pi/2), | |
coord.length()*math.sin(coord.angle()+math.pi/2) | |
) | |
# 點類別 | |
class Point(object): | |
# 起始方法 | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
# 繪製方法 | |
def drawMe(self, g, r): | |
self.g = g | |
self.r = r | |
self.g.save() | |
self.g.moveTo(self.x,self.y) | |
self.g.beginPath() | |
# 根據 r 半徑繪製一個圓代表點的所在位置 | |
self.g.arc(self.x, self.y, self.r, 0, 2*math.pi, True) | |
self.g.moveTo(self.x,self.y) | |
self.g.lineTo(self.x+self.r, self.y) | |
self.g.moveTo(self.x, self.y) | |
self.g.lineTo(self.x-self.r, self.y) | |
self.g.moveTo(self.x, self.y) | |
self.g.lineTo(self.x, self.y+self.r) | |
self.g.moveTo(self.x, self.y) | |
self.g.lineTo(self.x, self.y-self.r) | |
self.g.restore() | |
self.g.stroke() | |
# 加入 Eq 方法 | |
def Eq(self, pt): | |
self.x = pt.x | |
self.y = pt.y | |
# 加入 setPoint 方法 | |
def setPoint(self, px, py): | |
self.x = px | |
self.y = py | |
# 加上 distance(pt) 方法, 計算點到 pt 的距離 | |
def distance(self, pt): | |
self.pt = pt | |
x = self.x - self.pt.x | |
y = self.y - self.pt.y | |
return math.sqrt(x * x + y * y) | |
# 利用文字標示點的座標位置 | |
def tag(self, g): | |
self.g = g | |
self.g.beginPath() | |
self.g.fillText("%d, %d"%(self.x, self.y),self.x, self.y) | |
self.g.stroke() | |
# Line 類別物件 | |
class Line(object): | |
# 起始方法 | |
def __init__(self, p1, p2): | |
self.p1 = p1 | |
self.p2 = p2 | |
# 直線的第一點, 設為線尾 | |
self.Tail = self.p1 | |
# 直線組成的第二點, 設為線頭 | |
self.Head = self.p2 | |
# 直線的長度屬性 | |
self.length = math.sqrt(math.pow(self.p2.x-self.p1.x, 2)+math.pow(self.p2.y-self.p1.y,2)) | |
# setPP 以指定頭尾座標點來定義直線 | |
def setPP(self, p1, p2): | |
self.p1 = p1 | |
self.p2 = p2 | |
self.Tail = self.p1 | |
self.Head = self.p2 | |
self.length = math.sqrt(math.pow(self.p2.x-self.p1.x, 2)+math.pow(self.p2.y-self.p1.y,2)) | |
# setRT 方法 for Line, 應該已經確定 Tail 點, 然後以 r, t 作為設定 Head 的參考 | |
def setRT(self, r, t): | |
self.r = r | |
self.t = t | |
x = self.r * math.cos(self.t) | |
y = self.r * math.sin(self.t) | |
self.Tail.Eq(self.p1) | |
self.Head.setPoint(self.Tail.x + x,self.Tail.y + y) | |
# getR 方法 for Line | |
def getR(self): | |
# x 分量與 y 分量 | |
x = self.p1.x - self.p2.x | |
y = self.p1.y - self.p2.y | |
return math.sqrt(x * x + y * y) | |
# 根據定義 atan2(y,x), 表示 (x,y) 與 正 x 軸之間的夾角, 介於 pi 與 -pi 間 | |
def getT(self): | |
x = self.p2.x - self.p1.x | |
y = self.p2.y - self.p1.y | |
if (math.fabs(x) < math.pow(10,-100)): | |
if(y < 0.0): | |
return (-math.pi/2) | |
else: | |
return (math.pi/2) | |
else: | |
return math.atan2(y, x) | |
# setTail 方法 for Line | |
def setTail(self, pt): | |
self.pt = pt | |
self.Tail.Eq(pt) | |
self.Head.setPoint(self.pt.x + self.x, self.pt.y + self.y) | |
# getHead 方法 for Line | |
def getHead(self): | |
return self.Head | |
def getTail(self): | |
return self.Tail | |
def drawMe(self, g): | |
self.g = g | |
self.g.beginPath() | |
self.g.moveTo(self.p1.x,self.p1.y) | |
self.g.lineTo(self.p2.x,self.p2.y) | |
self.g.stroke() | |
def test(self): | |
return ("this is pure test to Inherit") | |
class Link(Line): | |
def __init__(self, p1, p2): | |
self.p1 = p1 | |
self.p2 = p2 | |
self.length = math.sqrt(math.pow((self.p2.x - self.p1.x), 2) + math.pow((self.p2.y - self.p1.y), 2)) | |
#g context | |
def drawMe(self, g): | |
self.g = g | |
hole = 5 | |
radius = 10 | |
length = self.getR() | |
# alert(length) | |
# 儲存先前的繪圖狀態 | |
self.g.save() | |
self.g.translate(self.p1.x,self.p1.y) | |
#alert(str(self.p1.x)+","+str(self.p1.y)) | |
#self.g.rotate(-((math.pi/2)-self.getT())) | |
self.g.rotate(-math.pi*0.5 + self.getT()) | |
#alert(str(self.getT())) | |
#self.g.rotate(10*math.pi/180) | |
#this.g.rotate(-(Math.PI/2-this.getT())); | |
# 必須配合畫在 y 軸上的 Link, 進行座標轉換, 也可以改為畫在 x 軸上... | |
self.g.beginPath() | |
self.g.moveTo(0,0) | |
self.g.arc(0, 0, hole, 0, 2*math.pi, True) | |
self.g.stroke() | |
self.g.moveTo(0,length) | |
self.g.beginPath() | |
self.g.arc(0,length, hole, 0, 2*math.pi, True) | |
self.g.stroke() | |
self.g.moveTo(0,0) | |
self.g.beginPath() | |
self.g.arc(0,0, radius, 0, math.pi, True) | |
self.g.moveTo(0+radius,0) | |
self.g.lineTo(0+radius,0+length) | |
self.g.stroke() | |
self.g.moveTo(0,0+length) | |
self.g.beginPath() | |
self.g.arc(0, 0+length, radius, math.pi, 0, True) | |
self.g.moveTo(0-radius,0+length) | |
self.g.lineTo(0-radius,0) | |
self.g.stroke() | |
self.g.restore() | |
''' | |
self.g.beginPath() | |
self.g.fillStyle = "red" | |
self.g.font = "bold 18px sans-serif" | |
self.g.fillText("%d, %d"%(self.p2.x, self.p2.y),self.p2.x, self.p2.y) | |
self.g.stroke() | |
''' | |
class Triangle(object): | |
def __init__(self, p1, p2, p3): | |
self.p1 = p1 | |
self.p2 = p2 | |
self.p3 = p3 | |
def getLenp3(self): | |
p1 = self.p1 | |
ret = p1.distance(self.p2) | |
return ret | |
def getLenp1(self): | |
p2 = self.p2 | |
ret = p2.distance(self.p3) | |
return ret | |
def getLenp2(self): | |
p1 = self.p1 | |
ret = p1.distance(self.p3) | |
return ret | |
# 角度 | |
def getAp1(self): | |
ret = math.acos(((self.getLenp2() * self.getLenp2() + self.getLenp3() * self.getLenp3()) - self.getLenp1() * self.getLenp1()) / (2* self.getLenp2() * self.getLenp3())) | |
return ret | |
# | |
def getAp2(self): | |
ret =math.acos(((self.getLenp1() * self.getLenp1() + self.getLenp3() * self.getLenp3()) - self.getLenp2() * self.getLenp2()) / (2* self.getLenp1() * self.getLenp3())) | |
return ret | |
def getAp3(self): | |
ret = math.acos(((self.getLenp1() * self.getLenp1() + self.getLenp2() * self.getLenp2()) - self.getLenp3() * self.getLenp3()) / (2* self.getLenp1() * self.getLenp2())) | |
return ret | |
def drawMe(self, g): | |
self.g = g | |
r = 5 | |
# 繪出三個頂點 | |
self.p1.drawMe(self.g,r) | |
self.p2.drawMe(self.g,r) | |
self.p3.drawMe(self.g,r) | |
line1 = Line(self.p1,self.p2) | |
line2 = Line(self.p1,self.p3) | |
line3 = Line(self.p2,self.p3) | |
# 繪出三邊線 | |
line1.drawMe(self.g) | |
line2.drawMe(self.g) | |
line3.drawMe(self.g) | |
# ends Triangle def | |
# 透過三個邊長定義三角形 | |
def setSSS(self, lenp3, lenp1, lenp2): | |
self.lenp3 = lenp3 | |
self.lenp1 = lenp1 | |
self.lenp2 = lenp2 | |
self.ap1 = math.acos(((self.lenp2 * self.lenp2 + self.lenp3 * self.lenp3) - self.lenp1 * self.lenp1) / (2* self.lenp2 * self.lenp3)) | |
self.ap2 = math.acos(((self.lenp1 * self.lenp1 + self.lenp3 * self.lenp3) - self.lenp2 * self.lenp2) / (2* self.lenp1 * self.lenp3)) | |
self.ap3 = math.acos(((self.lenp1 * self.lenp1 + self.lenp2 * self.lenp2) - self.lenp3 * self.lenp3) / (2* self.lenp1 * self.lenp2)) | |
# 透過兩個邊長與夾角定義三角形 | |
def setSAS(self, lenp3, ap2, lenp1): | |
self.lenp3 = lenp3 | |
self.ap2 = ap2 | |
self.lenp1 = lenp1 | |
self.lenp2 = math.sqrt((self.lenp3 * self.lenp3 + self.lenp1 * self.lenp1) - 2* self.lenp3 * self.lenp1 * math.cos(self.ap2)) | |
#等於 SSS(AB, BC, CA) | |
def setSaSS(self, lenp2, lenp3, lenp1): | |
self.lenp2 = lenp2 | |
self.lenp3 = lenp3 | |
self.lenp1 = lenp1 | |
if(self.lenp1 > (self.lenp2 + self.lenp3)): | |
#<CAB 夾角為 180 度, 三點共線且 A 介於 BC 之間 | |
ret = math.pi | |
else : | |
# <CAB 夾角為 0, 三點共線且 A 不在 BC 之間 | |
if((self.lenp1 < (self.lenp2 - self.lenp3)) or (self.lenp1 < (self.lenp3 - self.lenp2))): | |
ret = 0.0 | |
else : | |
# 透過餘絃定理求出夾角 <CAB | |
ret = math.acos(((self.lenp2 * self.lenp2 + self.lenp3 * self.lenp3) - self.lenp1 * self.lenp1) / (2 * self.lenp2 * self.lenp3)) | |
return ret | |
# 取得三角形的三個邊長值 | |
def getSSS(self): | |
temp = [] | |
temp.append( self.getLenp1() ) | |
temp.append( self.getLenp2() ) | |
temp.append( self.getLenp3() ) | |
return temp | |
# 取得三角形的三個角度值 | |
def getAAA(self): | |
temp = [] | |
temp.append( self.getAp1() ) | |
temp.append( self.getAp2() ) | |
temp.append( self.getAp3() ) | |
return temp | |
# 取得三角形的三個角度與三個邊長 | |
def getASASAS(self): | |
temp = [] | |
temp.append(self.getAp1()) | |
temp.append(self.getLenp1()) | |
temp.append(self.getAp2()) | |
temp.append(self.getLenp2()) | |
temp.append(self.getAp3()) | |
temp.append(self.getLenp3()) | |
return temp | |
#2P 2L return mid P | |
def setPPSS(self, p1, p3, lenp1, lenp3): | |
temp = [] | |
self.p1 = p1 | |
self.p3 = p3 | |
self.lenp1 = lenp1 | |
self.lenp3 = lenp3 | |
#bp3 is the angle beside p3 point, cp3 is the angle for line23, p2 is the output | |
line31 = Line(p3, p1) | |
self.lenp2 = line31.getR() | |
#self.lenp2 = self.p3.distance(self.p1) | |
#這裡是求角3 | |
ap3 = math.acos(((self.lenp1 * self.lenp1 + self.lenp2 * self.lenp2) - self.lenp3 * self.lenp3) / (2 * self.lenp1 * self.lenp2)) | |
#ap3 = math.acos(((self.lenp1 * self.lenp1 + self.lenp3 * self.lenp3) - self.lenp2 * self.lenp2) / (2 * self.lenp1 * self.lenp3)) | |
bp3 = line31.getT() | |
cp3 = bp3 - ap3 | |
temp.append(p3.x + self.lenp1*math.cos(cp3))#p2.x | |
temp.append(p3.y + self.lenp1*math.sin(cp3))#p2.y | |
return temp |
This file contains 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
# 威力彩電腦選號 | |
# weleadlottery | |
from browser import document, html, alert | |
import random | |
try: | |
total = int(input("請問要出幾張威力彩卷號碼?")) | |
except: | |
alert("請輸入要選擇威力彩電腦選號數量的'整數'") | |
total = int(input("請問要出幾張威力彩卷號碼?")) | |
# 準備將電腦選出的號碼, 輸出到內定 id="brython_div" 的標註區域 | |
output_div = document["brython_div"] | |
output_div <= "以下將出 " + str(total) + " 張威力彩電腦選號彩卷:" + html.BR() | |
for i in range(1, total + 1): | |
# 利用 list(range()) 產生第一區 1 到 38 , 第二區 1 到 8 的 population list | |
# 然後再透過 random.sample(population, k) | |
# 從 population, 產生 k 個不同的數字 | |
section1_numbers = random.sample(list(range(1, 38)), 6) | |
section2_number = random.sample(list(range(1, 8)), 1) | |
output_div <= str(i) + ". 電腦選號第一區為: " + str(section1_numbers) + html.BR() | |
output_div <= ". 電腦選號第二區為: " + str(section2_number) + html.BR() |
This file contains 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
# 畫中華人民共和國國旗 | |
# 根據 https://en.wikipedia.org/wiki/Flag_of_China 規格繪圖 | |
# 導入 doc | |
from browser import document as doc | |
# 以下將利用 html 產生所需的繪圖畫布 | |
from browser import html | |
# 利用 math 函式庫執行三角函數運算 | |
import math | |
canvas = html.CANVAS(width = 600, height = 400) | |
#canvas.style = {"width": "100%"} | |
canvas.id = "taiwan_flag" | |
# 將圖畫至 id 為 brython_div2 的 cnavas 標註 | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
# 準備繪圖畫布 | |
canvas = doc["taiwan_flag"] | |
ctx = canvas.getContext("2d") | |
# 進行座標轉換, x 軸不變, y 軸反向且移動 canvas.height 單位光點 | |
# ctx.setTransform(1, 0, 0, -1, 0, canvas.height) | |
# 以下採用 canvas 原始座標繪圖 | |
flag_w = canvas.width | |
flag_h = canvas.height | |
# 先畫滿地紅 | |
ctx.fillStyle='rgb(255, 0, 0)' | |
ctx.fillRect(0,0,flag_w,flag_h) | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 測試畫直線函式功能 | |
#draw_line(10, 10, 100, 100) | |
# 定義角度轉換為徑度變數 | |
deg = math.pi/180. | |
# 建立五星繪圖函式 | |
#x, y 為中心, r 為半徑, angle 旋轉角, solid 空心或實心, color 顏色 | |
def star(x, y, r, angle=0, solid=False, color="#ff0000"): | |
#以 x, y 為圓心, 計算五個外點 | |
# 圓心到水平線距離 | |
a = r*math.cos(72*deg) | |
# a 頂點向右到內點距離 | |
b = (r*math.cos(72*deg)/math.cos(36*deg))*math.sin(36*deg) | |
# 利用畢氏定理求內點半徑 | |
rin = math.sqrt(a*a + b*b) | |
# 查驗 a, b 與 rin | |
#print(a, b, rin) | |
if solid: | |
ctx.beginPath() | |
# angle 角度先轉 360/10, 讓五星對正 | |
angle = angle + 360/10 | |
for i in range(5): | |
xout = (x + r*math.sin((360/5)*deg*i+angle*deg)) | |
yout = (y + r*math.cos((360/5)*deg*i+angle*deg)) | |
# 外點增量 + 1 | |
xout2 = x + r*math.sin((360/5)*deg*(i+1)+angle*deg) | |
yout2 = y + r*math.cos((360/5)*deg*(i+1)+angle*deg) | |
xin = x + rin*math.sin((360/5)*deg*i+36*deg+angle*deg) | |
yin = y + rin*math.cos((360/5)*deg*i+36*deg+angle*deg) | |
# 查驗外點與內點座標 | |
#print(xout, yout, xin, yin) | |
if solid: | |
# 填色 | |
if i==0: | |
ctx.moveTo(xout, yout) | |
ctx.lineTo(xin, yin) | |
ctx.lineTo(xout2, yout2) | |
else: | |
ctx.lineTo(xin, yin) | |
ctx.lineTo(xout2, yout2) | |
else: | |
# 空心 | |
draw_line(xout, yout, xin, yin, color) | |
# 畫空心五芒星, 無關畫線次序, 若實心則與畫線次序有關 | |
draw_line(xout2, yout2, xin, yin, color) | |
if solid: | |
ctx.fillStyle = color | |
ctx.fill() | |
yellow = "#FFFF00" | |
# 大五星位於 (5x20, 5x20) 半徑為 3x20 | |
star(100, 100, 60, solid=True, color=yellow) | |
unit = int(canvas.width/30) | |
# 計算各小五星座標與轉角 | |
x1 = 10*unit | |
y1 = 2*unit | |
x2 = 12*unit | |
y2 = 4*unit | |
x3 = 12*unit | |
y3 = 7*unit | |
x4 = 10*unit | |
y4 = 9*unit | |
angle1 = 90 + math.atan(3/5)/deg | |
angle2 = 90 + math.atan(1/7)/deg | |
angle3 = 90 - math.atan(2/7)/deg | |
angle4 = 90 - math.atan(4/5)/deg | |
radius = 20 | |
star(x1, y1, radius , angle=angle1, solid=True, color=yellow) | |
star(x2, y2, radius, angle=angle2, solid=True, color=yellow) | |
star(x3, y3, radius, angle=angle3, solid=True, color=yellow) | |
star(x4, y4, radius, angle=angle4, solid=True, color=yellow) |
This file contains 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 https://levelup.gitconnected.com/writing-tetris-in-python-2a16bddb5318 | |
# 暫時關閉 system proxy 設定後, pip install pygame | |
#import pygame | |
import random | |
# 以下為 Brython 新增 | |
from browser import document as doc | |
from browser import html | |
import browser.timer | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 400, height = 500, id="canvas") | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
ctx = canvas.getContext("2d") | |
colors = [ | |
(0, 0, 0), | |
(120, 37, 179), | |
(100, 179, 179), | |
(80, 34, 22), | |
(80, 134, 22), | |
(180, 34, 22), | |
(180, 34, 122), | |
] | |
class Figure: | |
x = 0 | |
y = 0 | |
figures = [ | |
[[1, 5, 9, 13], [4, 5, 6, 7]], | |
[[4, 5, 9, 10], [2, 6, 5, 9]], | |
[[6, 7, 9, 10], [1, 5, 6, 10]], | |
[[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]], | |
[[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]], | |
[[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]], | |
[[1, 2, 5, 6]], | |
] | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
self.type = random.randint(0, len(self.figures) - 1) | |
self.color = random.randint(1, len(colors) - 1) | |
self.rotation = 0 | |
def image(self): | |
return self.figures[self.type][self.rotation] | |
def rotate(self): | |
self.rotation = (self.rotation + 1) % len(self.figures[self.type]) | |
class Tetris: | |
level = 2 | |
score = 0 | |
state = "start" | |
field = [] | |
height = 0 | |
width = 0 | |
x = 100 | |
y = 60 | |
zoom = 20 | |
figure = None | |
def __init__(self, height, width): | |
self.height = height | |
self.width = width | |
self.field = [] | |
self.score = 0 | |
self.state = "start" | |
for i in range(height): | |
new_line = [] | |
for j in range(width): | |
# 起始時每一個都填入 0 | |
new_line.append(0) | |
self.field.append(new_line) | |
def new_figure(self): | |
self.figure = Figure(3, 0) | |
def intersects(self): | |
intersection = False | |
for i in range(4): | |
for j in range(4): | |
if i * 4 + j in self.figure.image(): | |
# block 到達底部, 左右兩邊界, 或該座標有其他 block | |
if i + self.figure.y > self.height - 1 or \ | |
j + self.figure.x > self.width - 1 or \ | |
j + self.figure.x < 0 or \ | |
self.field[i + self.figure.y][j + self.figure.x] > 0: | |
intersection = True | |
return intersection | |
def break_lines(self): | |
lines = 0 | |
for i in range(1, self.height): | |
zeros = 0 | |
for j in range(self.width): | |
if self.field[i][j] == 0: | |
zeros += 1 | |
if zeros == 0: | |
lines += 1 | |
for i1 in range(i, 1, -1): | |
for j in range(self.width): | |
self.field[i1][j] = self.field[i1 - 1][j] | |
self.score += lines ** 2 | |
def go_space(self): | |
while not self.intersects(): | |
self.figure.y += 1 | |
self.figure.y -= 1 | |
self.freeze() | |
def go_down(self): | |
self.figure.y += 1 | |
if self.intersects(): | |
self.figure.y -= 1 | |
self.freeze() | |
def freeze(self): | |
for i in range(4): | |
for j in range(4): | |
if i * 4 + j in self.figure.image(): | |
self.field[i + self.figure.y][j + self.figure.x] = self.figure.color | |
self.break_lines() | |
self.new_figure() | |
if self.intersects(): | |
self.state = "gameover" | |
def go_side(self, dx): | |
old_x = self.figure.x | |
self.figure.x += dx | |
if self.intersects(): | |
self.figure.x = old_x | |
def rotate(self): | |
old_rotation = self.figure.rotation | |
self.figure.rotate() | |
if self.intersects(): | |
self.figure.rotation = old_rotation | |
# Define some colors | |
# from https://stackoverflow.com/questions/3380726/converting-a-rgb-color-tuple-to-a-six-digit-code | |
BLACK = '#%02x%02x%02x' % (0, 0, 0) | |
WHITE = '#%02x%02x%02x' % (255, 255, 255) | |
GRAY = '#%02x%02x%02x' % (128, 128, 128) | |
done = False | |
fps = 25 | |
game = Tetris(20, 10) | |
counter = 0 | |
pressing_down = False | |
def key_down(eve): | |
key = eve.keyCode | |
#if event.type == pygame.QUIT: | |
# 32 is pause | |
if key == 32: | |
done = True | |
# 82 is r key to rotate | |
if key == 82: | |
game.rotate() | |
# 40 is down key | |
if key == 40: | |
pressing_down = True | |
# 37 is left key | |
if key == 37: | |
game.go_side(-1) | |
# 39 is right key | |
if key == 39: | |
game.go_side(1) | |
# 68 is d key to move block to bottom | |
if key == 68: | |
game.go_space() | |
# 27 is escape | |
# reset the game | |
if key == 27: | |
game.__init__(20, 10) | |
def key_up(eve): | |
key = eve.keyCode | |
# 40 is down key | |
if key == 40: | |
pressing_down = False | |
#while not done: | |
def do_game(): | |
global counter | |
if game.figure is None: | |
game.new_figure() | |
counter += 1 | |
if counter > 100000: | |
counter = 0 | |
if counter % (fps // game.level // 2) == 0 or pressing_down: | |
if game.state == "start": | |
game.go_down() | |
for i in range(game.height): | |
for j in range(game.width): | |
ctx.fillStyle = WHITE | |
#ctx.scale(game.zoom, game.zoom) | |
ctx.fillRect(game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom) | |
if game.field[i][j] > 0: | |
ctx.fillStyle = '#%02x%02x%02x' % colors[game.field[i][j]] | |
ctx.fillRect(game.x + game.zoom * j + 1, game.y + game.zoom * i + 1, game.zoom - 2, game.zoom - 1) | |
ctx.lineWidth = 1 | |
ctx.strokeStyle = GRAY | |
ctx.beginPath() | |
ctx.rect(game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom) | |
ctx.stroke() | |
if game.figure is not None: | |
for i in range(4): | |
for j in range(4): | |
p = i * 4 + j | |
if p in game.figure.image(): | |
ctx.fillStyle = '#%02x%02x%02x' % colors[game.figure.color] | |
ctx.fillRect(game.x + game.zoom * (j + game.figure.x) + 1, | |
game.y + game.zoom * (i + game.figure.y) + 1, | |
game.zoom - 2, game.zoom - 2) | |
doc.addEventListener("keydown", key_down) | |
doc.addEventListener("keyup", key_up) | |
browser.timer.set_interval(do_game, fps) |
This file contains 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 https://levelup.gitconnected.com/writing-tetris-in-python-2a16bddb5318 | |
# 改為可自動執行模式 | |
import random | |
# 以下為 Brython 新增 | |
from browser import document as doc | |
from browser import html | |
import browser.timer | |
def intersects(game_field, x, y, game_width, game_height, game_figure_image): | |
intersection = False | |
for i in range(4): | |
for j in range(4): | |
if i * 4 + j in game_figure_image: | |
if i + y > game_height - 1 or \ | |
j + x > game_width - 1 or \ | |
j + x < 0 or \ | |
game_field[i + y][j + x] > 0: | |
intersection = True | |
return intersection | |
def simulate(game_field, x, y, game_width, game_height, game_figure_image): | |
while not intersects(game_field, x, y, game_width, game_height, game_figure_image): | |
y += 1 | |
y -= 1 | |
height = game_height | |
holes = 0 | |
filled = [] | |
breaks = 0 | |
for i in range(game_height-1, -1, -1): | |
it_is_full = True | |
prev_holes = holes | |
for j in range(game_width): | |
u = '_' | |
if game_field[i][j] != 0: | |
u = "x" | |
for ii in range(4): | |
for jj in range(4): | |
if ii * 4 + jj in game_figure_image: | |
if jj + x == j and ii + y == i: | |
u = "x" | |
if u == "x" and i < height: | |
height = i | |
if u == "x": | |
filled.append((i, j)) | |
for k in range(i, game_height): | |
if (k, j) not in filled: | |
holes += 1 | |
filled.append((k,j)) | |
else: | |
it_is_full = False | |
if it_is_full: | |
breaks += 1 | |
holes = prev_holes | |
return holes, game_height-height-breaks | |
def best_rotation_position(game_field, game_figure, game_width, game_height): | |
best_height = game_height | |
best_holes = game_height*game_width | |
best_position = None | |
best_rotation = None | |
for rotation in range(len(game_figure.figures[game_figure.type])): | |
fig = game_figure.figures[game_figure.type][rotation] | |
for j in range(-3, game_width): | |
if not intersects( | |
game_field, | |
j, | |
0, | |
game_width, | |
game_height, | |
fig): | |
holes, height = simulate( | |
game_field, | |
j, | |
0, | |
game_width, | |
game_height, | |
fig | |
) | |
if best_position is None or best_holes > holes or \ | |
best_holes == holes and best_height > height: | |
best_height = height | |
best_holes = holes | |
best_position = j | |
best_rotation = rotation | |
return best_rotation, best_position | |
# 建立一個自動執行的函式 | |
# step 1 | |
''' | |
def run_ai(): | |
game.rotate() | |
''' | |
#step 2 | |
def run_ai(game_field, game_figure, game_width, game_height): | |
rotation, position = best_rotation_position(game_field, game_figure, game_width, game_height) | |
if game_figure.rotation != rotation: | |
game.rotate() | |
elif game_figure.x < position: | |
game.go_side(1) | |
elif game_figure.x > position: | |
game.go_side(-1) | |
else: | |
game.go_space() | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 400, height = 500, id="canvas") | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
ctx = canvas.getContext("2d") | |
colors = [ | |
(0, 0, 0), | |
(120, 37, 179), | |
(100, 179, 179), | |
(80, 34, 22), | |
(80, 134, 22), | |
(180, 34, 22), | |
(180, 34, 122), | |
] | |
class Figure: | |
x = 0 | |
y = 0 | |
figures = [ | |
[[1, 5, 9, 13], [4, 5, 6, 7]], | |
[[4, 5, 9, 10], [2, 6, 5, 9]], | |
[[6, 7, 9, 10], [1, 5, 6, 10]], | |
[[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]], | |
[[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]], | |
[[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]], | |
[[1, 2, 5, 6]], | |
] | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
self.type = random.randint(0, len(self.figures) - 1) | |
self.color = random.randint(1, len(colors) - 1) | |
self.rotation = 0 | |
def image(self): | |
return self.figures[self.type][self.rotation] | |
def rotate(self): | |
self.rotation = (self.rotation + 1) % len(self.figures[self.type]) | |
class Tetris: | |
level = 2 | |
score = 0 | |
state = "start" | |
field = [] | |
height = 0 | |
width = 0 | |
x = 100 | |
y = 60 | |
zoom = 20 | |
figure = None | |
def __init__(self, height, width): | |
self.height = height | |
self.width = width | |
self.field = [] | |
self.score = 0 | |
self.state = "start" | |
for i in range(height): | |
new_line = [] | |
for j in range(width): | |
# 起始時每一個都填入 0 | |
new_line.append(0) | |
self.field.append(new_line) | |
def new_figure(self): | |
self.figure = Figure(3, 0) | |
def intersects(self): | |
intersection = False | |
for i in range(4): | |
for j in range(4): | |
if i * 4 + j in self.figure.image(): | |
# block 到達底部, 左右兩邊界, 或該座標有其他 block | |
if i + self.figure.y > self.height - 1 or \ | |
j + self.figure.x > self.width - 1 or \ | |
j + self.figure.x < 0 or \ | |
self.field[i + self.figure.y][j + self.figure.x] > 0: | |
intersection = True | |
return intersection | |
def break_lines(self): | |
lines = 0 | |
for i in range(1, self.height): | |
zeros = 0 | |
for j in range(self.width): | |
if self.field[i][j] == 0: | |
zeros += 1 | |
if zeros == 0: | |
lines += 1 | |
for i1 in range(i, 1, -1): | |
for j in range(self.width): | |
self.field[i1][j] = self.field[i1 - 1][j] | |
self.score += lines ** 2 | |
def go_space(self): | |
while not self.intersects(): | |
self.figure.y += 1 | |
self.figure.y -= 1 | |
self.freeze() | |
def go_down(self): | |
self.figure.y += 1 | |
if self.intersects(): | |
self.figure.y -= 1 | |
self.freeze() | |
def freeze(self): | |
for i in range(4): | |
for j in range(4): | |
if i * 4 + j in self.figure.image(): | |
self.field[i + self.figure.y][j + self.figure.x] = self.figure.color | |
self.break_lines() | |
self.new_figure() | |
if self.intersects(): | |
self.state = "gameover" | |
def go_side(self, dx): | |
old_x = self.figure.x | |
self.figure.x += dx | |
if self.intersects(): | |
self.figure.x = old_x | |
def rotate(self): | |
old_rotation = self.figure.rotation | |
self.figure.rotate() | |
if self.intersects(): | |
self.figure.rotation = old_rotation | |
# Define some colors | |
# from https://stackoverflow.com/questions/3380726/converting-a-rgb-color-tuple-to-a-six-digit-code | |
BLACK = '#%02x%02x%02x' % (0, 0, 0) | |
WHITE = '#%02x%02x%02x' % (255, 255, 255) | |
GRAY = '#%02x%02x%02x' % (128, 128, 128) | |
RED = '#%02x%02x%02x' % (255, 0, 0) | |
done = False | |
fps = 5 | |
game = Tetris(20, 10) | |
counter = 0 | |
pressing_down = False | |
def key_down(eve): | |
key = eve.keyCode | |
#if event.type == pygame.QUIT: | |
# 32 is pause | |
if key == 32: | |
done = True | |
# 82 is r key to rotate | |
if key == 82: | |
game.rotate() | |
# 40 is down key | |
if key == 40: | |
pressing_down = True | |
# 37 is left key | |
if key == 37: | |
game.go_side(-1) | |
# 39 is right key | |
if key == 39: | |
game.go_side(1) | |
# 68 is d key to move block to bottom | |
if key == 68: | |
game.go_space() | |
# 27 is escape | |
# reset the game | |
if key == 27: | |
# clear the previous score | |
ctx.fillStyle = WHITE | |
ctx.fillRect( 100, 0, 200, 50) | |
game.__init__(20, 10) | |
def key_up(eve): | |
key = eve.keyCode | |
# 40 is down key | |
if key == 40: | |
pressing_down = False | |
#while not done: | |
def do_game(): | |
global counter | |
if game.figure is None: | |
game.new_figure() | |
counter += 1 | |
if counter > 100000: | |
counter = 0 | |
if counter % (fps // game.level // 2) == 0 or pressing_down: | |
if game.state == "start": | |
game.go_down() | |
run_ai(game.field, game.figure, game.width, game.height) | |
for i in range(game.height): | |
for j in range(game.width): | |
ctx.fillStyle = WHITE | |
#ctx.scale(game.zoom, game.zoom) | |
ctx.fillRect(game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom) | |
if game.field[i][j] > 0: | |
ctx.fillStyle = '#%02x%02x%02x' % colors[game.field[i][j]] | |
ctx.fillRect(game.x + game.zoom * j + 1, game.y + game.zoom * i + 1, game.zoom - 2, game.zoom - 1) | |
ctx.lineWidth = 1 | |
ctx.strokeStyle = GRAY | |
ctx.beginPath() | |
ctx.rect(game.x + game.zoom * j, game.y + game.zoom * i, game.zoom, game.zoom) | |
ctx.stroke() | |
if game.figure is not None: | |
for i in range(4): | |
for j in range(4): | |
p = i * 4 + j | |
if p in game.figure.image(): | |
ctx.fillStyle = '#%02x%02x%02x' % colors[game.figure.color] | |
ctx.fillRect(game.x + game.zoom * (j + game.figure.x) + 1, | |
game.y + game.zoom * (i + game.figure.y) + 1, | |
game.zoom - 2, game.zoom - 2) | |
# score and Game Over scripts from https://s40723245.github.io/wcm2022 | |
# 宣告文字的大小為36px | |
ctx.font = '36px serif' | |
# 宣告文字顏色為黑色 | |
ctx.fillStyle = BLACK | |
# 將分數顯示在遊戲區上方, 座標為(10, 50), 並設定變數為text | |
ctx.fillText('Score:'+ str(game.score), 10,50) | |
# 宣告變數int = 1 ,如果分數大於int,則畫布清掉原本的分數填上新的得分分數 | |
int = 1 | |
if game.score >= int: | |
ctx.fillStyle = WHITE | |
ctx.fillRect( 100, 0, 200, 50) | |
ctx.fillStyle = BLACK | |
ctx.fillText(str(game.score), 108,50) | |
# 如果遊戲狀態為gameover,顯示Game Over及Press ESC,並將文字設定為紅色 | |
if game.state == "gameover": | |
ctx.fillStyle = RED | |
ctx.fillText("Game Over", 100, 200) | |
ctx.fillText("Press ESC", 105, 265) | |
ctx.fillStyle = WHITE | |
ctx.fillRect( 100, 0, 200, 50) | |
game.__init__(20, 10) | |
doc.addEventListener("keydown", key_down) | |
doc.addEventListener("keyup", key_up) | |
browser.timer.set_interval(do_game, fps) |
This file contains 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
# Python 的內建常數與函式 | |
# 內建常數 | |
print("Some builtin constants:") | |
print(True) | |
print(False) | |
print(None) | |
print("And some more constants in the math module:") | |
import math | |
print(math.pi) | |
print(math.e) | |
# 內建函式 | |
print("Type conversion functions:") | |
print(bool(0)) # convert to boolean (True or False) | |
print(float(42)) # convert to a floating point number | |
print(int(2.8)) # convert to an integer (int) | |
print("And some basic math functions:") | |
print(abs(-5)) # absolute value | |
print(max(2,3)) # return the max value | |
print(min(2,3)) # return the min value | |
print(pow(2,3)) # raise to the given power (pow(x,y) == x**y) | |
print(round(2.354, 1)) # round with the given number of digits | |
# other examples | |
print(3 * 2) | |
print(3 * "abc") | |
print(3 + 2) | |
print("abc" + "def") | |
print(3 + "def") | |
# Type Affect Semantics | |
print(3 * 2) | |
print(3 * "abc") | |
print(3 + 2) | |
print("abc" + "def") | |
print(3 + "def") | |
# Integer Division | |
print("The / operator does 'normal' float division:") | |
print(" 5/3 =", ( 5/3)) | |
print() | |
print("The // operator does integer division:") | |
print(" 5//3 =", ( 5//3)) | |
print(" 2//3 =", ( 2//3)) | |
print("-1//3 =", (-1//3)) | |
print("-4//3 =", (-4//3)) | |
# Modulus or Remainder Operator (%) | |
print(" 6%3 =", ( 6%3)) | |
print(" 5%3 =", ( 5%3)) | |
print(" 2%3 =", ( 2%3)) | |
print(" 0%3 =", ( 0%3)) | |
print("-4%3 =", (-4%3)) | |
print(" 3%0 =", ( 3%0)) | |
# Verify that (a%b) is equivalent to (a-(a//b)*b) | |
def mod(a, b): | |
return a - (a//b)*b | |
print(41%14, mod(41,14)) | |
print(14%41, mod(14,41)) | |
print(-32%9, mod(-32,9)) | |
print(32%-9, mod(32,-9)) | |
# Operator Order (Precedence and Associativity) | |
print("Precedence:") | |
print(2+3*4) # prints 14, not 20 | |
print(5+4%3) # prints 6, not 0 (% has same precedence as *, /, and //) | |
print(2**3*4) # prints 32, not 4096 (** has higher precedence than *, /, //, and %) | |
print() | |
print("Associativity:") | |
print(5-4-3) # prints -2, not 4 (- associates left-to-right) | |
print(4**3**2) # prints 262144, not 4096 (** associates right-to-left) | |
# Approximate Values of Floating-Point Numbers | |
print(0.1 + 0.1 == 0.2) # True, but... | |
print(0.1 + 0.1 + 0.1 == 0.3) # False! | |
print(0.1 + 0.1 + 0.1) # prints 0.30000000000000004 (uh oh) | |
print((0.1 + 0.1 + 0.1) - 0.3) # prints 5.55111512313e-17 (tiny, but non-zero!) | |
# Equality Testing with math.isclose | |
print("The problem....") | |
d1 = 0.1 + 0.1 + 0.1 | |
d2 = 0.3 | |
print(d1 == d2) # False (never use == with floats!) | |
print() | |
print("The solution...") | |
import math | |
print(math.isclose(d1, d2)) # True! | |
# math.isclose checks if the two numbers are ALMOST equal, within a small error | |
# Short-Circuit Evaluation | |
def yes(): | |
return True | |
def no(): | |
return False | |
def crash(): | |
return 1/0 # crashes! | |
print(no() and crash()) # Works! | |
print(crash() and no()) # Crashes! | |
print (yes() and crash()) # Never runs (due to crash), but would also crash (without short-circuiting) | |
# Or operator | |
def yes(): | |
return True | |
def no(): | |
return False | |
def crash(): | |
return 1/0 # crashes! | |
print(yes() or crash()) # Works! | |
print(crash() or yes()) # Crashes! | |
print(no() or crash()) # Never runs (due to crash), but would also crash (without short-circuiting) | |
# more examples | |
def isPositive(n): | |
result = (n > 0) | |
print("isPositive(",n,") =", result) | |
return result | |
def isEven(n): | |
result = (n % 2 == 0) | |
print("isEven(",n,") =", result) | |
return result | |
print("Test 1: isEven(-4) and isPositive(-4))") | |
print(isEven(-4) and isPositive(-4)) # Calls both functions | |
print("----------") | |
print("Test 2: isEven(-3) and isPositive(-3)") | |
print(isEven(-3) and isPositive(-3)) # Calls only one function! | |
# type vs isinstance | |
# Both type and isinstance can be used to type-check | |
# In general, (isinstance(x, T)) will be more robust than (type(x) == T) | |
print(type("abc") == str) | |
print(isinstance("abc", str)) | |
# We'll see better reasons for this when we cover OOP + inheritance later | |
# in the course. For now, here is one reason: say you wanted to check | |
# if a value is any kind of number (int, float, complex, etc). | |
# You could do: | |
def isNumber(x): | |
return ((type(x) == int) or | |
(type(x) == float)) # are we sure this is ALL kinds of numbers? | |
print(isNumber(1), isNumber(1.1), isNumber(1+2j), isNumber("wow")) | |
# But this is cleaner, and works for all kinds of numbers, including | |
# complex numbers for example: | |
import numbers | |
def isNumber(x): | |
return isinstance(x, numbers.Number) # works for any kind of number | |
print(isNumber(1), isNumber(1.1), isNumber(1+2j), isNumber("wow")) |
This file contains 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
# Python 函式 | |
# Vocabulary | |
x = 5 | |
def f(y, z): | |
result = x + y + z | |
return result | |
print(f(1, 2)) # 8 | |
print(f(3, 4)) # 12 | |
# Vocabulary: | |
# variables: global vs. local | |
# statements vs. expressions | |
# function definitions vs. function calls | |
# parameters vs. arguments | |
# Return Statements | |
# Basic example | |
def isPositive(x): | |
return (x > 0) | |
print(isPositive(5)) # True | |
print(isPositive(-5)) # False | |
print(isPositive(0)) # False | |
# Return ends the function immediately | |
def isPositive(x): | |
print("Hello!") # runs | |
return (x > 0) | |
print("Goodbye!") # does not run ("dead code") | |
print(isPositive(5)) # prints Hello, then True | |
# No return statement --> return None | |
def f(x): | |
x + 42 | |
print(f(5)) # None | |
def f2(x): | |
result = x + 42 | |
print(f2(5)) # None | |
# Print versus Return | |
# This is a common early mistake (confusing print and return): | |
def cubed(x): | |
print(x**3) # Here is the error! | |
cubed(2) # seems to work! | |
print(cubed(3)) # sort of works (but prints None, which is weird) | |
print(2*cubed(4)) # Error! | |
# Once again (correctly) | |
def cubed2(x): | |
return (x**3) # That's better! | |
cubed2(2) # seems to be ignored (why?) | |
print(cubed2(3)) # works! | |
print(2*cubed2(4)) # works! | |
# Different Parameter and Return Types | |
def hypotenuse(a, b): | |
return ((a**2) + (b**2))**0.5 | |
print(hypotenuse(3, 4)) # 5.0 (not 5) | |
print("---------------------") | |
def xor(b1, b2): | |
return ((b1 and (not b2)) or (b2 and (not b1))) # same as (b1 != b2) | |
print(xor(True, True)) # False | |
print(xor(True, False)) # True | |
print(xor(False, True)) # True | |
print(xor(False, False)) # False | |
print("---------------------") | |
def isPositive(n): | |
return (n > 0) | |
print(isPositive(10)) # True | |
print(isPositive(-1.234)) # False | |
# Function Composition | |
def f(w): | |
return 10*w | |
def g(x, y): | |
return f(3*x) + y | |
def h(z): | |
return f(g(z, f(z+1))) | |
print(h(1)) # hint: try the "visualize" feature | |
# Helper Functions | |
def onesDigit(n): | |
return n%10 | |
def largerOnesDigit(x, y): | |
return max(onesDigit(x), onesDigit(y)) | |
print(largerOnesDigit(134, 672)) # 4 | |
print(largerOnesDigit(132, 674)) # Still 4 | |
# Test Functions | |
# A broken test function | |
def onesDigit(n): | |
return n%10 | |
def testOnesDigit(): | |
print("Testing onesDigit()...", end="") | |
assert(onesDigit(5) == 5) | |
assert(onesDigit(123) == 3) | |
assert(onesDigit(100) == 0) | |
assert(onesDigit(999) == 9) | |
print("Passed!") | |
testOnesDigit() # Passed! Why is this bad? | |
# A better version | |
def onesDigit2(n): | |
return n%10 | |
def testOnesDigit2(): | |
print("Testing onesDigit()...", end="") | |
assert(onesDigit2(5) == 5) | |
assert(onesDigit2(123) == 3) | |
assert(onesDigit2(100) == 0) | |
assert(onesDigit2(999) == 9) | |
assert(onesDigit2(-123) == 3) # Added this test | |
print("Passed!") | |
testOnesDigit2() # Crashed! So the test function worked! | |
# Local Variable Scope | |
def f(x): | |
print("In f, x =", x) | |
x += 5 | |
return x | |
def g(x): | |
return f(x*2) + f(x*3) | |
print(g(2)) | |
# Another example | |
def f(x): | |
print("In f, x =", x) | |
x += 7 | |
return round(x / 3) | |
def g(x): | |
x *= 10 | |
return 2 * f(x) | |
def h(x): | |
x += 3 | |
return f(x+4) + g(x) | |
print(h(f(1))) | |
# Global Variable Scope | |
# In general, you should avoid using global variables. | |
# You will even lose style points if you use them! | |
# Still, you need to understand how they work, since others | |
# will use them, and there may also be some very few occasions | |
# where you should use them, too! | |
g = 100 | |
def f(x): | |
return x + g | |
print(f(5)) # 105 | |
print(f(6)) # 106 | |
print(g) # 100 | |
# Another exampl | |
g = 100 | |
def f(x): | |
# If we modify a global variable, we must declare it as global. | |
# Otherwise, Python will assume it is a local variable. | |
global g | |
g += 1 | |
return x + g | |
print(f(5)) # 106 | |
print(f(6)) # 108 | |
print(g) # 102 | |
# Default arguments | |
# Sometimes, a function has a parameter that has a natural default | |
# We can specify that default value in the function definition, | |
# then choose whether or not to include it in the function call. | |
def f(x, y=10): | |
return x + y | |
print(f(5)) # 15 | |
print(f(5,1)) # 6 |
This file contains 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
# Python 的內建資料型別 | |
import math | |
def f(): | |
print("This is a user-defined function") | |
return 42 | |
print("Some basic types in Python:") | |
print(type(2)) # int | |
print(type(2.2)) # float | |
print(type("2.2")) # str (string) | |
print(type(2 < 2.2)) # bool (boolean) | |
print(type(math)) # module | |
print(type(math.tan)) # builtin_function_or_method ("function" in Brython) | |
print(type(f)) # function (user-defined function) | |
print(type(type(42))) # type | |
print("#####################################################") | |
print("And some other types we will see later in the course...") | |
print(type(Exception())) # Exception | |
print(type(range(5))) # range | |
print(type([1,2,3])) # list | |
print(type((1,2,3))) # tuple | |
print(type({1,2})) # set | |
print(type({1:42})) # dict (dictionary or map) | |
print(type(2+3j)) # complex (complex number) (we may not see this type) |
This file contains 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
# 這個程式用於 demo 綠色方塊沿著網格 ㄇ形路徑行走 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 導入亂數模組 | |
from random import random, randint | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c defaul 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
# 從兩個座標點求中心點座標 | |
def center(lx, ly, rx, ry): | |
# lx is x coord of the left up corner | |
# rx is the x coord of th right down corner | |
x = (lx + rx)/2 | |
y = (ly + ry)/2 | |
return x, y | |
# 畫出填色方塊 | |
def draw_rect(gx, gy, gw, gh, color="lime"): | |
# gx is the grid coord at x direction | |
# gy is the grid coord at y direction | |
# gw is the with of the green rect | |
# gh is the height of the green rect | |
lx = origx + (gx-1)*w | |
ly = origy + (gy-1)*h | |
rx = origx + gx*w | |
ry = origy + gy*h | |
cx, cy = center(lx, ly, rx, ry) | |
# glx is the x coord of the left corner | |
# gly is the y coord of the left corner | |
glx = cx - gw/2 | |
gly = cy - gh/2 | |
# 利用設定的顏色值畫出 rectangle | |
ctx.fillStyle = color | |
ctx.fillRect(glx, gly, gw, gh) | |
# 以白色覆蓋位於 (nowx, nowy) | |
# 且比目標方塊長寬各大於 1 pixel的方塊 | |
def wipe(): | |
draw_rect(nowx, nowy, 30+1, 30+1, color="white") | |
# 畫出位於 (nowx, nowy) 的綠色方塊 | |
def draw(): | |
draw_rect(nowx, nowy, 30, 30, color="lime") | |
# 繞著外圍行走 | |
def walk(): | |
global stepx, stepy, go | |
# 去程 | |
if go == True: | |
# 向上 | |
if nowy == hnum and nowx == 1: | |
stepx = 0 | |
stepy = -1 | |
# 向右 | |
elif nowx < wnum and nowy == 1: | |
stepx = 1 | |
stepy = 0 | |
# 向下 | |
elif nowy == 1 and nowx == wnum: | |
stepx = 0 | |
stepy = 1 | |
elif nowx == wnum and nowy == hnum: | |
go = False | |
# 回程 | |
if go == False: | |
# 向上 | |
if nowx == wnum and nowy == hnum: | |
stepx = 0 | |
stepy = -1 | |
# 向左 | |
elif nowy == 1 and nowx > 1: | |
stepx = -1 | |
stepy = 0 | |
# 向下 | |
elif nowx == 1 and nowy == 1: | |
stepx = 0 | |
stepy = 1 | |
elif nowx ==1 and nowy == hnum: | |
stepx = 0 | |
stepy = -1 | |
go = True | |
# 每隔短暫時間即呼叫執行一次的函式 | |
def game(): | |
# 因 nowx 與 nowy 在函式外宣告 | |
# 且在函式內改變對應值, 因此需宣告為 global | |
global nowx, nowy | |
walk() | |
wipe() | |
nowx += stepx | |
nowy += stepy | |
draw() | |
# 綠色方塊起點座標與 x 及 y 方向的座標增量 | |
nowx = 1 | |
nowy = 15 | |
stepx = 0 | |
stepy = 0 | |
go = True | |
# 設定格數 | |
# width 方向格子數 | |
wnum = 15 | |
# height 方向格子數 | |
hnum = 15 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/wnum) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/hnum) - pixel | |
# 設定繪圖座標點起點位置 | |
origx = 1 | |
origy = 1 | |
# 利用 grid 函式畫出格線 | |
grid(origx, origy, w, h, wnum, hnum, pixel=1, color="black") | |
''' | |
# 利用 draw_rect 以 grid 座標畫出正方形, 內建為 lime | |
draw_rect(3, 3, 30, 30) | |
# 利用隨機亂數產生五個紅色方塊 | |
# wnum 與 hnum 為 x 與 y 方向的格子個數 | |
# w 與 h 方向上的方塊 pixel 數 | |
wrect_size = 30 | |
hrect_size = 30 | |
# 利用 for 迴圈產生五個紅色方塊 | |
for i in range(5): | |
xcoord = randint(1, wnum) | |
ycoord = randint(1, hnum) | |
draw_rect(xcoord, ycoord, wrect_size, hrect_size, color="red") | |
''' | |
browser.timer.set_interval(game, 100) |
This file contains 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
# 這個程式用於 demo 綠色方塊沿著網格 U 形路徑行走 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 導入亂數模組 | |
from random import random, randint | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div2"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c defaul 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
# 從兩個座標點求中心點座標 | |
def center(lx, ly, rx, ry): | |
# lx is x coord of the left up corner | |
# rx is the x coord of th right down corner | |
x = (lx + rx)/2 | |
y = (ly + ry)/2 | |
return x, y | |
# 畫出填色方塊 | |
def draw_rect(gx, gy, gw, gh, color="lime"): | |
# gx is the grid coord at x direction | |
# gy is the grid coord at y direction | |
# gw is the with of the green rect | |
# gh is the height of the green rect | |
lx = origx + (gx-1)*w | |
ly = origy + (gy-1)*h | |
rx = origx + gx*w | |
ry = origy + gy*h | |
cx, cy = center(lx, ly, rx, ry) | |
# glx is the x coord of the left corner | |
# gly is the y coord of the left corner | |
glx = cx - gw/2 | |
gly = cy - gh/2 | |
# 利用設定的顏色值畫出 rectangle | |
ctx.fillStyle = color | |
ctx.fillRect(glx, gly, gw, gh) | |
# 以白色覆蓋位於 (nowx, nowy) | |
# 且比目標方塊長寬各大於 1 pixel的方塊 | |
def wipe(): | |
draw_rect(nowx, nowy, 30+1, 30+1, color="white") | |
# 畫出位於 (nowx, nowy) 的綠色方塊 | |
def draw(): | |
draw_rect(nowx, nowy, 30, 30, color="lime") | |
# 繞著外圍行走 | |
def walk(): | |
global stepx, stepy, go | |
# 去程 | |
if go == True: | |
# 向下 | |
if nowy < hnum and nowx == 1: | |
stepx = 0 | |
stepy = 1 | |
# 向右 | |
elif nowx < wnum and nowy == hnum: | |
stepx = 1 | |
stepy = 0 | |
# 向上 | |
elif nowy == hnum and nowx == wnum: | |
stepx = 0 | |
stepy = -1 | |
elif nowx == wnum and nowy == 1: | |
go = False | |
# 回程 | |
if go == False: | |
# 向下 | |
if nowx == wnum and nowy == 1: | |
stepx = 0 | |
stepy = 1 | |
# 向左 | |
elif nowy == hnum and nowx > 1: | |
stepx = -1 | |
stepy = 0 | |
# 向上 | |
elif nowx == 1 and nowy == hnum: | |
stepx = 0 | |
stepy = -1 | |
elif nowx ==1 and nowy == 1: | |
stepx = 0 | |
stepy = 1 | |
go = True | |
# 每隔短暫時間即呼叫執行一次的函式 | |
def game(): | |
# 因 nowx 與 nowy 在函式外宣告 | |
# 且在函式內改變對應值, 因此需宣告為 global | |
global nowx, nowy | |
walk() | |
wipe() | |
nowx += stepx | |
nowy += stepy | |
draw() | |
# 綠色方塊起點座標與 x 及 y 方向的座標增量 | |
nowx = 1 | |
nowy = 1 | |
stepx = 0 | |
stepy = 0 | |
go = True | |
# 設定格數 | |
# width 方向格子數 | |
wnum = 15 | |
# height 方向格子數 | |
hnum = 15 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/wnum) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/hnum) - pixel | |
# 設定繪圖座標點起點位置 | |
origx = 1 | |
origy = 1 | |
# 利用 grid 函式畫出格線 | |
grid(origx, origy, w, h, wnum, hnum, pixel=1, color="black") | |
''' | |
# 利用 draw_rect 以 grid 座標畫出正方形, 內建為 lime | |
draw_rect(3, 3, 30, 30) | |
# 利用隨機亂數產生五個紅色方塊 | |
# wnum 與 hnum 為 x 與 y 方向的格子個數 | |
# w 與 h 方向上的方塊 pixel 數 | |
wrect_size = 30 | |
hrect_size = 30 | |
# 利用 for 迴圈產生五個紅色方塊 | |
for i in range(5): | |
xcoord = randint(1, wnum) | |
ycoord = randint(1, hnum) | |
draw_rect(xcoord, ycoord, wrect_size, hrect_size, color="red") | |
''' | |
browser.timer.set_interval(game, 100) |
This file contains 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
# 這個程式用於 demo 綠色方塊沿著特定網格路徑行走 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 導入亂數模組 | |
from random import random, randint | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div2 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div2"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c defaul 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
# 從兩個座標點求中心點座標 | |
def center(lx, ly, rx, ry): | |
# lx is x coord of the left up corner | |
# rx is the x coord of th right down corner | |
x = (lx + rx)/2 | |
y = (ly + ry)/2 | |
return x, y | |
# 畫出填色方塊 | |
def draw_rect(gx, gy, gw, gh, color="lime"): | |
# gx is the grid coord at x direction | |
# gy is the grid coord at y direction | |
# gw is the with of the green rect | |
# gh is the height of the green rect | |
lx = origx + (gx-1)*w | |
ly = origy + (gy-1)*h | |
rx = origx + gx*w | |
ry = origy + gy*h | |
cx, cy = center(lx, ly, rx, ry) | |
# glx is the x coord of the left corner | |
# gly is the y coord of the left corner | |
glx = cx - gw/2 | |
gly = cy - gh/2 | |
# 利用設定的顏色值畫出 rectangle | |
ctx.fillStyle = color | |
ctx.fillRect(glx, gly, gw, gh) | |
# 以白色覆蓋位於 (nowx, nowy) | |
# 且比目標方塊長寬各大於 1 pixel的方塊 | |
def wipe(): | |
draw_rect(nowx, nowy, 30+1, 30+1, color="white") | |
# 畫出位於 (nowx, nowy) 的綠色方塊 | |
def draw(): | |
draw_rect(nowx, nowy, 30, 30, color="lime") | |
# 繞著外圍行走 | |
def walk(): | |
global stepx, stepy | |
# 向下 | |
if nowy < hnum and nowx == 1: | |
stepx = 0 | |
stepy = 1 | |
# 向右 | |
elif nowx < wnum and nowy == hnum: | |
stepx = 1 | |
stepy = 0 | |
# 向上 | |
elif nowy == hnum and nowx == wnum: | |
stepx = 0 | |
stepy = -1 | |
# 向左 | |
elif nowx == wnum and nowy == 1: | |
stepx = -1 | |
stepy = 0 | |
# 每隔短暫時間即呼叫執行一次的函式 | |
def game(): | |
# 因 nowx 與 nowy 在函式外宣告 | |
# 且在函式內改變對應值, 因此需宣告為 global | |
global nowx, nowy | |
walk() | |
wipe() | |
nowx += stepx | |
nowy += stepy | |
draw() | |
# 綠色方塊起點座標與 x 及 y 方向的座標增量 | |
nowx = 1 | |
nowy = 1 | |
stepx = 0 | |
stepy = 0 | |
# 設定格數 | |
# width 方向格子數 | |
wnum = 15 | |
# height 方向格子數 | |
hnum = 15 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/wnum) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/hnum) - pixel | |
# 設定繪圖座標點起點位置 | |
origx = 1 | |
origy = 1 | |
# 利用 grid 函式畫出格線 | |
grid(origx, origy, w, h, wnum, hnum, pixel=1, color="black") | |
''' | |
# 利用 draw_rect 以 grid 座標畫出正方形, 內建為 lime | |
draw_rect(3, 3, 30, 30) | |
# 利用隨機亂數產生五個紅色方塊 | |
# wnum 與 hnum 為 x 與 y 方向的格子個數 | |
# w 與 h 方向上的方塊 pixel 數 | |
wrect_size = 30 | |
hrect_size = 30 | |
# 利用 for 迴圈產生五個紅色方塊 | |
for i in range(5): | |
xcoord = randint(1, wnum) | |
ycoord = randint(1, hnum) | |
draw_rect(xcoord, ycoord, wrect_size, hrect_size, color="red") | |
''' | |
browser.timer.set_interval(game, 100) |
This file contains 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
# 畫中華民國國旗 | |
# 導入 doc | |
from browser import document as doc | |
# 以下將利用 html 產生所需的繪圖畫布 | |
from browser import html | |
# 利用 math 函式庫執行三角函數運算 | |
import math | |
canvas = html.CANVAS(width = 600, height = 400) | |
#canvas.style = {"width": "100%"} | |
canvas.id = "taiwan_flag" | |
# 將圖畫至 id 為 brython_div2 的 cnavas 標註 | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
# 準備繪圖畫布 | |
canvas = doc["taiwan_flag"] | |
ctx = canvas.getContext("2d") | |
# 進行座標轉換, x 軸不變, y 軸反向且移動 canvas.height 單位光點 | |
# ctx.setTransform(1, 0, 0, -1, 0, canvas.height) | |
# 以下採用 canvas 原始座標繪圖 | |
flag_w = canvas.width | |
flag_h = canvas.height | |
circle_x = flag_w/4 | |
circle_y = flag_h/4 | |
# 先畫滿地紅 | |
ctx.fillStyle='rgb(255, 0, 0)' | |
ctx.fillRect(0,0,flag_w,flag_h) | |
# 再畫青天 | |
ctx.fillStyle='rgb(0, 0, 150)' | |
ctx.fillRect(0,0,flag_w/2,flag_h/2) | |
# 畫十二道光芒白日 | |
ctx.beginPath() | |
star_radius = flag_w/8 | |
angle = 0 | |
for i in range(24): | |
angle += 5*math.pi*2/12 | |
toX = circle_x + math.cos(angle)*star_radius | |
toY = circle_y + math.sin(angle)*star_radius | |
# 只有 i 為 0 時移動到 toX, toY, 其餘都進行 lineTo | |
if (i): | |
ctx.lineTo(toX, toY) | |
else: | |
ctx.moveTo(toX, toY) | |
ctx.closePath() | |
# 將填色設為白色 | |
ctx.fillStyle = '#fff' | |
ctx.fill() | |
# 白日:藍圈 | |
ctx.beginPath() | |
ctx.arc(circle_x, circle_y, flag_w*17/240, 0, math.pi*2, True) | |
ctx.closePath() | |
# 填色設為藍色 | |
ctx.fillStyle = 'rgb(0, 0, 149)' | |
ctx.fill() | |
# 白日:白心 | |
ctx.beginPath() | |
ctx.arc(circle_x, circle_y, flag_w/16, 0, math.pi*2, True) | |
ctx.closePath() | |
# 填色設為白色 | |
ctx.fillStyle = '#fff' | |
ctx.fill() |
This file contains 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
# 這個程式用於 demo 綠色方塊往隨機產生的紅色方塊位置移動 | |
# 從 Brython 程式庫中的 browser 模組導入 document 類別, 並以簡寫設定為 doc | |
from browser import document as doc | |
# 從 browser 模組導入 html 類別, 主要用於建立 CANVAS 標註物件, 並插入頁面中 | |
from browser import html | |
# 用於定時執行特定函式 | |
import browser.timer | |
# 導入亂數模組 | |
from random import random, randint | |
# 利用 html 建立一個 CANVAS 標註物件, 與變數 canvas 對應 | |
canvas = html.CANVAS(width = 600, height = 600) | |
# 將 canvas 標註的 id 設為 "canvas" | |
canvas.id = "canvas" | |
# 將 document 中 id 為 "brython_div2" 的標註 | |
# 設為與 brython_div 變數對應 | |
brython_div = doc["brython_div2"] | |
# 將 canvas 標註放入 brython_div 所在位置 | |
# 頁面中原本就已經放入 <div id="brython_div2"></div> 標註 | |
brython_div <= canvas | |
# 將頁面中 id 為 canvas 的 CANVAS 設為與 canvas 變數對應 | |
canvas = doc["canvas"] | |
# 將 canvas 的 2d 繪圖 context 命名為 ctx | |
ctx = canvas.getContext("2d") | |
# 建立一個 dRect() 函式 | |
# s default 為 1, c default 為紅色 | |
def dRect(lux, luy, w, h, s=1, c='#ff0000'): | |
ctx.lineWidth = s | |
ctx.strokeStyle = c | |
ctx.beginPath(); | |
ctx.rect(lux, luy, w, h) | |
ctx.stroke(); | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 建立 write Text 函式 | |
def wText(x, y, t, s=14, c='#0000ff'): | |
ctx.font = str(s) + "px Arial"; | |
ctx.fillText(t, x, y) | |
# 定義畫格線的函式 | |
def grid(startx, starty, w, h, wnum, hnum, pixel=1, color="#ff0000"): | |
# 利用迴圈與座標增量繪圖 | |
# 因為輸入 wnum 與 hnum 為格子數, 畫格線數則需加上 1 | |
for i in range(wnum+1): | |
for j in range(hnum+1): | |
# 畫上下直線 | |
yend = starty + h*(hnum) | |
xend = startx + w*(wnum) | |
x = startx + i*w | |
draw_line(x, starty, x, yend, color) | |
# 畫左右直線 | |
y = starty + j*h | |
draw_line(startx, y, xend, y, color) | |
#wText(w/2-10, y-w/2, str(j)) | |
# 從兩個座標點求中心點座標 | |
def center(lx, ly, rx, ry): | |
# lx is x coord of the left up corner | |
# rx is the x coord of th right down corner | |
x = (lx + rx)/2 | |
y = (ly + ry)/2 | |
return x, y | |
# 畫出填色方塊 | |
def draw_rect(gx, gy, gw, gh, color="lime"): | |
# gx is the grid coord at x direction | |
# gy is the grid coord at y direction | |
# gw is the width of the green rect | |
# gh is the height of the green rect | |
lx = origx + (gx-1)*w | |
ly = origy + (gy-1)*h | |
rx = origx + gx*w | |
ry = origy + gy*h | |
cx, cy = center(lx, ly, rx, ry) | |
# glx is the x coord of the left corner | |
# gly is the y coord of the left corner | |
glx = cx - gw/2 | |
gly = cy - gh/2 | |
# 利用設定的顏色值畫出 rectangle | |
ctx.fillStyle = color | |
ctx.fillRect(glx, gly, gw, gh) | |
# 以白色覆蓋位於 (nowx, nowy) | |
# 且比目標方塊長寬各大於 1 pixel的方塊 | |
def wipe(): | |
draw_rect(nowx, nowy, 30+1, 30+1, color="white") | |
# 畫出位於 (nowx, nowy) 的綠色方塊 | |
def draw(): | |
draw_rect(nowx, nowy, 30, 30, color="lime") | |
# 以隨機方式在格點座標中畫出紅色方塊 | |
def draw_red(): | |
draw_rect(redx, redy, wrect_size, hrect_size, color="red") | |
# 綠色方塊往紅色方塊位置移動, 抵達目標位置後停止移動 | |
def walk(): | |
global stepx, stepy | |
if nowx > redx: | |
stepx = -1 | |
stepy = 0 | |
if nowx < redx: | |
stepx = 1 | |
stepy = 0 | |
if nowy > redy: | |
stepx = 0 | |
stepy = -1 | |
if nowy < redy: | |
stepx = 0 | |
stepy = 1 | |
if nowx == redx and nowy == redy: | |
stepx = 0 | |
stepy = 0 | |
# 每隔短暫時間即呼叫執行一次的函式 | |
def game(): | |
# 因 nowx, nowy, redx, redy 在函式外宣告 | |
# 且在函式內改變對應值, 因此需宣告為 global | |
global nowx, nowy, redx, redy | |
# 當綠色方塊移動至紅色方塊座標後, 產生另一個紅色目標座標值 | |
if nowx == redx and nowy == redy: | |
# wnum 為 width 方向的格子數 | |
# hnum 為 height 方向的格子數 | |
redx = randint(1, wnum) | |
redy = randint(1, hnum) | |
draw_red() | |
walk() | |
wipe() | |
nowx += stepx | |
nowy += stepy | |
draw() | |
# 綠色方塊起點座標與 x 及 y 方向的座標增量 | |
nowx = 1 | |
nowy = 1 | |
stepx = 0 | |
stepy = 0 | |
go = True | |
# 設定格數 | |
# width 方向格子數 | |
wnum = 15 | |
# height 方向格子數 | |
hnum = 15 | |
# 紅色方塊座標起始座標位於右下角 | |
redx = wnum-1 | |
redy = hnum-1 | |
# 設定線寬 | |
pixel = 1 | |
# 設定 w 寬度 | |
w = int(canvas.width/wnum) - pixel | |
# 設定 h 高度 | |
h = int(canvas.height/hnum) - pixel | |
# 設定紅色方塊寬度與高度, 分別設為格子大小的 70% | |
wrect_size = int(w*0.7) | |
hrect_size = int(h*0.7) | |
# 設定繪圖座標點起點位置 | |
origx = 1 | |
origy = 1 | |
# 利用 grid 函式畫出格線 | |
grid(origx, origy, w, h, wnum, hnum, pixel=1, color="black") | |
browser.timer.set_interval(game, 100) |
This file contains 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_score2(new_score): | |
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 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 | |
def game2(): | |
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_score2(score) | |
ctx.fillStyle = "red" | |
ctx.fillRect(ax*gs, ay*gs, gs-2, gs-2) | |
def key_push2(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_instructions2(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-board2" | |
brython_div = doc["brython_div"] | |
brython_div <= canvas | |
score_doc = html.DIV("score2") | |
score_doc.id = "score2" | |
brython_div <= score_doc | |
high_score_doc = html.DIV("high-score2") | |
high_score_doc.id = "high-score2" | |
brython_div <= high_score_doc | |
button = html.BUTTON("Keys to control") | |
button.id = "instructions-btn2" | |
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_push2) | |
instructions_btn = doc["instructions-btn2"] | |
instructions_btn.addEventListener("click", show_instructions2) | |
browser.timer.set_interval(game2, 1000/15) |
This file contains 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
# 先將無法在 Brython 環境中執行的程式碼轉為 comment | |
#import curses | |
#from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN | |
from random import randint | |
# 貪食蛇運動的場地長寬 | |
HEIGHT = 10 | |
WIDTH = 20 | |
# FIELD_SIZE 為場地長乘以寬表示格子 (CELL) 總數 | |
FIELD_SIZE = HEIGHT * WIDTH | |
# 貪食蛇頭總是位於snake數列的第一個元素 | |
HEAD = 0 | |
# 用來代表不同意義的數字,由於矩陣上每個格子會處理成到達食物的路徑長度, | |
# 因此這三個變數間需要有足夠大的間隔(>HEIGHT*WIDTH) | |
# 以整數 0 代表 FOOD, 意即若 board 數列中某一元素 | |
# 將隨機取得座標後將該數列索引值設為 0 就表示該格子為 FOOD | |
# UNDEFINED 值之所以必須大於 HEIGHT*WIDTH, 因為該值將在 BFS 後 | |
# 表示蛇頭距離 FOOD 的路徑步數, 而最大距離步數將會是 HEIGHT*WIDTH | |
# SNAKE 以整數代表, 由於必須有別於 UNDEFINED 與 FOOD 的值, 因此選擇 | |
# 以 2 * UNDEFINED 這個數字代表該格子被 snake 身體佔住 | |
FOOD = 0 | |
UNDEFINED = (HEIGHT + 1) * (WIDTH + 1) | |
SNAKE = 2 * UNDEFINED | |
# 由於 snake 是一維數列,所以對應元素直接加上以下數值就表示向四個方向移動 | |
# 應該是說, 原本該以二維座標表示 board 中各格子的座標, 但在此選擇以一維 | |
# 數列的資料結構來代表二維座標, (1, 1) 可表示為 1*WIDTH+1, | |
# (x, y) 則表示為 x*WIDTH+y | |
# 因此往上或往下的移動, 就一維數列值而言, 必須減或加上 WIDTH | |
LEFT = -1 | |
RIGHT = 1 | |
UP = -WIDTH | |
DOWN = WIDTH | |
# 錯誤碼 | |
ERR = -1111 | |
# 用一維數列來表示二維的座標, 使用此資料結構的原因是: | |
# 貪食蛇每行進一步, 只需要配合蛇頭移動位置新增一個方格座標, | |
# 且更改蛇尾對應值 (即從原先的蛇身因行進移動一步而變更設定) | |
# 且讓 snake[HEAD] = x*WIDTH+y, 假設蛇長 snake_size=n | |
# 則蛇尾 snake[HEAD+n-1] 假設位於 xn*WIDTH+yn | |
# board[x*WIDTH+y]=SNAKE=2 * UNDEFINED | |
# board[xn*WIDTH+yn] 表示為蛇尾, 蛇頭走一步後, 蛇尾從 2 * UNDEFINED | |
# 轉為空白可在搜尋流程中加上距離食物的總步數 | |
# board 表示蛇運動的矩形場地 | |
# 初始化蛇頭在(1,1)的地方,第0行,HEIGHT行,第0列,WIDTH列為圍牆,不可用 | |
# 初始蛇長度為1 | |
# board 與 snake 均為總元素為格點數大小的一維數列 | |
board = [0] * FIELD_SIZE | |
#snake = [0] * (FIELD_SIZE+1) | |
# 原程式加 1 | |
snake = [0] * (FIELD_SIZE) | |
# 座標 (1, 1) 在一維數列中, 表示為 1*WIDTH+1 | |
snake[HEAD] = 1*WIDTH+1 | |
snake_size = 1 | |
# 與上面變量對應的臨時變量,蛇試探性地移動時使用 | |
tmpboard = [0] * FIELD_SIZE | |
#tmpsnake = [0] * (FIELD_SIZE+1) | |
# 原程式加 1 | |
tmpsnake = [0] * (FIELD_SIZE) | |
tmpsnake[HEAD] = 1*WIDTH+1 | |
tmpsnake_size = 1 | |
# food:食物位置(0~FIELD_SIZE-1),初始在(3, 3) | |
# best_move: 運動方向 | |
food = 3 * WIDTH + 3 | |
best_move = ERR | |
# 運動方向數組 | |
mov = [LEFT, RIGHT, UP, DOWN] | |
# 接收到的鍵和分數 | |
#key = KEY_RIGHT | |
# 初始蛇為一節 | |
score = 1 #分數也表示蛇長 | |
# 檢查一個 cell 有沒有被蛇身覆蓋,沒有覆蓋則為 free,返回 true | |
def is_cell_free(idx, psize, psnake): | |
return not (idx in psnake[:psize]) | |
# 檢查某個位置idx是否可向move方向運動 | |
def is_move_possible(idx, move): | |
flag = False | |
if move == LEFT: | |
flag = True if idx%WIDTH > 1 else False | |
elif move == RIGHT: | |
flag = True if idx%WIDTH < (WIDTH-2) else False | |
elif move == UP: | |
flag = True if idx > (2*WIDTH-1) else False # 即idx/WIDTH > 1 | |
elif move == DOWN: | |
flag = True if idx < (FIELD_SIZE-2*WIDTH) else False # 即idx//WIDTH < HEIGHT-2 | |
return flag | |
# 重置 board | |
# board_refresh 後,UNDEFINED 值都變為了到達食物的路徑長度 | |
# 如需要還原,則要重置它 | |
def board_reset(psnake, psize, pboard): | |
# 查驗所有格點內容 | |
for i in range(FIELD_SIZE): | |
if i == food: | |
pboard[i] = FOOD | |
elif is_cell_free(i, psize, psnake): # 該位置為空 | |
pboard[i] = UNDEFINED | |
else: # 該位置為蛇身 | |
pboard[i] = SNAKE | |
# 廣度優先搜索遍歷整個 board, | |
# 計算出 board 中每個非 SNAKE 元素到達食物的路徑長度 | |
def board_refresh(pfood, psnake, pboard): | |
queue = [] | |
queue.append(pfood) | |
inqueue = [0] * FIELD_SIZE | |
found = False | |
# while 循環結束後,除了蛇的身體, | |
# 其它每個方格中的數字代碼從它到食物的路徑長度 | |
while len(queue)!=0: | |
idx = queue.pop(0) | |
if inqueue[idx] == 1: continue | |
inqueue[idx] = 1 | |
for i in range(4): | |
if is_move_possible(idx, mov[i]): | |
if idx + mov[i] == psnake[HEAD]: | |
found = True | |
if pboard[idx+mov[i]] < SNAKE: # 如果該點不是蛇的身體 | |
if pboard[idx+mov[i]] > pboard[idx]+1: | |
pboard[idx+mov[i]] = pboard[idx] + 1 | |
if inqueue[idx+mov[i]] == 0: | |
queue.append(idx+mov[i]) | |
return found | |
# 從蛇頭開始,根據 board 中元素值, | |
# 從蛇頭周圍 4 個領域點中選擇最短路徑 | |
def choose_shortest_safe_move(psnake, pboard): | |
best_move = ERR | |
min = SNAKE | |
for i in range(4): | |
if is_move_possible(psnake[HEAD], mov[i]) and pboard[psnake[HEAD]+mov[i]]<min: | |
min = pboard[psnake[HEAD]+mov[i]] | |
best_move = mov[i] | |
return best_move | |
# 從蛇頭開始,根據board中元素值, | |
# 從蛇頭周圍 4 個領域點中選擇最遠路徑 | |
def choose_longest_safe_move(psnake, pboard): | |
best_move = ERR | |
max = -1 | |
for i in range(4): | |
if is_move_possible(psnake[HEAD], mov[i]) and pboard[psnake[HEAD]+mov[i]]<UNDEFINED and pboard[psnake[HEAD]+mov[i]]>max: | |
max = pboard[psnake[HEAD]+mov[i]] | |
best_move = mov[i] | |
return best_move | |
# 檢查是否可以追著蛇尾運動, 即蛇頭和蛇尾間是有路徑的 | |
# 為的是避免蛇頭陷入死路 | |
# 虛擬操作, 在 tmpboard,tmpsnake 中進行 | |
def is_tail_inside(): | |
global tmpboard, tmpsnake, food, tmpsnake_size | |
tmpboard[tmpsnake[tmpsnake_size-1]] = 0 # 虛擬地將蛇尾變為食物(因為是虛擬的,所以在tmpsnake,tmpboard中進行) | |
tmpboard[food] = SNAKE # 放置食物的地方,看成蛇身 | |
result = board_refresh(tmpsnake[tmpsnake_size-1], tmpsnake, tmpboard) # 求得每個位置到蛇尾的路徑長度 | |
for i in range(4): # 如果蛇頭和蛇尾緊挨著,則返回 False。即不能 follow_tail,追著蛇尾運動了 | |
if is_move_possible(tmpsnake[HEAD], mov[i]) and tmpsnake[HEAD]+mov[i]==tmpsnake[tmpsnake_size-1] and tmpsnake_size>3: | |
result = False | |
return result | |
# 讓蛇頭朝著蛇尾運行一步 | |
# 不管蛇身阻擋,朝蛇尾方向運行 | |
def follow_tail(): | |
global tmpboard, tmpsnake, food, tmpsnake_size | |
tmpsnake_size = snake_size | |
tmpsnake = snake[:] | |
board_reset(tmpsnake, tmpsnake_size, tmpboard) # 重置虛擬board | |
tmpboard[tmpsnake[tmpsnake_size-1]] = FOOD # 讓蛇尾成為食物 | |
tmpboard[food] = SNAKE # 讓食物的地方變成蛇身 | |
board_refresh(tmpsnake[tmpsnake_size-1], tmpsnake, tmpboard) # 求得各個位置到達蛇尾的路徑長度 | |
tmpboard[tmpsnake[tmpsnake_size-1]] = SNAKE # 還原蛇尾 | |
return choose_longest_safe_move(tmpsnake, tmpboard) # 返回運行方向(讓蛇頭運動 1 步) | |
# 在各種方案都不行時,隨便找一個可行的方向來走(1 步), | |
def any_possible_move(): | |
global food , snake, snake_size, board | |
best_move = ERR | |
board_reset(snake, snake_size, board) | |
board_refresh(food, snake, board) | |
min = SNAKE | |
for i in range(4): | |
if is_move_possible(snake[HEAD], mov[i]) and board[snake[HEAD]+mov[i]]<min: | |
min = board[snake[HEAD]+mov[i]] | |
best_move = mov[i] | |
return best_move | |
def shift_array(arr, size): | |
for i in range(size, 0, -1): | |
arr[i] = arr[i-1] | |
def new_food(): | |
global food, snake_size | |
cell_free = False | |
while not cell_free: | |
w = randint(1, WIDTH-2) | |
h = randint(1, HEIGHT-2) | |
# food coordinate | |
food = h * WIDTH + w | |
cell_free = is_cell_free(food, snake_size, snake) | |
win.addch(food//WIDTH, food%WIDTH, '@') | |
# 真正的蛇在這個函數中, 朝 pbest_move 走 1 步 | |
def make_move(pbest_move): | |
global key, snake, board, snake_size, score | |
shift_array(snake, snake_size) | |
snake[HEAD] += pbest_move | |
# 按 esc 退出,getch 同時保證繪圖的流暢性, 沒有它只會看到最終結果 | |
#win.timeout(10) | |
#event = win.getch() | |
#key = key if event == -1 else event | |
#if key == 27: return | |
p = snake[HEAD] | |
#win.addch(p//WIDTH, p%WIDTH, '*') | |
# 如果新加入的蛇頭就是食物的位置 | |
# 蛇長加 1,產生新的食物,重置 board (因為原來那些路徑長度已經用不上了) | |
# snake[HEAD] is the coordinate of the snake head | |
# food is the coordinate of the food | |
if snake[HEAD] == food: | |
# mark on the board where the snake head is | |
board[snake[HEAD]] = SNAKE # 新的蛇頭 | |
snake_size += 1 | |
score += 1 | |
if snake_size < FIELD_SIZE: new_food() | |
else: # 如果新加入的蛇頭不是食物的位置 | |
board[snake[HEAD]] = SNAKE # 新的蛇頭 | |
board[snake[snake_size]] = UNDEFINED # 蛇尾變為空格 | |
#win.addch(snake[snake_size]//WIDTH, snake[snake_size]%WIDTH, ' ') | |
# 虛擬地運行一次,然後在調用處檢查這次運行可否可行 | |
# 可行才真實運行。 | |
# 虛擬運行吃到食物後,得到虛擬下蛇在 board 的位置 | |
def virtual_shortest_move(): | |
global snake, board, snake_size, tmpsnake, tmpboard, tmpsnake_size, food | |
tmpsnake_size = snake_size | |
tmpsnake = snake[:] # 如果直接tmpsnake=snake,則兩者指向同一處內存 | |
tmpboard = board[:] # board中已經是各位置到達食物的路徑長度了,不用再計算 | |
board_reset(tmpsnake, tmpsnake_size, tmpboard) | |
food_eated = False | |
while not food_eated: | |
board_refresh(food, tmpsnake, tmpboard) | |
move = choose_shortest_safe_move(tmpsnake, tmpboard) | |
shift_array(tmpsnake, tmpsnake_size) | |
tmpsnake[HEAD] += move # 在蛇頭前加入一個新的位置 | |
# 如果新加入的蛇頭的位置正好是食物的位置 | |
# 則長度加1,重置board,食物那個位置變為蛇的一部分(SNAKE) | |
if tmpsnake[HEAD] == food: | |
tmpsnake_size += 1 | |
board_reset(tmpsnake, tmpsnake_size, tmpboard) # 虛擬運行後,蛇在board的位置(label101010) | |
tmpboard[food] = SNAKE | |
food_eated = True | |
else: # 如果蛇頭不是食物的位置,則新加入的位置為蛇頭,最後一個變為空格 | |
tmpboard[tmpsnake[HEAD]] = SNAKE | |
tmpboard[tmpsnake[tmpsnake_size]] = UNDEFINED | |
# 如果蛇與食物間有路徑,則調用本函數 | |
def find_safe_way(): | |
global snake, board | |
safe_move = ERR | |
# 虛擬地運行一次, 因為已經確保蛇與食物間有路徑,所以執行有效 | |
# 運行後得到虛擬下蛇在board中的位置, 即 tmpboard,見 label101010 | |
virtual_shortest_move() # 該函數唯一調用處 | |
if is_tail_inside(): # 如果虛擬運行後,蛇頭蛇尾間有通路,則選最短路運行(1步) | |
return choose_shortest_safe_move(snake, board) | |
safe_move = follow_tail() # 否則虛擬地follow_tail 1步,如果可以做到,返回 true | |
return safe_move | |
#curses.initscr() | |
#win = curses.newwin(HEIGHT, WIDTH, 0, 0) | |
#win.keypad(1) | |
#curses.noecho() | |
#curses.curs_set(0) | |
#win.border(0) | |
#win.nodelay(1) | |
#win.addch(food//WIDTH, food%WIDTH, '@') | |
''' | |
while key != 27: | |
win.border(0) | |
win.addstr(0, 2, 'S:' + str(score) + ' ') | |
win.timeout(10) | |
# 接收鍵盤輸入,同時也使顯示流暢 | |
event = win.getch() | |
key = key if event == -1 else event | |
# 重置矩陣 | |
board_reset(snake, snake_size, board) | |
# 如果蛇可以吃到食物,board_refresh返回true | |
# 並且board中除了蛇身(=SNAKE),其它的元素值表示從該點運動到食物的最短路徑長 | |
if board_refresh(food, snake, board): | |
best_move = find_safe_way() # find_safe_way 的唯一調用處 | |
else: | |
best_move = follow_tail() | |
if best_move == ERR: | |
best_move = any_possible_move() | |
# 上面一次思考,只得出一個方向,運行一步 | |
if best_move != ERR: make_move(best_move) | |
else: break | |
curses.endwin() | |
print("\nScore - " + str(score)) | |
''' | |
print("Start coding!") |
This file contains 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
# Cango gearUtils-0.9.js Spur Gears | |
from browser import document as doc | |
from browser import html | |
from browser import window | |
import browser.timer | |
import math | |
canvas = html.CANVAS(width = 600, height = 400) | |
canvas.id = "cango_gear" | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
canvas = doc["cango_gear"] | |
# 此程式採用 Cango Javascript 程式庫繪圖, 因此無需 ctx | |
#ctx = canvas.getContext("2d") | |
cango = window.Cango.new | |
path = window.Path.new | |
creategeartooth = window.createGearTooth.new | |
circle = window.circle.new | |
svgsegs = window.SVGsegs.new | |
# 經由 Cango 轉換成 Brython 的 cango, 指定將圖畫在 id="cango_gear" 的 canvas 上 | |
cgo = cango("cango_gear") | |
###################################### | |
# 畫正齒輪輪廓 | |
##################################### | |
def cangoGear(n, m, pa, x=0, y=0, color="#606060"): | |
# n 為齒數 | |
#n = 17 | |
# pa 為壓力角 | |
#pa = 25 | |
# m 為模數, 根據畫布的寬度, 計算適合的模數大小 | |
# Module = mm of pitch diameter per tooth | |
#m = 0.8*canvas.width/n | |
# pr 為節圓半徑 | |
pr = n*m/2 # gear Pitch radius | |
# generate gear data | |
data = creategeartooth(m, n, pa) | |
toothSVG = svgsegs(data) | |
toothSVG.rotate(180/n) # rotate gear 1/2 tooth to mesh | |
# 單齒的齒形資料經過旋轉後, 將資料複製到 gear 物件中 | |
one = toothSVG.dup() | |
# 利用單齒輪廓旋轉, 產生整個正齒輪外形 | |
for i in range(1, n): | |
newSVG = one.rotate(360*i/n) | |
toothSVG = toothSVG.appendPath(newSVG) | |
# 建立軸孔 | |
# add axle hole, hr 為 hole radius | |
hr = 0.6*pr # diameter of gear shaft | |
shaft = circle(hr) | |
shaftSVG = svgsegs(shaft) | |
spurSVG = toothSVG.appendPath(shaftSVG) | |
gear = path(spurSVG, {"x": x, "y": y, "strokeColor": color}) | |
return gear | |
# 設定兩齒齒數 | |
n1 = 84 | |
n2 = 18 | |
n3 = 99 | |
# 使用 80% 的畫布寬度 | |
m = 0.8*canvas.width/((n1+n2+n3)) | |
# 設定共同的壓力角 | |
pa = 25 | |
# n 齒輪的節圓半徑 | |
pr1 = n1*m/2 | |
# n2 齒輪的節圓半徑 | |
pr2 = n2*m/2 | |
pr3 = n3*m/2 | |
cx = canvas.width/2 | |
cy = canvas.height/2 | |
# Determine the coord of the middle gears | |
mcx = cx + (pr1-pr3) | |
mcy = cy | |
# 建立 gears | |
gear1 = cangoGear(n1, m, pa, color="red") | |
gear2 = cangoGear(n2, m, pa, color="green") | |
gear3 = cangoGear(n3, m, pa, color="blue") | |
deg = math.pi/180 | |
rotate_speed = 0 | |
def draw(): | |
global rotate_speed | |
rotate_speed += 5*deg | |
cgo.clearCanvas() | |
theta1 = 0+rotate_speed | |
gear1.rotate(theta1) | |
gear1.translate(mcx-(pr1+pr2), mcy) | |
cgo.render(gear1) | |
theta2 = 180+(360/n2/2)-(rotate_speed)*n1/n2 | |
gear2.rotate(theta2) | |
gear2.translate(mcx, mcy) | |
cgo.render(gear2) | |
theta3 = 180+(360/n3/2)+(180+(360/n2/2))*n2/n3+(rotate_speed*n1/n2)*(n2/n3) | |
gear3.rotate(theta3) | |
gear3.translate(mcx+(pr2+pr3), mcy) | |
cgo.render(gear3) | |
browser.timer.set_interval(draw, 2) |
This file contains 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
# Draw single Spur Gear | |
from browser import document as doc | |
from browser import html | |
import math | |
# 利用 Brython 建立 canvas 標註元件 | |
canvas = html.CANVAS(width = 600, height = 400) | |
# 將此 canvas 的 id 設為 "spur" | |
canvas.id = "gear" | |
# 將 brython_div 變數設為 id 為 "brython_div 的 doc 物件 | |
brython_div = doc["brython_div"] | |
# 將此 canvas 物件插入網頁 | |
brython_div <= canvas | |
# 利用 canvas 代表 id="spur" 標註元件 | |
# 表示要將 ctx 2d 繪圖至 canvas | |
canvas = doc["gear"] | |
ctx = canvas.getContext("2d") | |
# 插入輸入表單 | |
form = html.FORM() | |
gearNumInput = html.INPUT(type="text", id="gearnum", value="23") | |
button = html.BUTTON("設定齒數", id="set_num") | |
form <= "齒數: " + gearNumInput + html.BR() | |
brython_div <= form + button + html.BR() | |
#print(html.BUTTON("設定齒數", id="set_num").outerHTML) | |
# 以下建立正齒輪繪圖物件與設定齒數函式 | |
# deg 為角度轉為徑度的轉換因子 | |
deg = math.pi/180. | |
# 定義 Spur 類別 | |
class Spur: | |
def __init__(self, ctx): | |
self.ctx = ctx | |
def create_line(self, x1, y1, x2, y2, width=1, fill="red"): | |
self.ctx.beginPath() | |
self.ctx.lineWidth = width | |
self.ctx.moveTo(x1, y1) | |
self.ctx.lineTo(x2, y2) | |
self.ctx.strokeStyle = fill | |
self.ctx.stroke() | |
# 定義一個繪正齒輪的繪圖函式 | |
# midx 為齒輪圓心 x 座標 | |
# midy 為齒輪圓心 y 座標 | |
# rp 為節圓半徑, n 為齒數 | |
# pa 為壓力角 (deg) | |
# rot 為旋轉角 (deg) | |
# 針對 n 大於等於 52 齒時 base circle 與齒根圓大小必須進行判斷 | |
def Gear(self, midx, midy, rp, n=20, pa=20, color="black"): | |
# 齒輪漸開線分成 15 線段繪製 | |
imax = 15 | |
# 在輸入的畫布上繪製直線, 由圓心到節圓 y 軸頂點畫一直線 | |
self.create_line(midx, midy, midx, midy-rp) | |
# a 為模數 (代表公制中齒的大小), 模數為節圓直徑(稱為節徑)除以齒數 | |
# 模數也就是齒冠大小 | |
a=2*rp/n | |
# d 為齒根大小, 為模數的 1.157 或 1.25倍, 這裡採 1.25 倍 | |
d=2.5*rp/n | |
# ra 為齒輪的外圍半徑 | |
ra=rp+a | |
# rb 則為齒輪的基圓半徑 | |
# 基圓為漸開線長齒之基準圓 | |
rb=rp*math.cos(pa*deg) | |
# rd 為齒根圓半徑 | |
rd=rp-d | |
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd | |
# dr 則為基圓到齒頂圓半徑分成 imax 段後的每段半徑增量大小 | |
# 將圓弧分成 imax 段來繪製漸開線 | |
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd | |
if rd>rb: | |
dr = (ra-rd)/imax | |
else: | |
dr=(ra-rb)/imax | |
# tan(pa*deg)-pa*deg 為漸開線函數 | |
sigma=math.pi/(2*n)+math.tan(pa*deg)-pa*deg | |
for j in range(n): | |
ang=-2.*j*math.pi/n+sigma | |
ang2=2.*j*math.pi/n+sigma | |
lxd=midx+rd*math.sin(ang2-2.*math.pi/n) | |
lyd=midy-rd*math.cos(ang2-2.*math.pi/n) | |
for i in range(imax+1): | |
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd | |
if rd>rb: | |
r=rd+i*dr | |
else: | |
r=rb+i*dr | |
theta=math.sqrt((r*r)/(rb*rb)-1.) | |
alpha=theta-math.atan(theta) | |
xpt=r*math.sin(alpha-ang) | |
ypt=r*math.cos(alpha-ang) | |
xd=rd*math.sin(-ang) | |
yd=rd*math.cos(-ang) | |
# i=0 時, 繪線起點由齒根圓上的點, 作為起點 | |
if(i==0): | |
last_x = midx+xd | |
last_y = midy-yd | |
# 由左側齒根圓作為起點, 除第一點 (xd,yd) 齒根圓上的起點外, 其餘的 (xpt,ypt)則為漸開線上的分段點 | |
self.create_line((midx+xpt),(midy-ypt),(last_x),(last_y),fill=color) | |
# 最後一點, 則為齒頂圓 | |
if(i==imax): | |
lfx=midx+xpt | |
lfy=midy-ypt | |
last_x = midx+xpt | |
last_y = midy-ypt | |
# the line from last end of dedendum point to the recent | |
# end of dedendum point | |
# lxd 為齒根圓上的左側 x 座標, lyd 則為 y 座標 | |
# 下列為齒根圓上用來近似圓弧的直線 | |
self.create_line((lxd),(lyd),(midx+xd),(midy-yd),fill=color) | |
for i in range(imax+1): | |
# 當 rd 大於 rb 時, 漸開線並非畫至 rb, 而是 rd | |
if rd>rb: | |
r=rd+i*dr | |
else: | |
r=rb+i*dr | |
theta=math.sqrt((r*r)/(rb*rb)-1.) | |
alpha=theta-math.atan(theta) | |
xpt=r*math.sin(ang2-alpha) | |
ypt=r*math.cos(ang2-alpha) | |
xd=rd*math.sin(ang2) | |
yd=rd*math.cos(ang2) | |
# i=0 時, 繪線起點由齒根圓上的點, 作為起點 | |
if(i==0): | |
last_x = midx+xd | |
last_y = midy-yd | |
# 由右側齒根圓作為起點, 除第一點 (xd,yd) 齒根圓上的起點外, 其餘的 (xpt,ypt)則為漸開線上的分段點 | |
self.create_line((midx+xpt),(midy-ypt),(last_x),(last_y),fill=color) | |
# 最後一點, 則為齒頂圓 | |
if(i==imax): | |
rfx=midx+xpt | |
rfy=midy-ypt | |
last_x = midx+xpt | |
last_y = midy-ypt | |
# lfx 為齒頂圓上的左側 x 座標, lfy 則為 y 座標 | |
# 下列為齒頂圓上用來近似圓弧的直線 | |
self.create_line(lfx,lfy,rfx,rfy,fill=color) | |
# 以 button 驅動的事件函式 | |
def setgearnumber(e): | |
global ctx | |
ctx.clearRect(0, 0, canvas.width, canvas.height) | |
x = (canvas.width)/2 | |
y = (canvas.height)/2 | |
if doc["gearnum"].value.isdigit(): | |
n1 = int(doc["gearnum"].value) | |
else: | |
n1= 26 | |
# 設定齒輪參數 | |
x = (canvas.width)/2 | |
y = (canvas.height)/2 | |
r = 0.8*(canvas.height/2) | |
pa = 20 | |
# 繪出正齒輪 | |
spur = Spur(ctx) | |
spur.Gear(x, y, r, n1, pa, "blue") | |
#判定 button | |
setgearnumber(True) | |
doc['set_num'].bind('click',setgearnumber) |
This file contains 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
# try to generate binary or ascii stl part file and shown on browser - not complete yet | |
import struct | |
class StLFacet: | |
def __init__(self, normal, v1, v2, v3, att_bc=0): | |
self.coords = [normal, v1, v2, v3] | |
self.att_bc = att_bc | |
class StL: | |
def __init__(self, header): | |
self.header = header | |
self.facets = [] | |
def add_facet(self, facet): | |
self.facets.append(facet) | |
def get_binary(self): | |
# 原先 2.0 的版本 | |
#out = ['%-80.80s' % self.header] | |
# 改為 Python 3.0 格式 | |
# 第一行標頭的格式 | |
header = ['%-80.80s' % self.header][0] | |
# 利用 bytes() 將標頭字串轉為二位元資料 | |
out = [bytes(header,encoding="utf-8")] | |
# 接著則計算三角形面的數量, 並以二位元長整數格式存檔 | |
out.append(struct.pack('L',len(self.facets))) | |
# 接著則依照法線向量與三個點座標的格式, 分別以浮點數格式進行資料附加 | |
for f in self.facets: | |
for coord in f.coords: | |
out.append(struct.pack('3f', *coord)) | |
# att_bc 則內定為 0 | |
out.append(struct.pack('H', f.att_bc)) | |
return b"".join(out) | |
def test(): | |
stl=StL('Header ...') | |
stl.add_facet(StLFacet((0.,0.,1.),(0.,0.,0.),(1.,0.,0.),(0.,1.,0.))) | |
stl.add_facet(StLFacet((0.,0.,1.),(1.,0.,0.),(1.,1.,0.),(0.,1.,0.))) | |
# 第二個平面 | |
stl.add_facet(StLFacet((0.,-1.,0.),(0.,0.,0.),(0.,0.,-1.),(1.,0.,-1.))) | |
stl.add_facet(StLFacet((0.,-1.,0.),(0.,0.,0.),(1.,0.,-1.),(1.,0.,0.))) | |
return stl.get_binary() | |
# 指定存為 binary 格式檔案 | |
#stlfile = open("test.stl", "wb") | |
stlcontent = test() | |
#stlfile.write(stlcontent) | |
# 以下將 binary stlcontent 轉為 ASCII stl | |
normals = [] | |
points = [] | |
triangles = [] | |
triangle_number = 0 | |
def load_binary_stl(fp): | |
''' | |
二位元 STL 檔案格式如下: | |
檔案標頭共有 80 個字元(bytes), 內容通常省略, 但是內容不可使用 solid, 以免與文字檔案 STL 混淆 | |
UINT8[80] – Header | |
UINT32 – Number of triangles (I:佔 4 bytes 的 unsigned integer) | |
foreach triangle | |
REAL32[3] – Normal vector (f:每一座標分量為一佔 4 bytes 的 float, 共佔 12 bytes) | |
REAL32[3] – Vertex 1 | |
REAL32[3] – Vertex 2 | |
REAL32[3] – Vertex 3 | |
UINT16 – Attribute byte count (H:兩個 bytes 的 unsigned short, 表示 attribute byte count) | |
end | |
''' | |
# 已經在外部開檔 | |
#fp=open(filename,'rb') | |
header=fp.read(80) | |
triangle_number = struct.unpack('I',fp.read(4))[0] | |
#print(triangle_number) | |
count=0 | |
while True: | |
try: | |
p=fp.read(12) | |
if len(p)==12: | |
n=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
normals.append(n) | |
l = len(points) | |
#print(n) | |
p=fp.read(12) | |
if len(p)==12: | |
p1=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
points.append(p1) | |
#print(p1) | |
p=fp.read(12) | |
if len(p)==12: | |
p2=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
points.append(p2) | |
p=fp.read(12) | |
if len(p)==12: | |
p3=[struct.unpack('f',p[0:4])[0],struct.unpack('f',p[4:8])[0],struct.unpack('f',p[8:12])[0]] | |
points.append(p3) | |
triangles.append((l, l+1, l+2)) | |
# 使用 count 來計算三角形平面個數 | |
# triangle_number 為 STL 檔案中的三角形個數 | |
count += 1 | |
#print(count) | |
# 在前面 12*4 個 bytes 的 normal 與三個點資料後, 為 | |
# 一個 2 bytes 長的 unsigned short, 其值為零, 為 attribute | |
fp.read(2) | |
# 讀完所有三角平面後, 即跳出 while | |
if count > triangle_number: | |
break | |
except EOFError: | |
break | |
#fp.close() | |
def read_length(f): | |
length = struct.unpack("@i", f.read(4)) | |
return length[0] | |
def read_header(f): | |
f.seek(f.tell()+80) | |
def write_as_ascii(outfilename): | |
f = open(outfilename, "w") | |
f.write ("solid "+outfilename+"\n") | |
for n in range(len(triangles)): | |
f.write ("facet normal {} {} {}\n".format(normals[n][0],normals[n][1],normals[n][2])) | |
f.write ("outer loop\n") | |
f.write ("vertex {} {} {}\n".format(points[triangles[n][0]][0],points[triangles[n][0]][1],points[triangles[n][0]][2])) | |
f.write ("vertex {} {} {}\n".format(points[triangles[n][1]][0],points[triangles[n][1]][1],points[triangles[n][1]][2])) | |
f.write ("vertex {} {} {}\n".format(points[triangles[n][2]][0],points[triangles[n][2]][1],points[triangles[n][2]][2])) | |
f.write ("endloop\n") | |
f.write ("endfacet\n") | |
f.write ("endsolid "+outfilename+"\n") | |
f.close() | |
infilename = "ss1.stl" | |
outfilename = "ss2.stl" | |
try: | |
f = open(infilename, "rb") | |
#read_header(f) | |
#l = read_length(f) | |
try: | |
load_binary_stl(f) | |
l = len(normals) | |
except Exception as e: | |
print("Exception",e) | |
print(len(normals), len(points), len(triangles), l) | |
write_as_ascii(outfilename) | |
print("done") | |
except Exception as e: | |
print(e) |
This file contains 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
# http://mde.tw/2016fallcadp/blog/2016fall-dian-nao-fu-zhu-she-ji-shi-xi-ke-cheng-zong-jie-yi.html | |
#coding:utf-8 | |
# source: http://code.activestate.com/recipes/578246-stl-writer/ | |
import struct | |
ASCII_FACET = """facet normal 0 0 0 | |
outer loop | |
vertex {face[0][0]:.4f} {face[0][1]:.4f} {face[0][2]:.4f} | |
vertex {face[1][0]:.4f} {face[1][1]:.4f} {face[1][2]:.4f} | |
vertex {face[2][0]:.4f} {face[2][1]:.4f} {face[2][2]:.4f} | |
endloop | |
endfacet | |
""" | |
BINARY_HEADER ="80sI" | |
BINARY_FACET = "12fH" | |
class ASCII_STL_Writer: | |
""" Export 3D objects build of 3 or 4 vertices as ASCII STL file. | |
""" | |
def __init__(self, stream): | |
self.fp = stream | |
self._write_header() | |
def _write_header(self): | |
self.fp.write("solid python\n") | |
def close(self): | |
self.fp.write("endsolid python\n") | |
def _write(self, face): | |
self.fp.write(ASCII_FACET.format(face=face)) | |
def _split(self, face): | |
p1, p2, p3, p4 = face | |
return (p1, p2, p3), (p3, p4, p1) | |
def add_face(self, face): | |
""" Add one face with 3 or 4 vertices. """ | |
if len(face) == 4: | |
face1, face2 = self._split(face) | |
self._write(face1) | |
self._write(face2) | |
elif len(face) == 3: | |
self._write(face) | |
else: | |
raise ValueError('only 3 or 4 vertices for each face') | |
def add_faces(self, faces): | |
""" Add many faces. """ | |
for face in faces: | |
self.add_face(face) | |
class Binary_STL_Writer(ASCII_STL_Writer): | |
""" Export 3D objects build of 3 or 4 vertices as binary STL file. | |
""" | |
def __init__(self, stream): | |
self.counter = 0 | |
super(Binary_STL_Writer, self).__init__(stream) | |
def close(self): | |
self._write_header() | |
def _write_header(self): | |
self.fp.seek(0) | |
self.fp.write(struct.pack(BINARY_HEADER, b'Python Binary STL Writer', self.counter)) | |
def _write(self, face): | |
self.counter += 1 | |
data = [ | |
0., 0., 0., | |
face[0][0], face[0][1], face[0][2], | |
face[1][0], face[1][1], face[1][2], | |
face[2][0], face[2][1], face[2][2], | |
0 | |
] | |
self.fp.write(struct.pack(BINARY_FACET, *data)) | |
def example(): | |
def get_cube(): | |
# cube corner points | |
s = 3. | |
p1 = (0, 0, 0) | |
p2 = (0, 0, s) | |
p3 = (0, s, 0) | |
p4 = (0, s, s) | |
p5 = (s, 0, 0) | |
p6 = (s, 0, s) | |
p7 = (s, s, 0) | |
p8 = (s, s, s) | |
# define the 6 cube faces | |
# faces just lists of 3 or 4 vertices | |
return [ | |
[p1, p5, p7, p3], | |
[p1, p5, p6, p2], | |
[p5, p7, p8, p6], | |
[p7, p8, p4, p3], | |
[p1, p3, p4, p2], | |
[p2, p6, p8, p4], | |
] | |
''' | |
for writing ASCII STL cube file | |
with open('cube_ascii.stl', 'w') as fp: | |
writer = ASCII_STL_Writer(fp) | |
writer.add_faces(get_cube()) | |
writer.close() | |
''' | |
with open('cube_bin.stl', 'wb') as fp: | |
writer = Binary_STL_Writer(fp) | |
writer.add_faces(get_cube()) | |
writer.close() | |
if __name__ == '__main__': | |
example() |
This file contains 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
# Turtle1 繪圖 | |
from browser import window, html | |
from browser import document as doc | |
import turtle | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
t = turtle.Turtle() | |
t.width(5) | |
for c in ['red', '#00ff00', '#fa0', 'rgb(0,0,200)']: | |
t.color(c) | |
t.forward(100) | |
t.left(90) | |
# dot() and write() do not require the pen to be down | |
t.penup() | |
t.goto(-30, -100) | |
t.dot(40, 'rgba(255, 0, 0, 0.5') | |
t.goto(30, -100) | |
t.dot(40, 'rgba(0, 255, 0, 0.5') | |
t.goto(0, -70) | |
t.dot(40, 'rgba(0, 0, 255, 0.5') | |
t.goto(0, 125) | |
t.color('purple') | |
t.write("這就是 Brython, 網頁上的 Python", font=("Arial", 15, "normal")) | |
turtle.done() |
This file contains 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
# Turtle2 繪圖 | |
from browser import document as doc | |
import turtle | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
star = turtle.Turtle() | |
for i in range(5): | |
star.forward(250) | |
star.right(144) | |
turtle.done() |
This file contains 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
# Turtle3 繪圖 | |
# https://michael0x2a.com/blog/turtle-examples | |
from browser import document as doc | |
import turtle | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
painter = turtle.Turtle() | |
painter.pencolor("blue") | |
for i in range(50): | |
painter.forward(50) | |
painter.left(123) # Let's go counterclockwise this time | |
painter.pencolor("red") | |
for i in range(50): | |
painter.forward(100) | |
painter.left(123) | |
turtle.done() |
This file contains 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
# Turtle4 繪圖 | |
# https://docs.python.org/3.7/library/turtle.html?highlight=turtle | |
# https://fiftyexamples.readthedocs.io/en/latest/turtle.html | |
from browser import document as doc | |
import turtle | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
# 輸入 "turtle" 字串, 表示要使用內建的烏龜圖示 | |
t = turtle.Turtle("turtle") | |
# 設定繪圖尺寸 | |
screen_x = 500-20 | |
screen_y = 300 | |
# 提筆, 將烏龜移動到畫布中心 | |
t.penup() | |
t.home() | |
# 內定方向為右, 前進 screen_x/2 | |
t.forward(screen_x / 2) | |
# 將方向往右轉 90 度 | |
t.right(90) | |
# 此時方向向下, 前進 screen_y/2 | |
t.forward(screen_y / 2) | |
# 令烏龜方向轉絕對角度 180, 等同轉相對角度 90 度, 即 t.right(90) | |
t.setheading(180) | |
# 將畫筆顏色設為紅色 | |
t.pencolor('red') | |
# 下筆準備繪圖 | |
t.pendown() | |
# 設筆寬度為 10 | |
t.pensize(10) | |
# 進入重複迴圈, 此時方向向右, 分別 | |
# 前進 screen_x, 之後轉 90 度, 方向朝上 | |
# 再前進 screen_y, 之後再轉 90 度, 方向朝右 | |
# 再前進 screen_x, 之後轉 90 度, 方向朝下 | |
# 最後再前進 screen_y 後, 將方向轉為向左 | |
for distance in (screen_x, screen_y, screen_x, screen_y): | |
t.forward(distance) | |
t.right(90) | |
# 提筆後, 將烏龜轉回內定方向回到畫布中心 | |
t.penup() | |
t.home() | |
# 完成 turtle 繪圖 | |
turtle.done() |
This file contains 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
# Turtle5 繪圖 | |
# https://brython.info/gallery/turtle.html | |
from browser import document as doc | |
import turtle | |
import math | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
t = turtle.Turtle("turtle") | |
t.speed(1) | |
t.forward(50) | |
print("Should be (50, 0):", t.position()) | |
print("Should be 50: ", t.xcor()) | |
print("Should be 0: ", t.ycor()) | |
t.left(90) | |
t.color("blue") | |
t.speed(2) | |
t.fd(50) | |
print("Should be (50, 50):", t.pos()) | |
print("Should be 225: ", t.towards(0, 0)) | |
print("Should be 90: ", t.heading()) | |
print("Should be approximately 71:", t.distance(0, 0)) | |
# Draw the same square in three different angle modes | |
t.width(4) | |
print("Drawing using degrees - the default") | |
print("Heading should be 90: ", t.heading()) | |
for i in range(4): | |
t.forward(100) | |
t.left(90) | |
print("Drawing using radians") | |
t.radians() | |
print("Heading should be pi/2: ", t.heading()) | |
for i in range(4): | |
t.forward(100) | |
t.left(math.pi/2) | |
print("Drawing using gradients") | |
t.degrees(400) | |
print("Heading should be 100: ", t.heading()) | |
for i in range(4): | |
t.forward(100) | |
t.left(100) | |
t.degrees() | |
t.width(1) | |
t.lt(90) | |
t.color("orange") | |
t.backward(50) | |
t.right(90) | |
t.color("green") | |
t.back(50) | |
t.rt(90) | |
t.color("red") | |
t.bk(50) | |
t.stamp() | |
t.speed(4) | |
t.color("black", "white") | |
t.goto(-100, 100) | |
t.stamp() | |
t.color("blue", "yellow") | |
t.setposition(0, 100) | |
t.stamp() | |
t.color("green", "white") | |
t.setpos(100, 100) | |
t.stamp() | |
t.speed(10) | |
t.color("orange") | |
t.sety(-100) | |
t.setx(-100) | |
t.stamp() | |
t.color("cyan") | |
t.home() | |
t.stamp() | |
t.color("green") | |
t.width(4) | |
t.setheading(180) | |
t.forward(150) | |
t.seth(90) | |
t.fd(20) | |
t.dot(30, "rgba(255, 0, 0, 0.2)") | |
t.color("red") | |
t.speed(0) | |
t.forward(30) | |
t.left(90) | |
t.circle(30) | |
turtle.done() |
This file contains 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
# Turtle6 繪圖 | |
from browser import document as doc | |
import turtle | |
import math | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
t = turtle.Turtle("turtle") | |
t.speed(10) | |
colors = ['red', 'purple', 'blue', 'green', 'orange'] | |
for x in range(150): | |
t.pencolor(colors[x % 5]) | |
t.width(x/10 + 1) | |
t.forward(x) | |
t.left(59) | |
turtle.done() |
This file contains 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
# Turtle7 繪圖 | |
from browser import document as doc | |
import turtle | |
import math | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
screen=turtle.Screen() #making a canvas for drawing | |
screen.bgcolor('black') #making canvas black | |
trtl=turtle.Turtle() #making a turtle | |
trtl.pencolor('red') #making colour of the pen red | |
trtl.pensize(5) #choosing the size of pen nib | |
trtl.speed(10) #choosing the speed of drawing | |
# shape should be ‘arrow’, ‘classic’, ‘turtle’ or ‘circle’ | |
trtl.shape('turtle') #choosing the shape of pen nib | |
trtl.forward(150) #drawing a line of 150 pixels | |
trtl.right(90) #asking turtle to turn 90 degrees | |
trtl.forward(150) #drawing a line of 150 pixels | |
trtl.penup() # preparing for moving pen without drawing | |
trtl.setpos(-140,-120) # making the new position of the turtle | |
trtl.pendown() # bringing the pen down for drawing again | |
trtl.pencolor('green') # choosin the pen colour as green | |
trtl.write('Brython 烏龜繪圖', font=("Arial", 20, "bold")) # chosing the font | |
trtl.penup() | |
trtl.ht() # hiding the turtle from the screen | |
turtle.done() |
This file contains 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
# Turtle8 繪圖 | |
from browser import document as doc | |
import turtle | |
import math | |
turtle.set_defaults( | |
turtle_canvas_wrapper = doc['brython_div'] | |
) | |
wn = turtle.Screen() | |
wn.bgcolor("black") | |
skk = turtle.Turtle() | |
skk.speed(10) | |
skk.shape("circle") | |
skk.color("blue") | |
def sqrfunc(size): | |
for i in range(4): | |
skk.fd(size) | |
skk.left(90) | |
size = size-5 | |
sqrfunc(146) | |
sqrfunc(126) | |
sqrfunc(106) | |
sqrfunc(86) | |
sqrfunc(66) | |
sqrfunc(46) | |
sqrfunc(26) | |
turtle.done() |
This file contains 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
# for acos, atan2 and sin | |
import math | |
import sys | |
# radian to degree | |
deg = 180/math.pi | |
# link 1 length | |
a1 = 10 | |
# link 2 length | |
a2 = 10 | |
# derivated based up https://www.youtube.com/watch?v=IKOGwoJ2HLk&t=311s | |
def ik(x, y): | |
# (x, y) need to be located inside the circle with radius a1+a2 | |
if (x**2 + y**2) <= (a1+ a2)**2: | |
q2 = -math.acos((x**2+y**2-a1**2-a2**2)/(2*a1*a2)) | |
q1 = math.atan2(y, x) + math.atan2((a2*math.sin(q2)), (a1+a2*math.cos(q2))) | |
# The decimal point of number is rounded to the 4th place | |
return [round(q1*deg, 4), round(q2*deg, 4)] | |
else: | |
print("Over range!") | |
# end the script execution | |
sys.exit() | |
theta = ik(15, 1) | |
print(theta[0], theta[1]) |
This file contains 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
# 畫美國國旗 | |
# 根據 https://en.wikipedia.org/wiki/Flag_of_the_United_States#Specifications 規格繪圖 | |
# 導入 doc | |
from browser import document as doc | |
# 以下將利用 html 產生所需的繪圖畫布 | |
from browser import html | |
# 利用 math 函式庫執行三角函數運算 | |
import math | |
# height = 1, width = 1.9 | |
width = 600 | |
height = int(600/1.9) | |
canvas = html.CANVAS(width = width, height = height) | |
#canvas.style = {"width": "100%"} | |
canvas.id = "taiwan_flag" | |
# 將圖畫至 id 為 brython_div2 的 cnavas 標註 | |
brython_div = doc["brython_div2"] | |
brython_div <= canvas | |
# 準備繪圖畫布 | |
canvas = doc["taiwan_flag"] | |
ctx = canvas.getContext("2d") | |
# 進行座標轉換, x 軸不變, y 軸反向且移動 canvas.height 單位光點 | |
# ctx.setTransform(1, 0, 0, -1, 0, canvas.height) | |
# 以下採用 canvas 原始座標繪圖 | |
flag_w = canvas.width | |
flag_h = canvas.height | |
# 先畫滿地紅 | |
ctx.fillStyle='#B31942' | |
ctx.fillRect(0,0,flag_w,flag_h) | |
# 6 條白色長方形 | |
# 每條高度 height/13 | |
ctx.fillStyle ='#FFFFFF' | |
white_height = int(height/13) | |
whitex = 0 | |
whitey = white_height | |
white_width = width | |
for i in range(6): | |
ctx.fillRect(whitex, whitey+i*2*white_height, white_width, white_height) | |
# 藍色區域 | |
blue_height = int(height*7/13) | |
blue_width = int(width*2/5) | |
bluex = 0 | |
bluey = 0 | |
ctx.fillStyle ='#0A3161' | |
ctx.fillRect(bluex, bluey, blue_width, blue_height) | |
# 建立畫直線函式 | |
def draw_line(x1, y1, x2, y2, color="#ff0000"): | |
ctx.beginPath() | |
ctx.moveTo(x1, y1) | |
ctx.lineTo(x2, y2) | |
ctx.strokeStyle = color | |
ctx.stroke() | |
# 測試畫直線函式功能 | |
#draw_line(10, 10, 100, 100) | |
# 定義角度轉換為徑度變數 | |
deg = math.pi/180. | |
# 建立五星繪圖函式 | |
#x, y 為中心, r 為半徑, angle 旋轉角, solid 空心或實心, color 顏色 | |
def star(x, y, r, angle=0, solid=False, color="#ff0000"): | |
#以 x, y 為圓心, 計算五個外點 | |
# 圓心到水平線距離 | |
a = r*math.cos(72*deg) | |
# a 頂點向右到內點距離 | |
b = (r*math.cos(72*deg)/math.cos(36*deg))*math.sin(36*deg) | |
# 利用畢氏定理求內點半徑 | |
rin = math.sqrt(a*a + b*b) | |
# 查驗 a, b 與 rin | |
#print(a, b, rin) | |
if solid: | |
ctx.beginPath() | |
# angle 角度先轉 360/10, 讓五星對正 | |
angle = angle + 360/10 | |
for i in range(5): | |
xout = (x + r*math.sin((360/5)*deg*i+angle*deg)) | |
yout = (y + r*math.cos((360/5)*deg*i+angle*deg)) | |
# 外點增量 + 1 | |
xout2 = x + r*math.sin((360/5)*deg*(i+1)+angle*deg) | |
yout2 = y + r*math.cos((360/5)*deg*(i+1)+angle*deg) | |
xin = x + rin*math.sin((360/5)*deg*i+36*deg+angle*deg) | |
yin = y + rin*math.cos((360/5)*deg*i+36*deg+angle*deg) | |
# 查驗外點與內點座標 | |
#print(xout, yout, xin, yin) | |
if solid: | |
# 填色 | |
if i==0: | |
ctx.moveTo(xout, yout) | |
ctx.lineTo(xin, yin) | |
ctx.lineTo(xout2, yout2) | |
else: | |
ctx.lineTo(xin, yin) | |
ctx.lineTo(xout2, yout2) | |
else: | |
# 空心 | |
draw_line(xout, yout, xin, yin, color) | |
# 畫空心五芒星, 無關畫線次序, 若實心則與畫線次序有關 | |
draw_line(xout2, yout2, xin, yin, color) | |
if solid: | |
ctx.fillStyle = color | |
ctx.fill() | |
# 白色五星 | |
white = "#FFFFFF" | |
# 單數排白色五星 | |
star1x = int(blue_width/12) | |
star1y = int(blue_height/10) | |
star_radius = int(white_height*4/5/2) | |
# 沿 x 方向有 6 顆白色五星 | |
# 沿 y 方向有 5 顆白色五星 | |
inc1x = int(2*blue_width/12) | |
inc1y = int(2*blue_height/10) | |
for i in range(6): | |
for j in range(5): | |
star(star1x+i*inc1x, star1y+j*inc1y, star_radius, solid=True, color=white) | |
# 雙數排白色五星 | |
star2x = int(blue_width/12 + blue_width/12) | |
star2y = int(blue_height/10 + blue_height/10) | |
# 沿 x 方向有 5 顆白色五星 | |
# 沿 y 方向有 4 顆白色五星 | |
for i in range(5): | |
for j in range(4): | |
star(star2x+i*inc1x, star2y+j*inc1y, star_radius, solid=True, color=white) |
This file contains 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
# 參考: https://usefulangle.com/post/352/javascript-capture-image-from-camera | |
# 參考: https://usefulangle.com/post/353/javascript-canvas-image-upload | |
from browser import html, document, window | |
# 建立 video tag, 並且插入既有的 brython_div2 標註中 | |
# autoplay=True for phone camera, 480X640 for iphone | |
videotag = html.VIDEO(autoplay=True,id='video', width='480', height='640') | |
# 建立 canvas tag | |
canvas = html.CANVAS(width = 480, height = 640, id="canvas") | |
# 建立 button tag | |
button = html.BUTTON("Save Image", id="save") | |
# 分別將上列 tags 插入 id="brython_div2" 之位置 | |
brython_div = document['brython_div2'] | |
brython_div <= videotag + html.BR() | |
brython_div <= button + html.BR() | |
brython_div <= canvas | |
ctx = canvas.getContext("2d") | |
# 定義 OnSuccess 函式在 video 標註中播放 Webcam 串流影像 | |
def OnSuccess(stream): | |
video = document['video'] | |
video.srcObject = stream | |
video.play() | |
# 透過瀏覽器取得使用者同意後傳送 Webcam 串流資料 | |
# 'facingMode': 'environment' for rear camera | |
window.navigator.mediaDevices.getUserMedia( | |
{"video": {"facingMode": 'environment'}, "audio": False} | |
).then(OnSuccess) | |
# 建立儲存串流影像至圖檔函式 | |
def save_image(e): | |
#print(canvas.width) | |
video = document['video'] | |
ctx.drawImage(video, 0, 0, canvas.width, canvas.height) | |
image_data_url = canvas.toDataURL('image/jpeg') | |
print(image_data_url) | |
# click id="save" 按鈕後, 執行 save_image | |
# 此 image_data_url 可以用於將影像檔案送到 server | |
document["save"].bind("click", save_image) |
This file contains 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
# Brython websocket example | |
""" | |
# Python WebSocket server | |
# pip install asyncio websockets | |
import asyncio | |
import websockets | |
async def hello(websocket, path): | |
name = await websocket.recv() | |
print(f"< {name}") | |
greeting = f"Hello {name}!" | |
await websocket.send(greeting) | |
print(f"> {greeting}") | |
start_server = websockets.serve(hello, "localhost", 8765) | |
asyncio.get_event_loop().run_until_complete(start_server) | |
asyncio.get_event_loop().run_forever() | |
""" | |
from browser import document | |
from browser import html, window, websocket | |
# 將 brython_div 變數設為 id 為 "brython_div 的 doc 物件 | |
brython_div = document["brython_div"] | |
# 插入輸入表單 | |
form = html.FORM() | |
gearNumInput = html.INPUT(type="text", id="data", value="特斯拉") | |
button = html.BUTTON("Run Websocket", id="run") | |
button3 = html.BUTTON("Close Connection", id="close") | |
form <= "Name: " + gearNumInput + html.BR() | |
brython_div <= form + button + html.BR() | |
brython_div <= button3 + html.BR() | |
def on_open(evt): | |
# Web Socket is connected, send data using send() | |
data = document["data"].value | |
if data: | |
ws.send(data) | |
alert("Message is sent") | |
def on_message(evt): | |
# message received from server | |
alert("Message received : %s" %evt.data) | |
def on_close(evt): | |
# websocket is closed | |
alert("Connection is closed") | |
ws = None | |
def _test(_): | |
global ws | |
# open a web socket | |
# ws 為一般連線, wss 為 secure 連線 | |
ws = websocket.WebSocket("ws://localhost:8765") | |
# attach functions to web sockets events | |
ws.bind("open", on_open) | |
ws.bind("message", on_message) | |
ws.bind("close", on_close) | |
def close_connection(_): | |
ws.close() | |
def hello(ev): | |
alert("Hello !") | |
# 假如不導入 websocket, 也可以執行 Javascript 的 WebSocket | |
#websocket = window.WebSocket.new | |
document["run"].bind("click", _test) | |
document["close"].bind("click", close_connection) |
This file contains 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 bind, document, websocket | |
from browser.widgets.dialog import InfoDialog | |
from browser import html | |
# 將 brython_div 變數設為 id 為 "brython_div 的 doc 物件 | |
brython_div = document["brython_div"] | |
# 插入輸入表單 | |
form = html.FORM() | |
data_input = html.INPUT(type="text", id="data") | |
open_button = html.BUTTON("Open connection", id="openbtn") | |
send_button = html.BUTTON("Send", id="sendbtn") | |
close_button = html.BUTTON("Close connection", id="closebtn") | |
form <= data_input + html.BR() | |
brython_div <= open_button + html.BR() + form + send_button+ html.BR() + close_button | |
def on_open(evt): | |
document['sendbtn'].disabled = False | |
document['closebtn'].disabled = False | |
document['openbtn'].disabled = True | |
InfoDialog("websocket", f"Connection open") | |
def on_message(evt): | |
# message received from server | |
InfoDialog("websocket", f"Message received : {evt.data}") | |
def on_close(evt): | |
# websocket is closed | |
InfoDialog("websocket", "Connection is closed") | |
document['openbtn'].disabled = False | |
document['closebtn'].disabled = True | |
document['sendbtn'].disabled = True | |
ws = None | |
@bind('#openbtn', 'click') | |
def _open(ev): | |
if not websocket.supported: | |
InfoDialog("websocket", "WebSocket is not supported by your browser") | |
return | |
global ws | |
# open a web socket | |
ws = websocket.WebSocket("ws://127.0.0.1:8080/") | |
# bind functions to web socket events | |
ws.bind('open',on_open) | |
ws.bind('message',on_message) | |
ws.bind('close',on_close) | |
@bind('#sendbtn', 'click') | |
def send(ev): | |
data = document["data"].value | |
if data: | |
ws.send(data) | |
@bind('#closebtn', 'click') | |
def close_connection(ev): | |
ws.close() | |
document['openbtn'].disabled = False | |
''' websocket echo server in python | |
#!/usr/bin/env python | |
import asyncio | |
# pip install websockets | |
import websockets | |
import os | |
async def echo(websocket, path): | |
async for message in websocket: | |
print ("Received and echoing message: "+message, flush=True) | |
await websocket.send(message) | |
start_server = websockets.serve(echo, "0.0.0.0", os.environ.get('PORT') or 8080) | |
print("WebSockets echo server starting", flush=True) | |
asyncio.get_event_loop().run_until_complete(start_server) | |
print("WebSockets echo server running", flush=True) | |
asyncio.get_event_loop().run_forever() | |
''' |
This file contains 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
# Ycombinator quicksort example | |
Y = lambda f: lambda *args: f(Y(f))(*args) | |
quicksort = Y(lambda f: | |
lambda x: ( | |
f([item for item in x if item < x[0]]) | |
+ [y for y in x if x[0] == y] | |
+ f([item for item in x if item > x[0]]) | |
) if x else []) | |
print(quicksort([1, 3, 5, 4, 1, 3, 2])) |
This file contains 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
# 導入 Ex1 原始碼 | |
a = 0 | |
def my_print(n, input_str): | |
print("以下將重覆列印 '", input_str + "' " + str(n) + "次") | |
for i in range(n): | |
print(i, input_str) | |
my_print(5, "Hello World!") | |
the_str = "開始學習 Python" | |
num = input("請輸入要列印的次數!") | |
my_print(int(num), the_str) | |
a = a + 1 | |
print("(" + str(a) + ")", "_" * 25) | |
def myfun(): | |
yield 1 | |
yield 2 | |
yield 3 | |
for i in myfun(): | |
print(i) | |
x = iter(myfun()) | |
y = list(myfun()) | |
a = a + 1 | |
print("(" + str(a) + ")", "_" * 25) | |
print(x.__next__()) | |
print(x.__next__()) | |
print(x.__next__()) | |
print(y) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment