我希望有一个C#应用程序实现Konami代码来显示复活节彩蛋. http://en.wikipedia.org/wiki/Konami_Code
做这个的最好方式是什么?
这是一个标准的C#windows窗体应用程序.
在Windows窗体中,我将有一个类知道序列是什么,并保持序列中的位置状态.这样的事情应该做到这一点.
using System; using System.Collections.Generic; using System.Windows.Forms; namespace WindowsFormsApplication3 { public class KonamiSequence { ListKeys = new List {System.Windows.Forms.Keys.Up, System.Windows.Forms.Keys.Up, System.Windows.Forms.Keys.Down, System.Windows.Forms.Keys.Down, System.Windows.Forms.Keys.Left, System.Windows.Forms.Keys.Right, System.Windows.Forms.Keys.Left, System.Windows.Forms.Keys.Right, System.Windows.Forms.Keys.B, System.Windows.Forms.Keys.A}; private int mPosition = -1; public int Position { get { return mPosition; } private set { mPosition = value; } } public bool IsCompletedBy(Keys key) { if (Keys[Position + 1] == key) { // move to next Position++; } else if (Position == 1 && key == System.Windows.Forms.Keys.Up) { // stay where we are } else if (Keys[0] == key) { // restart at 1st Position = 0; } else { // no match in sequence Position = -1; } if (Position == Keys.Count - 1) { Position = -1; return true; } return false; } } }
要使用它,您需要在表单的代码中响应密钥事件.这样的事情应该这样做:
private KonamiSequence sequence = new KonamiSequence(); private void Form1_KeyUp(object sender, KeyEventArgs e) { if (sequence.IsCompletedBy(e.KeyCode)) { MessageBox.Show("KONAMI!!!"); } }
希望这足以满足您的需求.对于WPF,您需要的细微差别非常相似(请参阅编辑历史记录#1).
编辑:更新为winforms而不是wpf.
正确的顺序,就像Konami本身实现它一样:
得到输入
如果输入等于代码数组索引处的字节,则增加索引
否则,明确指数
如果index大于代码长度,则代码是正确的
这是不怎么做:
累积按键缓冲区,然后进行逐字节字符串比较.充其量只是效率低下.您正在为表单上的每个按键调用字符串解析例程,并且这些例程与可以采取相同的确切效果的一些简单步骤相比是缓慢而笨重的.
一个有限状态机,如果在代码中重复序列,每次都会中断.
具有"特殊情况"硬编码的有限状态机.现在,您无法在一个地方进行修改.您必须更改代码字符串并添加新代码以处理不适当实现的状态机.
实例化一个List对象以保存简单的字符列表.
参与String对象.
那么,这是如何做到的:
using System.Windows.Forms; namespace WindowsFormsApplication1 { public class KonamiSequence { readonly Keys[] _code = { Keys.Up, Keys.Up, Keys.Down, Keys.Down, Keys.Left, Keys.Right, Keys.Left, Keys.Right, Keys.B, Keys.A }; private int _offset; private readonly int _length, _target; public KonamiSequence() { _length = _code.Length - 1; _target = _code.Length; } public bool IsCompletedBy(Keys key) { _offset %= _target; if (key == _code[_offset]) _offset++; else if (key == _code[0]) _offset = 2; // repeat index return _offset > _length; } } }
现在,它很快,不会打扰字符串或实例化任何bulker而不是数组,并且对代码的更改就像修改数组一样简单.
构造函数中的字段初始化取代了与所需值等效的硬编码常量.如果我们使用常量,我们可以将代码缩短6个左右的"行".这有点浪费,但允许类尽可能轻松地适应新代码 - 您只需要更改数组列表.另外,所有"批量"都是在实例化时处理的,因此它不会影响我们的目标方法的效率.
乍一看,这段代码可以更简单.只要您在正确的代码输入上重置值,就不需要模数.
核心逻辑实际上可以组成一行代码:
_sequenceIndex = (_code[_sequenceIndex] == key) ? ++_sequenceIndex : 0;
在正常处理它们之前,将按键捕获到13(或代码的任何子集,因为您可能不想包含START键) - 字符列表/数组/字符串/等等.每次添加一个键时,如果(并且仅当)它是该系列中的最后一个键,则将缓冲区与正确的konami代码匹配.
我的建议是,如果他们按箭头键,将其映射到合理的字母......然后映射B和A,只需清除任何其他按键的缓冲区.
然后,将缓冲区设为字符串,将其与"UUDDLRLRBABA"进行比较