运行效果:知乎视频www.zhihu.com
代码:
import time, math
import turtle as t
class Human(object):
def __init__(self, x0, y0, x1=None, y1=None, v0=100, v1=100):
self.x0 = x0
self.y0 = y0
self.tx = x0
self.ty = y0
self.x1 = x0 if x1 is None else x1
self.y1 = y0 if y1 is None else y1
self.v0 = v0
self.v1 = v1
self.tv = v0
self.in_metro=False
self.need_get_on=True
self.should_remove=False
self.door_index=0
def move(self,back=False):
if self.need_get_on:
if back:
self.get_on_back()
else:
self.get_on()
else:
self.get_off()
def get_on(self):
if self.ty < -5:
self.ty+=3
elif self.ty < 0:
if self.tx
elif self.tx>self.x1:self.tx-=1
elif self.ty
self.ty+=2
elif self.ty
self.ty+=3
if self.ty>=self.y1:
self.in_metro=True
self.should_remove=True
def get_on_back(self):
if self.ty > self.y0:
self.ty-=1
else:
if self.tx > self.x0: self.tx-=1
elif self.tx
def get_off(self):
if self.ty>self.y1:
self.ty-=1
else:
self.in_metro=False
if self.tv>self.v1:
self.ty-=1
self.tv-=2
if self.tv<=self.v1:
self.should_remove=True
def draw(self):
if self.in_metro:
return
x=self.tx
y=self.ty
clr=(1-self.tv*0.01,1-self.tv*0.01,1-self.tv*0.01)
t.pencolor(clr)
goto_with_penup(x, y)
t.pensize(1)
t.begin_fill()
t.fillcolor(clr)
t.circle(4)
t.end_fill()
ty = 2
t.sety(y - ty)
t.goto(x - 6, y - ty - 2)
t.goto(x, y - ty)
t.goto(x + 6, y - ty - 2)
t.goto(x, y - ty)
ty = 6
t.sety(y - ty)
t.goto(x - 2, y - ty - 8)
t.goto(x, y - ty)
t.goto(x + 2, y - ty - 8)
t.goto(x, y - ty)
def fill_rect(w,h,c):
t.fillcolor(c)
t.begin_fill()
for i in range(2):
t.forward(w)
t.left(90)
t.forward(h)
t.left(90)
t.end_fill()
def draw_rect(w,h):
for i in range(2):
t.forward(w)
t.left(90)
t.forward(h)
t.left(90)
def setx_with_penup(tx):
t.penup()
t.setx(tx)
t.pendown()
def sety_with_penup(ty):
t.penup()
t.sety(ty)
t.pendown()
def goto_with_penup(tx,ty):
t.penup()
t.goto(tx,ty)
t.pendown()
size=60 # 车厢大小
block_num=7 # 车厢数量
human_num_str=[5,2,0,1,3,1,4] # 每节车厢的人数
# amount_human_num_str=[17,16,20,21,25,21,19] # 每节车厢的人数
amount_human_num_str=[26,25,24,24,25,24,25] # 每节车厢的人数
door_pos_arr=[0.3,1,1.7] # 一节车厢三扇门的位置
door_max_get_on_num=4
def draw_metro(x,size=40,door_open_ratio=0):
t.clear()
# t.fillcolor('white')
# t.begin_fill()
# goto_with_penup(-1000,200)
# t.goto(1000,200)
# t.goto(1000,-500)
# t.goto(-1000,-500)
# t.goto(-1000,200)
# t.end_fill()
t.pencolor('black')
t.pensize(1)
every_width=size*2
metro_h=size*0.6
goto_with_penup(x,0)
t.setx(x-every_width*0.2)
t.goto(x=x-every_width*0.1,y=metro_h)
t.setx(x)
goto_with_penup(x-every_width*0.1,metro_h*0.3)
draw_rect(metro_h*0.2,metro_h*0.4)
for a in range(0,block_num):
sety_with_penup(0)
setx_with_penup(x + a * every_width)
draw_rect(size*2,metro_h)
sety_with_penup(size*0.2)
window_w=size*0.3
window_h=size*0.25
for dx in [(door_pos_arr[0]+door_pos_arr[1])*0.5,(door_pos_arr[1]+door_pos_arr[2])*0.5]:
setx_with_penup(x + a * every_width + size*dx-window_w*0.5)
draw_rect(window_w,window_h)
sety_with_penup(0)
door_w=size*0.26
door_open_diff=0
if door_open_ratio>0:
r=door_open_ratio*0.01
door_open_diff=round(door_w*0.5*r)
for dx in door_pos_arr:
setx_with_penup(x + a * every_width + size*dx-door_w*0.5-door_open_diff)
if door_open_diff > 0: fill_rect(door_w*0.5,metro_h*0.8,'white')
draw_rect(door_w*0.5,metro_h*0.8)
setx_with_penup(x + a * every_width + size*dx+door_open_diff)
if door_open_diff > 0: fill_rect(door_w*0.5,metro_h*0.8,'white')
draw_rect(door_w*0.5,metro_h*0.8)
if door_open_diff > 0:
setx_with_penup(x + a * every_width + size*dx-door_open_diff)
fill_rect(door_open_diff*2,metro_h*0.8,'black')
sety_with_penup(size*0.25)
door_window_w=door_w*0.8
door_window_h=size*0.2
for dx in door_pos_arr:
setx_with_penup(x + a * every_width + size * dx - door_window_w * 0.5-door_open_diff)
draw_rect(door_window_w * 0.5, door_window_h)
setx_with_penup(x + a * every_width + size * dx+door_open_diff)
draw_rect(door_window_w * 0.5, door_window_h)
t.pensize(4)
for a in range(1,block_num):
sety_with_penup(0+1)
setx_with_penup(x + a*every_width+1)
t.sety(metro_h-1)
def make_human_pos(x,n,door_index,two_line=True):
arr=[]
if two_line:
for i in range(0,n,2):
human = Human(x-8,-20-20*(i//2),x1=x-4,y1=20)
human.door_index=door_index
arr.append(human)
for i in range(1,n,2):
human = Human(x+8,-20-20*(i//2),x1=x+4,y1=20)
human.door_index=door_index
arr.append(human)
else:
for i in range(0,n):
human = Human(x,-20-20*i,x1=x,y1=20)
human.door_index=door_index
arr.append(human)
return arr
def make_in_metro_human_pos(x, n,door_index):
arr=[]
for i in range(0, n):
human = Human(x, 20 * i, x1=x, y1=-10, v1=0)
human.door_index=door_index
human.in_metro=True
human.need_get_on=False
arr.append(human)
return arr
def any_get_off_human_in_door(human_arr, door_index):
for human in human_arr:
if human.door_index==door_index:
if not human.need_get_on and human.in_metro:
return True
return False
def check_no_got_on(all_human, door_got_on_num_dic):
for human in all_human:
if human.need_get_on and door_got_on_num_dic[human.door_index]>0:
return False
return True
def draw_all(all_human):
door_got_on_num_dic={}
for i in range(block_num):
door_got_on_num_dic[i*3]=door_max_get_on_num
door_got_on_num_dic[i*3+1]=door_max_get_on_num
door_got_on_num_dic[i*3+2]=door_max_get_on_num
t.screensize()
t.setup(width=1.0, height=1.0)
train_speed = 30
startx = 800
endx = -500
tempx = startx
while tempx > endx:
if tempx - endx < 500 and train_speed > 1:
if len(all_human)<20:
train_speed *= 0.9 if train_speed < 10 else 0.95
else:
train_speed *= 0.95 if train_speed > 10 else 0.98
tempx -= train_speed
if tempx < endx:
tempx = endx
t.tracer(False)
draw_metro(int(tempx), size=size)
for human in all_human: human.draw()
t.hideturtle()
t.tracer(True)
time.sleep(0)
for i in range(0, 100, 4):
t.tracer(False)
draw_metro(-500, size=size, door_open_ratio=i)
for human in all_human: human.draw()
t.hideturtle()
t.tracer(True)
time.sleep(0)
while True:
t.tracer(False)
draw_metro(int(tempx), size=size, door_open_ratio=100)
for human in all_human: human.draw()
t.hideturtle()
t.tracer(True)
time.sleep(0)
for i in range(len(all_human) - 1, -1, -1):
human = all_human[i]
if human.need_get_on:
if door_got_on_num_dic[human.door_index]>0:
if not any_get_off_human_in_door(all_human, human.door_index):
human.get_on()
if human.in_metro:
door_got_on_num_dic[human.door_index]-=1
else:
human.get_off()
if all_human[i].should_remove:
del all_human[i]
if len(all_human) == 0:
break
if check_no_got_on(all_human, door_got_on_num_dic):
for human in all_human:
if human.need_get_on:
human.y0+=20*(door_max_get_on_num//2)
break
for i in range(100, -4, -4):
t.tracer(False)
draw_metro(-500, size=size, door_open_ratio=i)
for human in all_human:
human.move(back=True)
human.draw()
t.hideturtle()
t.tracer(True)
time.sleep(0)
for x in range(len(all_human) - 1, -1, -1):
if all_human[x].should_remove:
del all_human[x]
endx = -2000
while tempx > endx:
train_speed = train_speed * 1.02
tempx -= train_speed
if tempx < endx:
tempx = endx
t.tracer(False)
draw_metro(int(tempx), size=size)
for human in all_human:
human.move(back=True)
human.draw()
t.hideturtle()
t.tracer(True)
time.sleep(0)
for x in range(len(all_human) - 1, -1, -1):
if all_human[x].should_remove:
del all_human[x]
def run(add_in_metro=None, amount_human=False):
endx = -500
num_str = amount_human_num_str if amount_human else human_num_str
all_human = []
for i in range(block_num):
n = 0 if i >= len(num_str) else int(num_str[i])
n1 = n // 3
n3 = int(math.ceil(n / 3))
n2 = n - n1 - n3
all_human += make_human_pos(endx + i * size * 2 + door_pos_arr[0] * size, n1, i * 3)
all_human += make_human_pos(endx + i * size * 2 + door_pos_arr[1] * size, n2, i * 3 + 1)
all_human += make_human_pos(endx + i * size * 2 + door_pos_arr[2] * size, n3, i * 3 + 2)
if add_in_metro:
n = 0 if i >= len(add_in_metro) else int(add_in_metro[i])
n1 = n // 3
n3 = int(math.ceil(n / 3))
n2 = n - n1 - n3
all_human += make_in_metro_human_pos(endx + i * size * 2 + door_pos_arr[0] * size, n1, i * 3)
all_human += make_in_metro_human_pos(endx + i * size * 2 + door_pos_arr[1] * size, n2, i * 3 + 1)
all_human += make_in_metro_human_pos(endx + i * size * 2 + door_pos_arr[2] * size, n3, i * 3 + 2)
while len(all_human)>0:
draw_all(all_human)
def draw_str(s):
t.screensize()
t.setup(width=1.0, height=1.0)
t.penup()
t.goto(-300,0)
t.pendown()
t.write(s, font=("Arial", 32,"bold"))
t.hideturtle()
if __name__ == '__main__':
draw_str('你希望上班坐地铁是这样的')
time.sleep(2)
run()
draw_str('再或者上班坐地铁是这样的')
time.sleep(2)
run(add_in_metro=[1,3,1,4,5,2,0])
draw_str('实际上上班坐地铁是这样的')
time.sleep(2)
run(amount_human=True,add_in_metro=[1,3,1,4,5,2,0])
draw_str('这段程序送给在外打拼的你')
time.sleep(2)
t.mainloop()