The Last Slice: Final

The Last Slice series:Intro |  Challenge 1 & 2 | Challenge 3 | Thanks

 

The Last Slice是由巨硬举办的极客挑战赛。本篇是讲解最终挑战Challenge 3的解法。

在完成Challenge 2后,会收到巨硬的邮件,按照指示填写个人地址,巨硬会寄一个“披萨”过来:

当然,其中是一个树莓派2(以及线材和说明书):

树莓派上已经通过GPIO接了一个Arduino pro mini控制的板子,带有一个8x8 LED矩阵。Challenge3的题目就在这个板子中,你所要做的就是将程序写到树莓派上,控制GPIO与Arduino交互,解开谜题。

首先,要向树莓派刷写Windows IoT系统。(树莓派2、3,以及其他一些类似设备都可以刷写此系统,所以如果你想体验一把,都可以试试)访问以下地址下载Windows IoT Dashboard:

https://developer.microsoft.com/en-us/windows/iot/Downloads

在这个工具中可以下载Windows IoT到TF卡,也可以管理已连接的设备,包括打开设备上的Powershell和Webshell,还是比较实用的,只是互传文件用的网络共享(IPC)功能从来没成功过,另外远程桌面连接(RDP)不支持树莓派2代(由于没有硬件加速……)。

刷写成功后就可以开机树莓派了。WinIoT作为物联网用系统,并没有完整的UI前端,但可以运行有UI的UWP应用。它默认会自动启动一个UWP应用作为UI前端——这个默认应用能显示设备信息、带有浏览器和命令行,并附带一些开发教程示例。你也可以配置它默认启动别的UI应用或后台应用。此系统同一时间只能启动一个前台UI应用,但后台可以同时运行数个无UI的应用(服务)。

通过“网线——网线转USB——PC”的方式将树莓派连接到电脑,打开VS,现在你就可以用C#/UWP开发物联网应用了:

回到挑战的话题,在树莓派启动时,Arduino板也会一起启动,最后会在LED上显示一个笑脸:

比赛正式开始后,会收到邮件:

Use the following GPIO Connection Guide:

 

GPIO -> Pizza Oven 2000

2 -> A

3 -> B

4 -> D

5 -> L

6 -> N

7 -> P

8 -> R

9 -> T

10 -> U

 

  1. To unlock your IoT device, give Mad Dog and Scorpion 30 lives.
  2. Collect the correct pizza ingredients.
  3. Further instructions will be given through Gordon Bell’s first design at DEC.  Any submissions made prior to the delivery of this email do not qualify.

前半部分是在讲GPIO接口,2就是指new GpioPin(2)。而A则是对应到Arduino板(以下简称“A板”)的某种输入。

后半部分是挑战的提示。第一条:要解锁挑战,给“疯狗”和“蝎子”30条命。

疯狗和蝎子我们并不熟悉,但是“30条命”则是很容易联想的——是的,这是在说著名FC(NES)游戏《魂斗罗》(Contra)的作弊码:上上下下左右左右BA。(疯狗和蝎子是美版魂斗罗的两位主角的名字。)也就是说我们接下来要向A板输入这个。

观察上面的GPIO提示中的字母,我们可以发现U、D、L、R、B、A这些字母应该对应上下左右BA。但是还有3个字母:P、N、T。一开始我并没意识到这三个字母是干什么的,就只用UDLR一直在试却一直无效。后来我在单步调试时偶然把P放到首位去试才发现了端倪。

其实是这样的:上是UP,下是DowN,左是LefT,右是RighT。要“按键”,就要使GPIO的输入能够匹配某个按键所需的电平(在此处按键需要的是低电平)。比如要按“UP”键,则要使U和P端(GPIO PIN 10和7)处于低电平(若同时出现其他组合,比如D和N也处于低电平,则相当于同时按下了UP和DOWN两个键,但是此游戏中不需要这种情况)。

此时就可以写个程序将输入映射到键盘上:

        Window.Current.CoreWindow.KeyDown += Page_KeyDown;

        private async Task Push(int n, bool reset = true)
        {
            var gc = GpioController.GetDefault();
            using (var l = gc.OpenPin(n))
            {
                l.Write(GpioPinValue.Low);
                l.SetDriveMode(GpioPinDriveMode.Output);
                
                if (reset)
                {
                    await Task.Delay(30);
                    l.Write(GpioPinValue.High);
                }
                
            }
        }

        private async void Page_KeyDown(CoreWindow coreWindow, KeyEventArgs args)
        {
            var gc = GpioController.GetDefault();
            u = gc.OpenPin(10);
            p = gc.OpenPin(7);
            d = gc.OpenPin(4);
            n = gc.OpenPin(6);
            l = gc.OpenPin(5);
            r = gc.OpenPin(8);
            t = gc.OpenPin(9);
            a = gc.OpenPin(2);
            b = gc.OpenPin(3);

            switch (args.VirtualKey)
            {
                case VirtualKey.Up:
                    await Push(u, false);
                    await Push(p, true);
                    break;
                case VirtualKey.Down:
                    await Push(d, false);
                    await Push(n, true);
                    break;
                case VirtualKey.Left:
                    await Push(l, false);
                    await Push(t, true);
                    break;
                case VirtualKey.Right:
                    await Push(r, false);
                    await Push(t, true);
                    break;
                case VirtualKey.A:
                    await Push(a, true);
                    break;
                case VirtualKey.B:
                    await Push(b, true);
                    break;
                default:
                    //txtStatus.Text = "unknown key pressed.";
                    break;
            }
        }

能够在物联网开发中用上async/await、XAML之类,这就是Windows IoT的魅力吧。

随后用键盘输入↑↑↓↓←→←→BA,每当正确输入一个键,LED就会用绿灯显示对应的按键(A、B是全亮)。输入完后,进入第二关:(当时没有拍,此图转自twitter

此时LED上会显示地图,黄色=墙,绿色=食材,红色=你。按照第二条指示:收集披萨原料。此处就是控制小红点将所有食材收集到即可。操作方式同第一关,只用上下左右键。

此时我们会联想到在Challenge 2中,一直有一个没有用到的要素:RaspiMap.cs。显然那个地图就是这里要用的了。这个地图实际上是16*11的,而LED屏幕却是8*8的。我们的初始位置是(0,0),尝试走出下或右边界,却发现镜头并没有跟着动,仍然显示的是左上角的这块8*8的区域。也就是说,其他区域你要“摸黑”尝试了。不过,右上角8*8的区域地图已经记载在RaspiMap当中,只有左下和右下区域需要自己反复尝试(可以每走一步都记到地图上)。一旦撞到墙或非食材物品,游戏都会从(0,0)重来(吃到的东西也全部清零)。其实本游戏共4个食材,左上、左下、右上、右下区域各一个。

全部集齐后,LED屏幕会滚动显示UART——这就是第三条提示:贝尔在DEC的第一个发明,UART。模仿巨硬的文档,写一个UART的接收程序(同样会使用async/await语法)。每次UART闪过一遍,你都会接收到同样的一条消息:You've found all ingredients, here is your order confirmation# 后接一串key和一个网址。访问网址,自动跳转到skype(幸好装了)与一个聊天机器人发起对话。这时进入第三关:聊天机器人问答。

在确认MS账号之后,聊天机器人(bot)开始提问:

Q1:在Challenge 1中,最后一关完成后显示的街道地址是什么?

没想到,这个机器人竟然会从Challenge 1开始问起。我早就把Challenge 1的程序删掉了,这下不得不重新打开VS部署起来,通关之后才看到结尾动画中出现的那个地址。

Q2:在Challenge 2中,你通关时找到的所有食材以及对应的优惠券编号是?

厉害了,这是有意让没有注意所有细节的挑战者重新玩一把前两个挑战。所谓优惠券编号是在Challenge 2提交了正确答案(所有找到的食材)后返回的一个GUID,提示了要好好保存,但是没想到竟然还要输入对应的所有食材!幸好我当时有截了完整的图。如果没有记住这个,也可以再玩一遍Challenge 2以得到满足要求的结果,可以推测这里的GUID并不是完全随机,而是包含了食材的hash在里面。

Q3:请输入Challenge 3的Order Confirmation Number。

这个就输入从UART中得到的key即可。

只要正确答对这三个问题,挑战就正式完成了。但是这个bot有点不怎么“智能”,它会要求你先输入所有问题的答案,然后才开始验证,这其中若有一点偏差,就要从头(输入MS账号)开始……加上“回味”前两个挑战的时间,这个bot看似简单却能够拖延人很长时间。

最终,挑战完成,你会收到bot的最终寄语,大意是:

 

你的答案都对了,然而,你不是前五个完成的挑战者。

虽然今天不是你的幸运日,但你显然还是证明了你的实力。

我们,作为Last Slice总部(也叫微软Windows组),祝贺你完成了所有的挑战!

从我这机器人的小内心中,由衷地感谢您参加The Last Slice。

 

至此,Challenge 3挑战完成。

事实上,在比赛开始后仅数分钟,已经有2个人完成了挑战。我怀疑他们可能是把A板上的程序搞了出来然后逆向从而跳过了前两关,直接坐等机器人上线。尽管这种方式有违比赛精神,但我还是要承认,不同于拿到树莓派就沾沾自喜的我,这才是真正想赚10k美刀的人的觉悟。

 
The Challenge 3 was begun at the day of QiXi (七夕, a.k.a. Chinese Valentine's Day). I spent all the day with this little "girlfriend".
 
 
I would like to thank AxisRay , my friend who has a girlfriend but still talked to me a lot at that day. 
 
And I would like to thank Mr. JP Hellemons, who would like to read my blog via google translate, joined the game at 2:00am that day (GMT+2) and also talked to me a lot before exhausted. Thank you and I'm sorry that I was unable to figure out the key to the first level earlier... m(_ _)m

添加评论

Loading