Continuando a serie sobre o modulo asyncio do Python 3.4, vamos ver como criar um jogo simples, em modo texto. O objetivo do jogo e simplesmente mostrar um labirinto e deixar o jogador se mover utilizando o teclado numerico (4 - esquerda, 6 - direita, 8 - cima, 2 - baixo e S para sair). Para exercitar nossos musculos da epoca do DOS com programacao assincrona, vamos exibir o relogio na ultima linha. O resultado final deve se parecer com a imagem abaixo:Voce precisa instalar o colorconsole e o Python 3.4.1 para executar este programa.from random import shuffle, randrangefrom colorconsole import terminalfrom concurrent.futures import ThreadPoolExecutorimport datetimeimport asyncio# Colorconsole: https://github.com/lskbr/colorconsole# Make_make: http://rosettacode.org/wiki/Maze_generation#Pythondef make_maze(w = 16, h = 8): vis = [[0] * w + [1] for _ in range(h)] + [[1] * (w + 1)] ver = [["| "] * w + ['|'] for _ in range(h)] + [[]] hor = [["+--"] * w + ['+'] for _ in range(h + 1)] def walk(x, y): vis[y][x] = 1 d = [(x - 1, y), (x, y + 1), (x + 1, y), (x, y - 1)] shuffle(d) for (xx, yy) in d: if vis[yy][xx]: continue if xx == x: hor[max(y, yy)][x] = "+ " if yy == y: ver[y][max(x, xx)] = " " walk(xx, yy) walk(randrange(w), randrange(h)) maze = [] for (a, b) in zip(hor, ver): maze.append("".join(a)) maze.append("".join(b)) return maze class Jogo: LARGURA = 24 ALTURA = 11 def __init__(self): self.tela = terminal.get_terminal(conEmu=False) self.tela.enable_unbuffered_input_mode() self.labirinto_cores = (terminal.colors["RED"],terminal.colors["BLACK"]) self.jogador_carac = (terminal.colors["WHITE"],terminal.colors["BLUE"],'*') self.labirinto = make_maze(Jogo.LARGURA,Jogo.ALTURA) self.loop = asyncio.get_event_loop() self.tpool = ThreadPoolExecutor(max_workers=2) while True: self.x = randrange(Jogo.LARGURA*3) self.y = randrange(Jogo.ALTURA*2) if self.pode_mover(self.x, self.y): break self.jogando = True def fim_do_jogo(self): self.jogando = False self.loop.stop() @asyncio.coroutine def le_teclado(self): while self.jogando: key = yield from self.loop.run_in_executor(self.tpool, self.tela.getch) if(key!=None): nx, ny = self.x, self.y if key == b'4': if nx > 1: nx-=1 elif key == b'6': if nx < Jogo.LARGURA*3-1: nx+=1 elif key == b'8': if ny > 0: ny -=1 elif key == b'2': if ny < Jogo.ALTURA*2: ny +=1 elif key == b"S": self.fim_do_jogo() break if self.pode_mover(nx,ny) and (nx, ny) != (self.x,self.y): self.x, self.y = nx, ny self.desenha() def pode_mover(self, x,y): return self.labirinto[y][x]==' ' def desenha(self): self.tela.set_color(*self.labirinto_cores) #self.tela.clear() self.tela.gotoXY(0,0) self.tela.set_title("Labirinto") for linha in self.labirinto: print(linha) self.tela.gotoXY(self.x, self.y) self.tela.cprint(*self.jogador_carac) @asyncio.coroutine def relogio(self): while self.jogando: self.tela.print_at(10,23,datetime.datetime.now().strftime("%d/%m/%y %H:%M:%S")) yield from asyncio.sleep(1) def execute(self): self.tela.clear() self.desenha() try: asyncio.async(self.le_teclado()) asyncio.async(self.relogio()) self.loop.run_forever() except KeyboardInterrupt: print("exit") finally: self.tpool.shutdown() self.loop.close() self.tela.restore_buffered_mode()jogo = Jogo()jogo.execute()Para adicionar um pouco de variacao ao jogo, eu baixei uma funcao que gera labirintos deste site. O programa foi modificado para retornar uma lista de strings, que e utilizada para detectar as paredes do labirinto.O loop de eventos e parecido com o dos artigos anteriores, mas desta vez um ThreadPool esta sendo criado. Um thread extra e necessario, pois vamos bloquear a rotina ate que uma tecla seja pressionada. Esta construcao funciona com varias rotinas bloqueantes que nao podem ser utilizadas com asyncio, pois interromperiam a execucao de todas as outras corotinas. @asyncio.coroutine def le_teclado(self): while self.jogando: key = yield from self.loop.run_in_executor(self.tpool, self.tela.getch) if(key!=None): nx, ny = self.x, self.y if key ==