Подтвердить что ты не робот

Код Konami в С#

Я ищу, чтобы приложение С# реализовало код Konami для отображения пасхального яйца. http://en.wikipedia.org/wiki/Konami_Code

Каков наилучший способ сделать это?

Это стандартное приложение форм Windows С#.

4b9b3361

Ответ 1

В формах Windows у меня будет класс, который знает, что представляет собой последовательность, и содержит состояние того, где вы находитесь в последовательности. Что-то вроде этого должно это сделать.

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).

EDIT: обновлен для winforms вместо wpf.

Ответ 2

Правильная последовательность, так же, как сама Konami внедрила бы ее:

  • введите
  • если вход равен байту в индексе массива кода, индекс инкремента
    • else, ясный индекс
  • если индекс больше длины кода, правильный код

Вот как это НЕ делать:

  • Накопление буфера нажатий клавиш и последующее сравнение байтовых байтов. В лучшем случае это неэффективно. Вы делаете вызовы в строковые синтаксические процедуры для каждого нажатия клавиши на форме, и эти подпрограммы медленны и громоздки по сравнению с некоторыми простыми шагами, которые можно предпринять для получения того же точного эффекта.

  • Конечный автомат, который прерывается каждый раз, если вы повторяете последовательности в коде.

  • Машина конечного состояния, которая имеет "особые случаи" жестко закодированную. Теперь вы не можете вносить изменения в одно место. Вы должны изменить строку кода плюс добавить новый код, чтобы иметь дело с неадекватно реализованным конечным автоматом.

  • Создайте экземпляр объекта 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;
        }
    }
}

Теперь, это быстро, не беспокоит строки или создает экземпляр какого-либо букера, чем массив, а изменения в коде так же просто, как изменение массива.

Инициализация поля в конструкторе заменяет жесткие константы кодирования, которые эквивалентны требуемым значениям. Если бы мы использовали константы, мы могли бы сократить код на 6 или около того "строк". Это немного расточительно, но позволяет классу максимально легко адаптироваться к новым кодам - ​​вам просто нужно изменить список массивов. Кроме того, вся "основная" обрабатывается во время создания экземпляра, поэтому это не влияет на эффективность нашего целевого метода.

С другой стороны, этот код можно сделать еще проще. Модуль не нужен, если вы вернетесь к правильному вводу кода.

Основная логика действительно может быть превращена в одну строку кода:

_sequenceIndex =  (_code[_sequenceIndex] == key) ? ++_sequenceIndex : 0;

Ответ 3

Поймать клавиши в 13 (или любое другое подмножество кода, поскольку вы, вероятно, не хотите включать ключ START) - список символов/массив/строка/все, прежде чем обрабатывать их в обычном режиме. Каждый раз, когда добавляется ключ, если (и только если) он последний ключ в серии, сопоставьте буфер с правильным кодом konami.

Мое предложение состоит в том, что если они нажмут клавишу со стрелкой, сопоставьте его с разумным письмом... затем также сопоставьте B и A, просто очистив буфер для любого другого нажатия клавиши.

Затем, сделав буфер строкой, сравните его с: "UUDDLRLRBABA"

Ответ 4

В соответствии с запросом, здесь класс, который разрешает "проблему", возможность слишком медленно вводить последовательность, чтобы быть "секретным кодом".;)

Исходный код в картридже 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;

        }
    }
}

Ответ 5

Я рекомендую вам реализовать как список событий поиска, так и указатель ссылки "захватить" на элементы этого списка.

Концептуально, вы начинаете указатель захвата на первый элемент списка поиска. Если самое следующее событие соответствует элементу поиска, указатель захвата увеличивается до следующего элемента. В противном случае это начало reset.

Если указатель увеличился за последний элемент, у вас есть полное соответствие.

Ответ 6

должна ли быть таблица времени выполнения? вы можете попасть в последовательность "UUDDLRLRBABA", но с нажатием клавиши 1 в минуту

Ответ 7

Я искал то же самое, и я придумал ОЧЕНЬ простой код, который просто работает. Keypreview должен быть True в форме, объявляющей одну строку с именем "konami" в вашей форме

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

Ответ 8

Я прочитал все ответы и обнаружил, что повторный ввод начальной последовательности является общей проблемой реализации. Следующее - простая реализация, не сталкиваясь с повторением исходной проблемы. Никаких особых случаев, ничто действительно не жестко закодировано, целые числа, указанные в классе, предназначены только для значения по умолчанию.

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

Ответ 9

Ответ можно найти в Reactive Extensions. Для этого вам нужен скользящий буфер. Это означает, что вам нужно сравнить последние десять нажатий клавиш с кодом Konami. Это работает с использованием двух разных операторов

  • Окно, чтобы получить поток потоков (в конечном итоге приводя к 10 одновременные потоки)
  • Буфер для суммирования каждого потока в IList

Буфер внутри RX выполняет обе эти задачи для нас. Буферирует последние 10 элементов и пропускает 1 (так эффективно создает 10 буферов).

        var keysIO = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
                                    .Select(arg => arg.EventArgs.Key)
                                    .Buffer(10, 1)
                                    .Select(keys => Enumerable.SequenceEqual(keys, _konamiArray))
                                    .Where(result => result)
                                    .Subscribe(i =>
                                                    {
                                                        Debug.WriteLine("Found Konami");
                                                    });

EDIT: Удалено временное решение., слишком сложное

РЕДАКТИРОВАНИЕ II: Я также решил использовать решение тайм-аута. Красота SelectMany: -)

        var keysIO = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
                            .Select(e => e.EventArgs.Key)
                            .Window(10, 1)
                            .SelectMany(obs => obs.Buffer(TimeSpan.FromSeconds(10), 10))
                            .Where(keys => Enumerable.SequenceEqual(_konamiArray, keys))
                            .Subscribe(keys => Debug.Write("Found Konami"));

Ответ 10

Я знаю, что это старый вопрос, но я отправился в то же путешествие в 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 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

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

Ответ 11

Здесь довольно простое и эффективное решение:

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!");
}

Ответ 12

Вот еще одна реализация, основанная на Ответ Джеймса и комментарии:

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 (см. эту статью), однако обратите внимание, что он доступен только при вводе ключа из последовательности.
  • Принимает дело "UUUUUUUUUUUDDLRLRBA".
  • Конечно, сбрасывает последовательность, если набрал неправильный ключ.