Skip to content

Instantly share code, notes, and snippets.

@mdecycu
Created August 19, 2022 02:25
Show Gist options
  • Save mdecycu/d9082d678096bd58378d6afe2c7fa05d to your computer and use it in GitHub Desktop.
Save mdecycu/d9082d678096bd58378d6afe2c7fa05d to your computer and use it in GitHub Desktop.
cp2022_python
# 程式自動猜數字遊戲
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(平均猜測次數)
# 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)
# 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()
# 大樂透電腦選號
# 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()
# 導入 sys 模組
import sys
# 導入 keyword 模組
import keyword
# 利用 sys 模組中的 version_info 印出 Python 版次
print("Python version: ", sys.version_info)
# 利用 keyword 模組中的 kwlist 印出關鍵字
print("Python keywords: ", keyword.kwlist)
# 從 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)
# 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()
# 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()
# 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)
# 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)
# 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)
# 這個程式用於 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)
# 利用 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)
# 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()
# 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()
# 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)
# 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
# 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()
# 這個程式用於 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")
# 這個程式用於 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")
# import from downloads/py/fibo.py
import fibo
fibo.fib(5)
print(fibo.fib2(5))
# 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)
# 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)
# 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))
# 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)
# 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()))
# 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
# 威力彩電腦選號
# 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()
# 畫中華人民共和國國旗
# 根據 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)
# 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)
# 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)
# 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"))
# 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
# 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)
# 這個程式用於 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)
# 這個程式用於 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)
# 這個程式用於 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)
# 畫中華民國國旗
# 導入 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()
# 這個程式用於 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)
# 從 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)
# 先將無法在 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!")
# 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)
# 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)
# 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)
# 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()
# 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()
# 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()
# 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()
# 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()
# 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()
# 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()
# 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()
# 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()
# 畫美國國旗
# 根據 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)
# 參考: 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)
# 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)
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()
'''
# 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]))
# 導入 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