使用Echosim语音控制2048

前一阵子去hackathon和队友体验了一把Amazon Echo特别爽。这个小玩意扩展能力很好,你可以通过它直接和你的电脑进行交互。这次我们使用python的Flask来通过Echosim操作2048,完成各个方向的滑动。

让我们开始吧!

##工具准备

修改2048的输入源

要使用Echo作为输入源,原理非常简单粗暴。只要把程序中的键盘输入源去掉->换成Echo输入源。具体来讲就是

  • 使用’left’,’right’字符串来代替键盘按键
  • 使用Echo生成’left,’right’字符串

让我们先来看看2048程序里面的两个文件,其中puzzle部分包含了键盘输入部分,分为下面几个部分

第一部分定义了2048以接受’wasd’字符的方式来操作

1
2
3
4
   KEY_UP = "'w'"
KEY_DOWN = "'s'"
KEY_LEFT = "'a'"
KEY_RIGHT = "'d'"

因为我们会对Echo说出具体方向,所以我们把它改成

1
2
3
4
KEY_UP = "up"
KEY_DOWN = "down"
KEY_LEFT = "left"
KEY_RIGHT = "right"

现在我们要取消wasd与键盘上的wasd按键绑定,先删掉GameGrid类中init方法里面的

1
self.master.bind("<Key>", self.key_down)

接着将key_down方法中的

1
key = repr(event.char)

改成

1
key = event

由于程序使用了tkinter的GUI,内部又一个封闭的循环我们不能通过python的交互指令来操作2048,所以我们需要先禁用这个初始化以及自动循环的过程,后面我们手动初始化,来到GameGrid类中的init方法,删除下面几行

1
self.mainloop()

这个时候我们已经摆脱了了原来的操作方式,来python的交互界面试一下

1
2
3
4
5
>>> from puzzle import *
>>> gamegrid = Gamegrid()
#操作
>>> gamegrid.key_down('up')
>>> gamegrid.key_down('down')

这个时候我们对puzzle.py的改造已经完成,我们需要一种类似input的方法来进行移动,接下来我们来使用Flask搭建和Echo沟通的桥梁

交互模型

修改了原游戏之前,我们先来看交互模型,这为我们以后使用Flask-Ask构建了框架。在交互模型中我们要定义Intent(意图),和不同表达所对应的意图。

Intent Schema

{

    "intents": [

      {

    "intent": "YesIntent"

      }, 


      {

    "intent": "AnswerIntent",

    "slots": [
    {
        "name": "action",
        "type": "LIST_OF_SIGNS"
    }
        ]

       }

        ]

}

Amazon Echo 使用json格式来进行交互,上面这个交互模型主要包含了各个Intent,其中

  • YesIntent: 当Echo询问是否开始游戏时,回答是时执行(启动游戏)
  • AnswerIntent:启动游戏后,所有的指令都通过此条传输,此处定义Echo接收的变量名名称和类型,我们将使用slot来定义,名字为action,类型为自定义类型LIST_OF_SIGNS

Custom Slot Types

在这里我们定义LIST_OF_SIGNS为

up
down
left
right

Sample Utterance

这里定义了识别方法,当你说yes/start/begin时会自动导向YesIntent,’{}’内是会被传输的变量

YesIntent start
YesIntent begin
YesIntent Yes

AnswerIntent {action} 
AnswerIntent turn {action}
AnswerIntent go {action}

##使用Flask-Ask搭建服务

创建一个名为server.py的文件

初始化

引入我们的工具包,引入2048

1
2
3
from flask import Flask
from flask_ask import Ask,statement,question,session
from puzzle import *

对引入的内容进行初始化,因为游戏没有开始,我们设置状态为not_yet

1
2
3
4
app = Flask(\_\_name\_\_)
ask = Ask(app,"/")
gamegrid=None
status = 'not\_yet'

欢迎界面

欢迎界面就是当你命令Alexa(Echo)启动程序后,Echo的反应。我们在这里使用装饰器@ask.launch,并用字符串来表达Alexa要说的话,用question来发问。

1
2
3
4
5
@ask.launch
def welcome():
welcome_msg = "welcome to 20 48,say yes to begin, say instruction for instruction "

return question(welcome_msg)

启动游戏

欢迎界面过后我们要说yes来启动游戏,@ask.intent(‘YesIntent’)表示回答”YesIntent”后对应的操作,我们在这里初始化游戏(使用global表示游戏在主线程中,tkinter的GUI必须在主线程中),修改当前状态(避免在游戏过程中被重复执行)

1
2
3
4
5
6
7
8
9
10
11
@ask.intent('YesIntent')
def start_instruction():

global status
global gamegrid
if status == 'not_yet':
gamegrid =GameGrid()
ins_msg = "Please say the direction to move"
status = 'in_game'
return question(ins_msg)
return question("already in game")

操作

启动游戏后,自动进入操作模式。这个时候我们使用定义好的AnswerIntent来接收指令,将action通过装饰器作为字符串变量传入函数中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ask.intent("AnswerIntent",convert ={'action':str})
def game(action):
global direc
global gamegrid
if action in ['left','right','up','down']:
try:

gamegrid.key_down(action)

except:
pass

return question('')

return question('invalid action,please try again')

如果action是有效的,我们的question是’’,即没有反馈,当遇到非法操作时才报错

question和statement

每次通过ask.launch进入程序后,都有一个新的session,statement和question的作用都是将预先安排好的字符串作为语音反馈。但是有一个关键的区别:statement会结束掉当前Session(直接退出),而question会保留当前Session,这是我们循环的根本。

测试

启动服务

下载ngrok,并运行

  • Unix/Mac

    ./ngrok http 5000

  • Windows

    ngrok.exe http 5000

接着同时运行你写好的server.py

Developer上创建新技能

登入你的Amazon Developer账户,点击Alexa ->Alexa Skill Kit -> Create a new skill进入设置页面

  • Skill Information,为我们的程序起名字(启动口令),我们这里叫 twenty forty eight

“Alexa,Start Twenty forty eight”

  • Interaction Model,将我们的交互模型放入即可
  • Configuration,选择HTTPS,并将你的ngrok生成的https地址拷贝到上面

  • SSL Certificate下选择‘My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority’一项后,点击next,你的测试状态就变为可用了

Echosim

进入Echosim.io,用你的amazon developer账户登入即可测试

课后拓展

有兴趣的朋友可以完成以下任务来巩固所学知识

  • 添加一个StopIntent使其可以终止
  • 判断输赢,并报出结果
  • 通过口令重新启动游戏

Reference