我想让一个C#应用程序实现Konami代码以显示复活节彩蛋。 http://en.wikipedia.org/wiki/Konami_Code
最佳的实现方式是什么?
这是一个标准的C# Windows窗体应用程序。
我想让一个C#应用程序实现Konami代码以显示复活节彩蛋。 http://en.wikipedia.org/wiki/Konami_Code
最佳的实现方式是什么?
这是一个标准的C# Windows窗体应用程序。
在 Windows Forms 中,我会创建一个类,该类知道序列是什么,并保存您在序列中的位置状态。像这样的代码应该可以实现:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsFormsApplication3 {
public class KonamiSequence {
List<Keys> Keys = new List<Keys>{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自身实现方式相同:
以下是不正确的做法:
累积一个按键缓冲区,然后逐字节进行字符串比较。效率低下,最好不要这样做。在表单上每次按键都会调用字符串解析例程,并且这些例程与采取一些简单步骤相比都很慢和笨重,而可以达到完全相同的效果。
一个有限状态机,在代码中重复序列时会出错。
一个有限状态机,硬编码了“特殊情况”。现在,你不能在一个地方进行修改。你必须改变代码字符串并添加新代码来处理你不合适的实现状态机。
实例化一个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;
}
}
}
_sequenceIndex = (_code[_sequenceIndex] == key) ? ++_sequenceIndex : 0;
按照要求,这里提供一个类来解决“问题”,即输入序列过慢无法像“秘密代码”一样。 ;)
NES卡带中的原始代码将在帧例程中调用,因此将通过计算执行次数来跟踪时间。
由于我们被限制为事件驱动的面向对象编程,因此我们将不得不涉及事件。由于这些事件需要强制“到期”,因此我们将不得不涉及Timer对象。
using System;
using System.Windows.Forms;
using Timer=System.Timers.Timer;
namespace WindowsApplication1
{
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 _sequenceIndex;
private readonly int _codeLength;
private readonly int _sequenceMax;
private readonly Timer _quantum = new Timer();
public KonamiSequence()
{
_codeLength = _code.Length - 1;
_sequenceMax = _code.Length;
_quantum.Interval = 3000; //ms before reset
_quantum.Elapsed += timeout;
}
public bool IsCompletedBy(Keys key)
{
_quantum.Start();
_sequenceIndex %= _sequenceMax;
_sequenceIndex = (_code[_sequenceIndex] == key) ? ++_sequenceIndex : 0;
return _sequenceIndex > _codeLength;
}
private void timeout(object o, EventArgs e)
{
_quantum.Stop();
_sequenceIndex = 0;
}
}
}
public class KonamiSequence
{
private static readonly Keys[] KonamiCode = { Keys.Up, Keys.Up, Keys.Down, Keys.Down, Keys.Left, Keys.Right, Keys.Left, Keys.Right, Keys.B, Keys.A };
private readonly Queue<Keys> _inputKeys = new Queue<Keys>();
public bool IsCompletedBy(Keys inputKey)
{
_inputKeys.Enqueue(inputKey);
while (_inputKeys.Count > KonamiCode.Length)
_inputKeys.Dequeue();
return _inputKeys.SequenceEqual(KonamiCode);
}
}
用例:
private readonly KonamiSequence _konamiSequence = new KonamiSequence();
private void KonamiForm_KeyDown(object sender, KeyEventArgs e)
{
if (_konamiSequence.IsCompletedBy(e.KeyCode))
MessageBox.Show("Konami!");
}
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class KonamiSequence
{
private 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 _index = 0;
public bool IsCompletedBy(Keys key)
{
if (key == _code[_index]) {
if (_index == _code.Length - 1) {
_index = 0;
return true;
}
++_index;
} else {
_index = 0;
}
return false;
}
}
}
_code.Length
(请参阅本文),但注意它仅在从序列中键入密钥时访问。Private Sub frm_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
Dim i As String = "UpUpDownDownLeftRightLeftRightBA"
If (e.KeyCode.ToString = "Up") And (konami <> "Up") Then konami = ""
konami = konami & e.KeyCode.ToString
'Debug.Print(konami)
If konami = i Then '' << INSERT YOUR MESSAGE HERE >> ''
If e.KeyCode.ToString = "Return" Then konami = ""
If konami.Length > 60 Then konami = ""
End Sub
我知道这是一个老问题,但我在VB中也开始了同样的旅程。我为此创建了一个类:
Public Class Konami
' Here is the pattern to match
Property KonamiOrder As List(Of Keys) = New List(Of Keys) From {Keys.Up, Keys.Up, Keys.Down, Keys.Down, Keys.Left, Keys.Right, Keys.Left, Keys.Right, Keys.B, Keys.A}
' Just calling these out ahead of time
Property sequence As List(Of Boolean)
Property ix As Integer = 0
' Hey new object, better set the important bits
Public Sub New()
me.reset()
End Sub
' Reset on pattern failure, or completion
Public Function reset() As Boolean
Me.sequence = New List(Of Boolean) From {False, False, False, False, False, False, False, False, False, False}
ix = 0
End Function
' Here's where all the action happens
Public Function checkKey(keycode As Keys)
'Check to see what they pressed
If sequence(ix) = False And keycode = KonamiOrder(ix) Then
' Hurray, they pressed the right key, better keep track of it
sequence(ix) = True
ix += 1
Else
' Nope, reset
Me.reset()
End If
'Is the code complete and correct?
If sequence.Contains(False) Then
' Nope, send back failure
Return False
Else
'Yep, reset so it can be used again and send back a success
Me.reset()
Return True
End If
End Function
End Class
这只是一个示例表单的代码后台,演示了konami类的用法。
Public Class Form1
Private oKonami As New Konami
Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
' Send the Key press on its way, and get some logic going
If oKonami.checkKey(e.KeyCode) Then
' Congrats, pattern match
MsgBox("Konami Code Entered")
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
' This will intercept the key events on this form
Me.KeyPreview = True
End Sub
End Class
我已经阅读了所有的答案,并发现序列初始值的重复输入是实现中常见的问题。以下是一个简单的实现,没有遇到初始问题的重复。没有特殊情况,没有真正硬编码的内容,类中指定的整数仅用于默认值。
public partial class KonamiCode {
public bool IsCompletedBy(int keyValue) {
for(var i=sequence.Count; i-->0; ) {
if(sequence[i]!=keyValue) {
if(0==i)
count=0;
continue;
}
if(count!=i)
continue;
++count;
break;
}
var isCompleted=sequence.Count==count;
count=isCompleted?0:count;
return isCompleted;
}
public KonamiCode(int[] sequence=default(int[])) {
this.sequence=
sequence??new[] { 38, 38, 40, 40, 37, 39, 37, 39, 66, 65 };
}
int count;
IList<int> sequence;
public static readonly KonamiCode Default=new KonamiCode();
}