Cómo verificar una secuencia de pulsaciones de teclas

I'm making a game and want to implement cheat codes like the Konami code.

But how do I check for that sequence of keystrokes?

I want it to work so that if a player just types the code it will trigger.

Gracias de antemano!

preguntado el 27 de agosto de 11 a las 20:08

5 Respuestas

Below is a class that checks for the Konami code, including cases such as "UP, UP, UP, DOWN, etc."

Esto debería funcionar para cualquier given sequence.

import java.util.Map;
import java.util.TreeMap;

public class Konami {

    static private int[] code = 
        {UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B};
    static private Map<Integer, Integer>[] graph;
    static private int currentNode = 0;

    public static void main(String args[]) {
        //Create graph
        graph = generateSequenceMap(code);

        //Call checkKonami(key) whenever a key is pressed
    }


    static public boolean checkKonami(int keyPressed) {
        Integer nextNode = graph[currentNode].get(keyPressed);

        //Set currentNode to nextNode or to 0 if no matching sub-sequence exists
        currentNode = (nextNode==null ? 0 : nextNode);

        return currentNode == code.length-1;
    }


    static private Map<Integer, Integer>[] generateSequenceMap(int[] sequence) {

        //Create map
        Map<Integer, Integer>[] graph = new Map[sequence.length];
        for(int i=0 ; i<sequence.length ; i++) {
            graph[i] = new TreeMap<Integer,Integer>();
        }

        //i is delta
        for(int i=0 ; i<sequence.length ; i++) {
            loop: for(int j=i ; j<sequence.length-1 ; j++) {
            if(sequence[j-i] == sequence[j]) {
                System.out.println("If at Node "+j+" you give me seq["+(j-i+1) 
                        + "] OR " + (sequence[j-i+1]) + " , goto Node " + (j-i+1));

                //Ensure that the longest possible sub-sequence is recognized
                Integer value = graph[j].get(sequence[j-i+1]);
                if(value == null || value < j-i+1)
                    graph[j].put(sequence[j-i+1], j-i+1);
            }
            else
                break loop;
            }
        }
        return graph;
    }
}

Respondido 29 ago 11, 17:08

Also a very gode solution, and with a little modification, now allows me to check for a hold range of cheats. :) another thank-you send your way. - Noloxs

EDIT: See my other post for code that always works. The following doesn't detect the code if it overlaps with itself (for instance: "UP, UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B" wouldn't work)

Gracias a Gevorg por señalar esto


If it's how to identify the sequence that you are concerned with only (I'll assume you know how to get input from the keyboard) then you can have something as follows.

int[] sequence = {UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B};
int currentButton = 0;

boolean checkKonami(int keyPressed) {
    //Key sequence pressed is correct thus far
    if(keyPressed == sequence[currentButton]) {
        currentButton++;

        //return true when last button is pressed
        if(currentButton == sequence.length) {

            //Important! Next call to checkKonami()
            //would result in ArrayIndexOutOfBoundsException otherwise
            currentButton = 0;

            return true;
        }
    }
    else {
        //Reset currentButton
        currentButton = 0;
    }

    return false;
}

Call this function whenever a key press is registered, passing the key that has been pressed. Of course modify the types where appropriate.

Respondido 29 ago 11, 17:08

UP,UP,UP,DOWN,DOWN,LEFT,RIGHT,LEFT,RIGHT,B breaks your algorithm, doesn't it? The third U brings currentButton back to 0 and the patter is not recognized. Note that if you discard the first UP, the sequence is valid! - Marsellus Wallace

Well spotted! The latter part was supposed to be in an else block. - WaelJ

Fijo. Bangs head against wall - WaelJ

Yeah, that 'else' helps! But the algorithm still breaks for my given sequence above. By the 3rd keystroke your algorithm accepted the first two already (UP,UP) and it was expecting 'DOWN' but after receiveing another 'UP' currentButton goes back to 0. Still, my keystrokes sequence is valid and it should be accepted. - Marsellus Wallace

Had to add a = more in the first if, and remove the -1 to sequence.length. But is working perfectly now, thank you very much - Noloxs

I'm sure you're past this project now, but I just implemented this into one of my assignments and wanted to leave it for others to find. This solution logs the last n keystrokes (defined here as 10) into a circular array and returns true when they match our code. You could just as easily pass in different lengths and codes as part of the method (but this implementation didn't require it). I used ^ ^ v v < > < > b a.

public class Code {
private static int[] history = new int[10];
private static int front = 0;
private static int size = 0;

// Here is the Code they must enter (ascii vals for konami).
private static int[] code = {38, 38, 40, 40, 37, 39, 37, 39, 66, 65};

// Static class. No constructor.
private Code(){}

// Adds key-press into history buffer. If code is matched, return true.
public static boolean add(int e){

    // Write the value into our key history.
    history[(front + size) % 10] = e;

    // Stop growing at length 10 and overwrite the oldest value instead.
    if (size < 10){
        size++;
    } else {
        front = front + 1 % 10;
    }

    // Compare our history (from the current front) to the code (from 0)
    for(int i = front; i < front + size; i++){
        if (history[i % 10] != code[i-front]){
            // Any 1 mismatch will abort
            return false;
        }
    }
    // Make sure we've logged enough keystrokes so it doesn't fire off
    // if your first key press matches the code.
    if (size < 10){
        return false;
    }
    return true;
}

¡Disfrutar! :RE

respondido 31 mar '13, 08:03

I don't know what your needs are. Are you trying to create a game using System.in y System.out or are you trying to make a full visual GUI?

In the meantime, see interface java.awt.event.KeyListener. (Documentación de Oracle) Also, see Oracle's Tutorial.

And based on personal experience, the code below approximates what you need.

import java.awt.event.*; //Specifically KeyListener and KeyEvent
import java.util.ArrayList;

public class Test implements KeyListener {

    private final int[] cheatCode = {38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 83, 84, 65, 82, 84} //assuming user types out "start"
    private final ArrayList<Integer> KONAMI_CODE = createCheatCode(cheatCode);
    private ArrayList<Integer> typedKeys = new ArrayList<Integer>();
    public Test() {
        //constructor goes here, if necessary
    }

    public /*static*/ ArrayList<Integer> createCheatCode(int[] code) { //uses Key Codes
        ArrayList<Integer> temp = new ArrayList<Integer>();
        for (int i = 0; i < code.length; i++)
            temp.add(new Integer(code[i]));
        return temp;
    }

// Warning: MUST implement ALL KeyListener methods, or compiler will complain

    public /*static*/ void keyPressed(KeyEvent e) {}

    public /*static*/ void keyReleased(KeyEvent e) {
        typedKeys.add(new Integer(e.getKeyCode()));
    }

    public /*static*/ void keyTyped(KeyEvent e) {}

    public /*static*/ boolean cheatEntered() {
        int cheatLen = KONAMI_CODE.size(); // or length, depending on what you use
        int index = typedKeys.size() - cheatLen;
        if (index < 0)
            return false;
        return typedKeys.get(index, typedKeys.size()).equals(KONAMI_CODE);
    }
}

When using a runner method, just specify that

if (test.cheatEntered()) {
    // do something
}

Puedes eliminar el /*static*/ if you want object-oriented programming; otherwise, get rid of the /* y */ pairs if you want to run it using static methods.

Respondido 28 ago 11, 01:08

What do you mean with "Implementation to follow"? - BalusC

@BalusC I meant that I would flesh out my answer. It just takes some time. - Edwin

How would this work? Would a thread/helper be running in the background? Just curious. - James P.

@James In the past, I just did it by attaching the listeners to the object (e.g., the paddles in Pong or buttons in a GUI). My guess is it's a thread, but I don't know enough about shell scripting to be 100% sure. - Edwin

Ah ok, so I assume this in Swing. There should be some way of cumulating the keys entered and resetting if there's a mistake. This could be done in keyTyped. Algo como typedKeys.add(keyChar) with an index comparison check on the KONAMI_CODE constant. If one of characters doesn't correspond then typedKeys.clear() otherwise trigger the cheat or set a flag. It'd be interesting to know if it's possible to enter more than one key at a time in which case some additional checks should be added. - James P.

It might be interesting to look at the state pattern but you might try the trick below since this is a simple case:

  1. Put the sequence to be recognized in a String secretCode
  2. Crear un StringBuilder userInput that will hold the keys pressed by the user
  3. Every time a user presses a key append it to userInput
  4. After each key that is appended in userInput check if the updated userInput contiene el secretCode withthe following: userInput.indexOf(secretCode)>-1

You might want to empty the userInput from now and then based on time or after a sequence is recognized for instance.

Respondido 28 ago 11, 03:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.