Ratonera de Michael Crichton

9

En 1984, Michael Crichton escribió un programa de seguridad en BASIC que fue publicado en la revista Creative Computing. El programa le pedirá al usuario que escriba una frase de su elección, registre los intervalos entre las pulsaciones de teclas y luego le pida que vuelva a escribir la frase. Si los tiempos diferían demasiado, el programa identificaría al usuario como un impostor.

Su tarea: cree una versión del programa de Crichton en el idioma que elija.

Reglas:

  1. Las frases para comunicarse con el usuario ("Escriba la frase clave", "Escriba la frase clave nuevamente", etc.) cuentan como un byte, independientemente de la longitud real. Esto es solo para la comunicación del usuario, no intente ocultar el código del programa en las cadenas.

  2. La prueba de aprobación / reprobación debe basarse en el valor absoluto promedio de las variaciones porcentuales de los intervalos originales. Si las cadenas no coinciden, devuelva el error o permita que el usuario lo intente nuevamente, a su discreción.

  3. La frase clave no debe permitir una cadena nula. En el caso de que la frase clave sea demasiado larga para su tipo de datos de cadena, ya sea truncar o rechazar y comenzar de nuevo, a su discreción.

  4. La sensibilidad de la prueba (el umbral para la prueba de aprobación / reprobación) debe ser ajustable en el código fuente.

  5. Originalmente proporcioné una bonificación del 20% del recuento total de bytes si su código fuente puede formatearse para parecerse a un dinosaurio. Se ha señalado que esto es altamente subjetivo y quizás más apropiado para un concurso de popularidad, por lo que he eliminado este bono. Sin embargo , todavía animo de todo corazón el formateo de dinosaurios, y si formatea su código para que parezca un dinosaurio, puede deducir cualquier comentario puramente cosmético, salto de línea o caracteres de espacio en blanco del total de bytes.

  6. El recuento de bytes más corto gana, sujeto a la longitud de la cadena y los ajustes de formato de dinosaurio.

Tenga en cuenta que mi especificación anterior no coincide exactamente con la operación del código de Crichton, cuyas copias se pueden encontrar en línea. Siga las especificaciones, no intente clonar el original.

Michael Stern
fuente
55
"Es Michael Crichton, así que reste el 20% del total de bytes si su código fuente puede formatearse para parecerse a un dinosaurio". - Umm no. Esta regla es demasiado subjetiva. Por favor eliminar. Aparte de eso, por favor adelante.
John Dvorak
44
@ JanDvorak No creo que sea "demasiado" subjetivo. Es una llamada bastante fácil llamar a un poco de arte ASCII como dino o no
Optimizer
3
@Optimizer No en todos los casos. ¿La letra griega lambda parece un dinosaurio? Estoy bastante seguro de que sí.
John Dvorak
3
Algunos otros comentarios menores: ¿ "Please type the key phrase"Cuenta como 1 byte, o solo la frase cuenta y la frase citada cuenta como 3 bytes ( ", frase, ")? ¿Es intencional que un intervalo mucho más largo y un intervalo mucho más corto se "cancelen" y se vuelvan aún más? ¿El programa tiene que verificar que las dos frases clave coincidan?
Pomo de la puerta
3
Fue en junio de 1984. Puedes ver el programa original aquí.
r3mainer

Respuestas:

9

Rubí, 171 167 157 bytes

require'io/console';t=Time;f=->a{loop{x=t.now;STDIN.getch==?\r?break: a<<t.now-x};a};p"Please type the key phrase";f[r=[]];p"Please type the key phrase again";p r.zip(f[[]]).map{|x,y|(1-x/y).abs}.reduce(:+)/r.size>0.2

Salidas truesi la varianza promedio es superior al 20%, de lo contrario, salidas false.

Intento de arte ASCII de dinosaurio:

(_=/\
  \ \
   \ \
    \ \              _...---..__
     \ \          .∕` #{t=Time} `\._
      \ \      .∕ #{z='io/console'} `\.
       \ \.__.∕  #{require z;s=STDIN} `\.
        \ #{p'1:';f=->a{loop{x=t.now;#   \.
         s.getch==?\r?break: a<<t.now-x;# `\.
          };a};f[r=[]];p'2:';p r.zip(f[[]])#\  
           .map{|x,y|(1-x/y).abs}.reduce(:+)#|
            .fdiv(r.size)>0.2}###########\   \
            `-._    ,___...----...__,   ,__\  \
                |   |_|           |_|   |    \ \
                |___|               |___|      \\/)

Sin golf:

require 'io/console' # so we can read one char at a time

t = Time

f = ->(a) {
  loop {
    x = t.now # record start time
    break if STDIN.getch == ?\r
    a << t.now - x # push (start time - end time) into array
  }
  a
}

puts "Please type the key phrase"
f[r = []] 

puts "Please type the key phrase again"

# interweave timing arrays, compute variances, sum elements
# then divide by array length. Check to see if average
# is greater than threshold (0.2)
p r.zip(f[[]]).map { |x,y| (1-x/y).abs }.reduce(:+) / r.size > 0.2

require 'io/console' podría eliminarse cuando se ejecuta en algunos REPL de Ruby, ya que la biblioteca ya está cargada.

agosto
fuente
4

Java 768 bytes

¿Qué? ¿Java? para el código de golf?

Esto es probablemente lo peor que puedo hacer, pero lo intenté de todos modos.

Muestra cualquier mensaje en la ventana de la consola, pero la escritura real ocurre en JTextField. No es exactamente bonito. Ah, y para guardar 5 bytes, debe cambiar el tamaño del JFrame usted mismo. Además, no verifica la corrección de la cadena la segunda vez. No estoy seguro si eso va en contra de las especificaciones.

Usar:

Escriba su clave en el campo de texto.

No presione enter, vaya a la consola y escriba algo. Mostrará otro mensaje

Escriba lo mismo en el campo de texto (que ahora debería borrarse).

Ve a la consola y presiona algo otra vez. Mostrará si eres un intruso o no.

sin golf:

import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

public class CrichtonsMousetrap {
    public static void main(String[]a){
        new CrichtonsMousetrap();
    }
    long start;
    List<Long>elapsed = new ArrayList<>();
    List<Long>e2;
    public CrichtonsMousetrap(){
        JFrame f = new JFrame();
        f.setSize(199,70);
        f.setVisible(true);
        JTextField t = new JTextField();
        System.out.println("please type in the key phrase.");
        f.add(t);
        t.getDocument().addDocumentListener(new DocumentListener(){
            @Override
            public void changedUpdate(DocumentEvent e) {}
            @Override
            public void insertUpdate(DocumentEvent e) {
                long r = System.nanoTime();
                if(start!=0){elapsed.add(r-start);}
                start=r;}
            @Override
            public void removeUpdate(DocumentEvent e) {}            
        });
        Scanner s = new Scanner(System.in);
        s.next();
        System.out.println("please type that again!");
        e2=elapsed;
        elapsed=new ArrayList<>();
        start=0;
        t.setText("");
        s.next();
        double sum=0;
        for(int i=0;i<e2.size();i++){
            sum+=Math.abs(1-elapsed.get(i)/(double)e2.get(i));
        }
        System.out.println("your average percent error was " + sum/e2.size());
        double okLimit = .2;
        System.out.println(sum/e2.size() < okLimit ? "you're ok":"INTRUDER!");
    }
}

golfizado:

import java.util.*;import javax.swing.*;import javax.swing.event.*;class q{static long p;static List<Long>y=new ArrayList<>(),o;public static void main(String[]a){JFrame f=new JFrame();f.setSize(0,0);f.setVisible(true);JTextField t=new JTextField();System.out.println("please type in the key phrase.");f.add(t);t.getDocument().addDocumentListener(new DocumentListener(){public void changedUpdate(DocumentEvent e){}public void insertUpdate(DocumentEvent e){long r=System.nanoTime();if(p!=0){y.add(r-p);}p=r;}public void removeUpdate(DocumentEvent e){}});Scanner s = new Scanner(System.in);s.next();System.out.println("please type that again!");o=y;y=new ArrayList<>();p=0;t.setText("");s.next();double b=0;for(int i=0;i<o.size();b+=Math.abs(1-y.get(i)/(double)o.get(i++)));System.out.print(b/o.size() < .25 ? "you're ok":"INTRUDER!");}}
Estiramiento Maniaco
fuente
No hay forma de configurar TTY en modo sin formato desde Java (a menos que esté listo para usar JNI). Así que entiendo por qué necesitas un JFrame. Pero realmente, este es el programa menos fácil de usar que he visto en mucho tiempo :-) No estoy seguro de si quiero votar a favor o en contra esta respuesta.
coredump
Voto por la gran cantidad de hostilidad del usuario (¿es eso una palabra?). Básicamente es arte.
Ingo Bürk
Creo que esto se podría jugar más al hacer que la clase se extienda JFrame, por lo que no sería necesario f.
PurkkaKoodari
3

HTML, JavaScript (ES6), 328

El recuento total de bytes del código es de 402 bytes y los mensajes para interactuar con el usuario:

"Valid User"
"Imposter alert!!"
"Please Enter the Key again"
Please Enter the Key

son 78 bytes en total, por lo que la puntuación total => 402-78 + 4 = 328

Ejecute el fragmento a continuación en un último Firefox y escriba la clave en el cuadro de entrada seguido de la tecla Intro.

El código verifica que tanto las teclas ingresadas como las reingresadas sean iguales (solicita que se vuelva a ingresar de nuevo si no), calcula el porcentaje de diferencia absoluta promedio y verifica si es menor que el valor de la variable V

<a id=t >Please Enter the Key</a><input id=f /><script>V=.3,a=[],i=0,s=b="",q=0
c=_=>(j=0,_.slice(1).map(v=>j+=Math.abs(v)/i),alert(j<V?"Valid User":"Imposter alert!!"))
r=_=>(a=[],i=0,t.textContent="Please Enter the Key again",f.value="")
f.onkeyup=_=>_.keyCode==13?q++?s==f.value?(A=a,B=b,A=a.map((v,i)=>v-A[i-1]),c(b.map((v,i)=>(v-B[i-1]-A[i])/A[i]))):r():r(b=a,s=f.value):a[i++]=Date.now()</script>

Optimizador
fuente
3

C, 154 (86 + 68 para banderas)

d[99],i,a,b;main(x,y){P"Please type the key phrase"W(E-13)U,x=y;U;P"Please type 
the key phrase again"W(a<i)E,b+=abs(Y-Z)*99/Z,++a,x=y;b<a*9||P"No cake for imposters");}

Compilar con -DY=(y=clock())-x, -DZ=a[d], -DE=getch(), -DW=);while, -DU=i++[d]=Yy -DP=puts(. Las nuevas líneas se agregan para fines de presentación y pueden eliminarse (el recuento de bytes dado es sin).

Ungolfed + comentarios:

d[99],i,a,b;
main(x,y,z){
    puts("Please type the key phrase");
    do
        z = getch(),
        i++[d] = (y = clock()) - x, // save amount of time from last key. first value is garbage.
        x = y;
    while((z = getch())-13); // read until carriage return. 
    for(;a < i && getch(); ++a) // don't check for validity, just get a char
        b += abs((y = clock())- x - d[a])*99/d[a], // (y=clock())-x is time from last key.
                                                     // subtract from original time, *99, divide by new
                                                     // then get sum of these
        x = y;
    b < i*9  // check that the average difference is less than 9/99
    || puts("No cake for imposters"); // identify as imposter if greater/equal
    // don't output anything if not an imposter
}

Esto no verifica que la frase reescrita sea idéntica, ni genera nada si el usuario no se identifica como un impostor.

Esto tampoco considera el tiempo que se tarda después de preguntar antes de la primera pulsación de tecla.

es1024
fuente
No debería getchapostar getco getchar? Tengo una referencia indefinida a 'getch', que si recuerdo correctamente está en desuso.
coredump
También tuve "file.c: 1: 1: advertencia: la definición de datos no tiene tipo ni clase de almacenamiento" (gcc). Agregué charantes de las declaraciones globales, y ahora, esto da un error de segmentación en tiempo de ejecución. ¿Puedes dar detalles sobre cómo construirlo? Qué compilador estas usando? Gracias.
coredump
@coredump Las advertencias son inofensivas; sin embargo, si desea eliminar las advertencias, debe escribirlas inte inicializarlas 0. He probado esto usando gcc en Windows (usando Windows getch). getchse usa en lugar de getco getcharporque getchno requiere que se presione la tecla de retorno antes de procesar cualquier carácter (de getchhecho, está en desuso en Windows, aunque no hay nada de malo en usar funciones en desuso aquí).
es1024
Estoy probando en Linux y recurrí a stackoverflow.com/questions/7469139/… para que funcione. Gracias.
coredump
2

Scala REPL 233

def l:Stream[(Int,Long)]=(Console.in.read,System.nanoTime)#::l    
def m={
    println("Enter");     
    l.takeWhile(_._1!=13).map(_._2).toList.sliding(2).map(a=>a(1)-a(0))
}
val k=m.zip(m)     
k.map(a=>Math.abs(a._2-a._1)/(a._1.toDouble*k.length)).sum<0.2

Con todo el espacio eliminado, tiene:

def l:Stream[(Int,Long)]=(Console.in.read,System.nanoTime)#::l;def m={println("Enter");l.takeWhile(_._1!=13).map(_._2).toList.sliding(2).map(a=>a(1)-a(0))};val k=m.zip(m);k.map(a=>Math.abs(a._2-a._1)/(a._1.toDouble*k.length)).sum<0.2

¡Lo cual estoy seguro de que alguien más talentoso que yo podría hacer un dinosaurio!

Breve explicacion:

El lmétodo lee los caracteres y mantiene el trazo nanoTimede cuándo se escribió cada carácter.

El mmétodo imprime "Enter", interrumpe el lmétodo al presionar enter (carácter 13), luego lo asigna solo a nanoTimes, y luego obtiene los intervalos de tiempo entre cada carácter.

Las siguientes 2 líneas se leen en 2 cadenas, las comprime, luego encuentra el valor absoluto promedio de la diferencia porcentual entre el segundo intervalo y el primero, y finalmente imprime si este promedio fue menor o no 0.2.

Ben Reich
fuente
1

Lisp común: 660

(ql:quickload'(cl-charms alexandria))(defun m(&key(ok 0.2))(labels((^(s)(fresh-line)(princ s)(return-from m))(d(a b)(abs(/ (- b a) b)))($(x)(princ x)(force-output))(?(m)(charms:with-curses()($ m)(clear-input)(charms:enable-raw-input)(loop for c = (read-char)for n = (get-internal-real-time)for x = nil then (/(- n b)internal-time-units-per-second)for b = n when (eql c #\Esc)do (^"QUIT")when x collect x into % until (eql c #\Newline) collect c into ! finally(progn(terpri)(return(cons(coerce !'string)%)))))))(let*((ip(?"INIT PASSWORD: "))(ps(car ip))(sp(if(equal""ps)(^"NO EMPTY PASSWORD ALLOWED")(?"ENTER PASSWORD: ")))(r(if(equal ps(car sp))(alexandria:mean(mapcar #'d(cdr sp)(cdr ip)))(^"YOU DIDN'T SAY THE MAGIC WORD!"))))(if(> r ok)($"YOU ARE A FAKE!")($"IDENTITY CONFIRMED")))))(m)

Sin golf

(ql:quickload'(cl-charms alexandria))
(defun m(&key(ok 0.2))
  (labels
      ((^(s)(fresh-line)(princ s)(return-from m))
       (d(a b)(abs(/ (- b a) b)))
       ($(x)(princ x)(force-output))
       (?(m)(charms:with-curses()
              (clear-input)
              ($ m)
              (charms:enable-raw-input)
              (loop for c = (read-char)
                    for n = (get-internal-real-time)
                    for x = nil then (/ (- n b)
                                        internal-time-units-per-second)
                    for b = n
                    when (eql c #\Esc)
                      do (^"QUIT")
                    when x
                      collect x into %
                    until (eql c #\Newline)
                    collect c into !
                    finally (progn
                              (terpri)
                              (return
                                (cons (coerce !'string) %)))))))
    (let* ((ip (?"INIT PASSWORD: "))
           (ps (car ip))
           (sp (if (equal "" ps)
                 (^"NO EMPTY PASSWORD ALLOWED")
                 (?"ENTER PASSWORD: ")))
           (r (if (equal ps (car sp))
                (alexandria:mean(mapcar #'d(cdr sp)(cdr ip)))
                (^"YOU DIDN'T SAY THE MAGIC WORD!"))))
      (if (> r ok)
        ($"YOU ARE A FAKE!")
        ($"IDENTITY CONFIRMED")))))

(m) ;; call function

Observaciones adicionales

  • Cumple con todas las reglas
  • Cuando el usuario da por primera vez una contraseña vacía, el programa aborta limpiamente
  • Al escribir Escape, el programa aborta limpiamente.
  • Probado en implementaciones recientes de SBCL y CCL
  • Requiere cl-charms, que es un envoltorio alrededor de Ncurses. Esta es la forma más fácil de capturar datos sin procesar.
  • Esto está inspirado (pero no copiado) de la versión original encontrada por squeamish-ossifrage

Bonus de dinosaurio

Debería tener una bonificación porque todo el mundo sabe que " Common Lisp es un dinosaurio moribundo ".

volcado de memoria
fuente
¿Se puede cambiar al bloque de código en lugar del bloque de comillas? (para su código)
Optimizer
@Optimizer hecho
coredump