Konami Code in C#

后端 未结 11 2107
执笔经年
执笔经年 2020-12-07 19:07

I am looking to have a C# application implement the Konami Code to display an Easter Egg. http://en.wikipedia.org/wiki/Konami_Code

What is the best way to do this?<

相关标签:
11条回答
  • 2020-12-07 19:53

    The correct sequence, the same way Konami itself would have implemented it:

    • get input
    • if input equals byte at index of code array, increment index
      • else, clear index
    • if index is greater than code length, code is correct

    Here is how NOT to do it:

    • Accumulating a buffer of keystrokes and then doing a byte-by-byte string compare. Inefficient, at best. You're making calls into string parsing routines for every key press on the form and those routines are slow and bulky compared to some simple steps that could be taken to get the same exact effect.

    • A finite state machine that breaks every time if you repeat sequences in the code.

    • A finite state machine that has "special cases" hard coded. Now, you can't make modifications in one place. You have to change the code string plus add new code to deal with your inappropriately implemented state machine.

    • Instantiate a List object to hold something simple like a list of characters.

    • Involve String objects.

    So, here's how to it:

    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;
            }
        }
    }
    

    Now, it's fast, does not bother with strings or instantiate anything bulker than an array, and changes to the code are as simple as modifying the array.

    The field initialization in the constructor takes the place of hard coding constants that are equivalent to the values needed. If we used constants, we could have shortened the code by 6 or so "lines." This is slightly wasteful, but allows the class to be as easily adaptable to new codes as possible -- you just need to change the array list. Plus, all of the "bulk" is handled at the time of instantiation, so it is not affecting the efficiency of our target method.

    On second glance, this code could be made even simpler. The modulus is not needed, so long as you are resetting the value on correct code entry.

    The core logic could actually be made into a single line of code:

    _sequenceIndex =  (_code[_sequenceIndex] == key) ? ++_sequenceIndex : 0;
    
    0 讨论(0)
  • 2020-12-07 19:55

    As requested, here's an class that resolves the "issue" of being able to enter the sequence too slowly to be "secret code like." ;)

    The original code in the NES cartridge would have been called within a frame routine and thus would have tracked time by counting execution passes.

    Since we're relegated to event-driven, object oriented programming, we're going to have to involve events. Since these events will need to enforce an "expiration," we're going to have to involve a Timer object.

    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;
    
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-07 19:58

    I know it's an old question, but I embarked on this same journey in VB. I created a class for it:

    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
    

    This is just a sample form's code behind on the usage of the konami class.

    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
    

    https://github.com/the1337moderator/KonamiCodeforVB.net

    0 讨论(0)
  • 2020-12-07 20:03

    I was searching for the same thing and I came up with a VERY simple code that just works. Keypreview have to be True on the form declare one string named "konami" on your form

    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
    
    0 讨论(0)
  • 2020-12-07 20:05

    I recommend you implement as a list of search events and a "capture" reference pointer to elements of that list.

    Conceptually, you start the capture pointer to the first element of the search list. If the very next event matches the search element, the capture pointer is incremented to the next element. Otherwise, it is reset to the beginning.

    If the pointer is incremented past the last element, you have a full match.

    0 讨论(0)
提交回复
热议问题