Trappola per topi di Michael Crichton


9

Nel 1984, Michael Crichton scrisse un programma di sicurezza in BASIC che fu pubblicato sulla rivista Creative Computing. Il programma chiederebbe all'utente di digitare una frase di sua scelta, registrare gli intervalli tra i tasti, quindi chiederle di digitare nuovamente la frase. Se i tempi differissero eccessivamente, il programma identificherebbe l'utente come impostore.

Il tuo compito: crea una versione del programma di Crichton nella lingua che preferisci.

Regole:

  1. Le frasi per comunicare con l'utente ("Digitare la frase chiave", "Digitare nuovamente la frase chiave", ecc.) Contano come un byte ciascuna indipendentemente dalla lunghezza effettiva. Questo è solo per la comunicazione dell'utente, non tentare di nascondere il codice del programma nelle stringhe.

  2. Il test di superamento / superamento dovrebbe basarsi sul valore assoluto medio delle variazioni percentuali rispetto agli intervalli originali. Se le stringhe non corrispondono, restituire non riesce o consentire all'utente di riprovare, a propria discrezione.

  3. La frase chiave non deve consentire una stringa nulla. Nel caso in cui la frase chiave sia troppo lunga per il tipo di dati della stringa, troncare o non consentire e ricominciare, a propria discrezione.

  4. La sensibilità del test (la soglia per il test pass / fail) dovrebbe essere regolabile nel codice sorgente.

  5. Inizialmente ho fornito un bonus del 20% del numero totale di byte se il tuo codice sorgente può essere formattato in modo da assomigliare in modo riconoscibile a un dinosauro. È stato sottolineato che questo è altamente soggettivo e forse più appropriato per un concorso di popolarità, quindi ho rimosso questo bonus. però incoraggio ancora di cuore la formattazione dei dinosauri e, se formatti il ​​tuo codice per sembrare un dinosauro, puoi dedurre qualsiasi commento puramente cosmetico, interruzione di riga o caratteri di spazi bianchi dal totale dei byte.

  6. Vince il conteggio dei byte più corto, soggetto a regolazioni della lunghezza della stringa e della formattazione dei dinosauri.

Nota che le mie specifiche di cui sopra non corrispondono esattamente al funzionamento del codice di Crichton, le cui copie possono essere trovate online. Segui le specifiche, non provare a clonare l'originale.


5
"È Michael Crichton, quindi sottrai il 20% dal conteggio totale dei byte se il tuo codice sorgente può essere formattato per assomigliare in modo riconoscibile a un dinosauro." - umm ... no. Questa regola è troppo soggettiva. Si prega di rimuovere. A parte questo, per favore vai avanti.
John Dvorak,

4
@JanDvorak Non penso che sia "troppo" soggettivo. È una chiamata abbastanza semplice chiamare arte ASCII come dino o no
Optimizer,

3
@Optimizer Non in tutti i casi. La lettera greca lambda sembra un dinosauro? Sono abbastanza sicuro che lo faccia.
John Dvorak,

3
Alcuni altri commenti minori: "Please type the key phrase"conta come 1 byte o conta solo la frase e la frase citata conta come 3 byte ( ", frase, ")? È intenzionale che un intervallo molto più lungo e un intervallo molto più breve si "annullino" e diventino ancora una volta? Il programma deve verificare che le due frasi chiave corrispondano?
Maniglia della porta

Risposte:


9

Rubino, 171 167 157 byte

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

Emette truese la varianza media è superiore al 20%, altrimenti genera false.

Dinosaur ASCII art tent:

(_=/\
  \ \
   \ \
    \ \              _...---..__
     \ \          .∕` #{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}###########\   \
            `-._    ,___...----...__,   ,__\  \
                |   |_|           |_|   |    \ \
                |___|               |___|      \\/)

Ungolfed:

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' potrebbe essere rimosso quando eseguito in alcuni REPL di Ruby, poiché la libreria è già caricata.


4

Java 768 byte

che cosa? Giava? per il codice golf?

Questa è probabilmente la cosa peggiore da fare, ma l'ho provato comunque.

Visualizza tutti i messaggi nella finestra della console, ma la digitazione effettiva si verifica in JTextField. Non proprio bello. Oh, e per salvare 5 byte, devi ridimensionare JFrame da solo. Inoltre, non verifica la correttezza della stringa la seconda volta. Non sono sicuro se questo è contro le specifiche.

Usare:

Digita la chiave nel campo di testo.

Non premere Invio, vai alla console e digita qualcosa. Verrà visualizzato un altro messaggio

Digita la stessa cosa nel campo di testo (che ora dovrebbe essere cancellato).

Vai alla console e premi di nuovo qualcosa. Mostrerà se sei un intruso o no.

ungolfed:

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

golfed:

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

Non è possibile impostare TTY in modalità raw da Java (a meno che non si sia pronti a utilizzare JNI). Quindi capisco perché hai bisogno di un JFrame. Ma davvero, questo è il programma meno intuitivo che abbia mai visto in età :-) Non sono sicuro di voler votare o sottovalutare questa risposta.
coredump,

Ho votato per l'enorme quantità di ostilità degli utenti (è anche una parola?). È fondamentalmente arte.
Ingo Bürk,

Credo che questo potrebbe essere migliorato di più estendendo la classe JFrame, quindi non ne avresti bisogno f.
PurkkaKoodari,

3

HTML, JavaScript (ES6), 328

Il conteggio totale dei byte del codice è 402 byte e i messaggi per interagire con l'utente:

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

sono 78 byte totali, quindi il punteggio totale => 402 - 78 + 4 = 328

Esegui lo snippet di seguito in un ultimo Firefox e digita il tasto nella casella di input seguito dal tasto Invio.

Il codice verifica che le chiavi immesse e reinserite siano uguali (richiede di reinserirla in caso contrario), calcola la percentuale di differenza assoluta media e verifica se è inferiore al valore della variabile 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>


3

C, 154 (86 + 68 per le bandiere)

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

Compilare con -DY=(y=clock())-x, -DZ=a[d], -DE=getch(), -DW=);while, -DU=i++[d]=Ye-DP=puts( . Newline aggiunti a scopo di presentazione e possono essere rimossi (il conteggio dei byte indicato è senza).

Commenti + Ungolfed:

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
}

Ciò non verifica che la frase reinserita sia identica, né genera alcun risultato se l'utente non viene identificato come impostore.

Anche questo non considera il tempo impiegato dopo la richiesta prima della prima sequenza di tasti.


Non dovresti getchscommettere getco getchar? Ho un riferimento indefinito a "getch", che se ricordo bene è deprecato?
coredump,

Ho anche avuto "file.c: 1: 1: attenzione: la definizione dei dati non ha tipo o classe di archiviazione" (gcc). Ho aggiunto charprima delle dichiarazioni globali e ora questo dà un errore di segmentazione in fase di esecuzione. Puoi fornire dettagli su come costruirlo? Che compilatore stai usando? Grazie.
coredump,

@coredump Gli avvisi sono innocui; tuttavia, se si desidera rimuovere gli avvisi, è necessario digitare inte inizializzare 0. Ho provato questo usando gcc su Windows (usando Windows getch). getchviene utilizzato al posto di getco getcharperché getchnon richiede che il tasto invio venga premuto prima di elaborare i caratteri ( getchè in effetti deprecato su Windows, anche se qui non c'è nulla di sbagliato nell'usare funzioni deprecate).
es1024

Sto testando su Linux e ho fatto ricorso a stackoverflow.com/questions/7469139/… per farlo funzionare. Grazie.
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 tutta la spaziatura rimossa, hai:

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

Che sono sicuro che qualcuno più talentuoso di me potrebbe diventare un dinosauro!

Breve spiegazione:

Il lmetodo legge i caratteri e mantiene il tratto nanoTimedi quando ogni carattere è stato digitato.

Il mmetodo stampa "Enter", interrompe il lmetodo premendo il tasto Invio (carattere 13), quindi lo associa solo a nanoTimes, quindi ottiene gli intervalli di tempo tra ciascun carattere.

Le successive 2 righe leggono in 2 stringhe, le zippano, quindi trovano il valore assoluto medio della differenza percentuale tra il secondo intervallo e il primo e infine stampa se questa media è stata o meno inferiore 0.2.


1

Lisp comune: 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)

Ungolfed

(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

Osservazioni aggiuntive

  • Conforme a tutte le regole
  • Quando l'utente fornisce per la prima volta una password vuota, il programma si interrompe in modo pulito
  • Durante la digitazione Escape, il programma si interrompe in modo pulito.
  • Testato su recenti implementazioni SBCL e CCL
  • Richiede cl-charms, che è un wrapper per Ncurses. Questo è il modo più semplice per acquisire input non elaborati.
  • Questo è ispirato (ma non copiato) dalla versione originale trovata da ossido di schifo

Bonus dinosauro

Dovrei avere un bonus perché tutti sanno che " Common Lisp è un dinosauro moribondo ".


Puoi passare al blocco di codice anziché al blocco di virgolette? (per il tuo codice)
Ottimizzatore

@Optimizer fatto
coredump
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.