Pułapka na myszy Michaela Crichtona

9

W 1984 r. Michael Crichton napisał program bezpieczeństwa w języku BASIC, który został opublikowany w magazynie Creative Computing. Program poprosi użytkownika o wpisanie wybranej frazy, zarejestruje interwały między naciśnięciami klawiszy, a następnie poprosi ją o ponowne wpisanie frazy. Jeśli czasy różnią się zbytnio, program zidentyfikuje użytkownika jako oszusta.

Twoje zadanie: stwórz wersję programu Crichtona w wybranym języku.

Zasady:

  1. Zwroty do komunikacji z użytkownikiem („Wpisz frazę kluczową”, „Wpisz ponownie frazę kluczową” itp.) Liczą się jako jeden bajt bez względu na rzeczywistą długość. Służy to wyłącznie komunikacji użytkownika, nie próbuj ukrywać kodu programu w ciągach znaków.

  2. Test pozytywny / negatywny powinien opierać się na średniej wartości bezwzględnej procentowych odchyleń od pierwotnych przedziałów. Jeśli ciągi nie pasują, albo zwróć błąd, albo pozwól użytkownikowi spróbować ponownie, według własnego uznania.

  3. Fraza kluczowa nie powinna dopuszczać łańcucha zerowego. W przypadku, gdy fraza kluczowa jest za długa dla typu ciągu znaków, możesz ją obciąć lub zabronić i zacząć od nowa, według własnego uznania.

  4. Czułość testu (próg testu pozytywnego / negatywnego) powinna być regulowana w kodzie źródłowym.

  5. Pierwotnie zapewniłem premię w wysokości 20% całkowitej liczby bajtów, jeśli kod źródłowy można sformatować tak, aby przypominał dinozaura. Zwrócono uwagę, że jest to bardzo subiektywne i być może bardziej odpowiednie dla konkursu popularności, więc usunąłem ten bonus. Jednak nadal serdecznie zachęcam do formatowania dinozaurów, a jeśli sformatujesz swój kod, aby wyglądał jak dinozaur, możesz odjąć wszelkie czysto kosmetyczne komentarze, znaki podziału linii lub białe znaki od całkowitej liczby bajtów.

  6. Wygrywa najkrótsza liczba bajtów, z zastrzeżeniem dostosowania długości łańcucha i formatowania dinozaura.

Zauważ, że moja powyższa specyfikacja nie do końca pasuje do działania kodu Crichtona, którego kopie można znaleźć w Internecie. Postępuj zgodnie ze specyfikacją, nie próbuj klonować oryginału.

Michael Stern
źródło
5
„To Michael Crichton, więc odejmij 20% od całkowitej liczby bajtów, jeśli twój kod źródłowy można sformatować tak, by przypominał dinozaura”. - Umm nie. Ta zasada jest zbyt subiektywna. Proszę usuń. Poza tym, proszę śmiało.
John Dvorak,
4
@JanDvorak Nie sądzę, że jest to „zbyt” subiektywne. To dość łatwe wezwanie, aby nazwać sztukę ASCII jako dino lub nie
Optimizer
3
@Optimizer Nie we wszystkich przypadkach. Czy grecka litera lambda wygląda jak dinozaur? Jestem prawie pewien, że tak.
John Dvorak,
3
Kilka innych drobnych komentarzy: Czy "Please type the key phrase"liczy się jako 1 bajt, czy tylko fraza się liczy, a cytowana fraza liczy się jako 3 bajty ( ", fraza ")? Czy jest celowe, że znacznie dłuższy i znacznie krótszy interwał „skasuje się” i zrówna się? Czy program musi sprawdzić, czy oba kluczowe frazy są zgodne?
Klamka
3
To było w czerwcu 1984 roku. Tutaj możesz zobaczyć oryginalny program.
r3mainer

Odpowiedzi:

9

Rubin, 171 167 157 bajtów

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

Dane truewyjściowe, jeśli średnia wariancja przekracza 20%, w przeciwnym razie dane wyjściowe false.

Próba plastyczna dinozaura ASCII:

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

Nie golfowany:

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' może zostać usunięty po uruchomieniu w niektórych Ruby REPL, ponieważ biblioteka jest już załadowana.

sierpień
źródło
4

Java 768 bajtów

co? Jawa? do golfa kodowego?

Jest to prawdopodobnie najgorsza rzecz do zrobienia, ale i tak spróbowałem.

Wyświetla wszelkie komunikaty w oknie konsoli, ale rzeczywiste pisanie występuje w polu JTextField. Niezupełnie ładnie wyglądające. Aha, aby zaoszczędzić 5 bajtów, musisz sam zmienić rozmiar JFrame. Ponadto nie sprawdza poprawności łańcucha za drugim razem. Nie jestem pewien, czy jest to niezgodne ze specyfikacjami.

Używać:

Wpisz swój klucz w polu tekstowym.

Nie naciskaj Enter, przejdź do konsoli i wpisz coś. Wyświetli inną wiadomość

Wpisz to samo w polu tekstowym (które powinno być teraz wyczyszczone).

Idź do konsoli i naciśnij coś jeszcze raz. Wyświetli się informacja, czy jesteś intruzem, czy nie.

bez golfa:

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

grał w golfa:

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!");}}
Stretch Maniac
źródło
Nie ma możliwości ustawienia TTY w trybie surowym z Java (chyba że jesteś gotowy do używania JNI). Rozumiem więc, dlaczego potrzebujesz JFrame. Ale tak naprawdę jest to najmniej przyjazny dla użytkownika program, jaki widziałem od wieków :-) Nie jestem pewien, czy chcę głosować za lub głosować tę odpowiedź.
coredump
Głosuję za samą nieprzyjaznością użytkownika (czy to nawet słowo?). To w zasadzie sztuka.
Ingo Bürk
Wierzę, że można to pograć w golfa JFrame, przedłużając klasę , abyś nie potrzebował f.
PurkkaKoodari
3

HTML, JavaScript (ES6), 328

Całkowita liczba bajtów kodu wynosi 402 bajty, a komunikaty do interakcji z użytkownikiem:

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

są łącznie 78 bajtów, więc łączny wynik => 402 - 78 + 4 = 328

Uruchom poniższy fragment w najnowszym Firefoksie i wpisz klucz w polu wprowadzania, a następnie klawisz Enter.

Kod sprawdza, czy oba wpisane i ponownie wprowadzone klucze są takie same (monituje o ponowne wprowadzenie, jeśli nie), oblicza średni procent różnicy bezwzględnej i sprawdza, czy jest on mniejszy niż wartość zmiennej 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>

Optymalizator
źródło
3

C, 154 (86 + 68 dla flag)

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

Skompilować -DY=(y=clock())-x, -DZ=a[d], -DE=getch(), -DW=);while, -DU=i++[d]=Yi -DP=puts(. Nowe linie dodane w celach prezentacji i mogą zostać usunięte (podana liczba bajtów nie występuje).

Niegolfowane + komentarze:

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
}

Nie sprawdza to, czy wpisana fraza jest identyczna, ani nie wyświetla niczego, jeśli użytkownik nie zostanie zidentyfikowany jako oszust.

Nie uwzględnia to również czasu potrzebnego po wyświetleniu monitu przed pierwszym naciśnięciem klawisza.

es1024
źródło
Nie powinieneś getchstawiać getclub getchar? Mam niezdefiniowane odniesienie do `getch ', które, jeśli dobrze pamiętam, jest przestarzałe?
coredump
Miałem też „file.c: 1: 1: ostrzeżenie: definicja danych nie ma typu ani klasy pamięci” (gcc). Dodałem charprzed deklaracjami globalnymi, a teraz daje to błąd segmentacji w czasie wykonywania. Czy możesz podać szczegóły, jak to zbudować? Z jakiego kompilatora korzystasz? Dzięki.
coredump
@coredump Ostrzeżenia są nieszkodliwe; chociaż jeśli chcesz usunąć ostrzeżenia, należy wpisać inti zainicjować 0. Przetestowałem to za pomocą gcc w systemie Windows (za pomocą systemu Windows getch). getchjest używany zamiast getclub getcharponieważ getchnie wymaga naciśnięcia klawisza Return przed przetworzeniem jakichkolwiek znaków ( getchjest rzeczywiście przestarzały w Windows, chociaż nie ma nic złego w używaniu przestarzałych funkcji tutaj).
es1024,
Testuję na Linuksie i uciekłem się do stackoverflow.com/questions/7469139/… , aby działało. Dzięki.
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

Po usunięciu wszystkich odstępów masz:

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

Który jestem pewien, że ktoś bardziej utalentowany niż ja mógłby zrobić z dinozaura!

Krótkie wyjaśnienie:

lSposób odczytać znaki i utrzymuje oddechowych nanoTime, kiedy każdy znak został wpisany.

Te modciski metoda "Enter", łamie lmetodę momencie uderzenia wejść (13 znaków), a następnie odwzorowuje to, aby po prostu nanoTimes, a następnie dostaje odstępów czasowych pomiędzy każdym znaku.

Następne 2 wiersze czyta 2 ciągi, zamyka je, a następnie znajduje średnią wartość bezwzględną różnicy procentowej między drugim interwałem a pierwszym, i ostatecznie drukuje, czy ta średnia była mniejsza niż 0.2.

Ben Reich
źródło
1

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

Nie golfił

(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

Dodatkowe uwagi

  • Zgodny ze wszystkimi zasadami
  • Gdy użytkownik po raz pierwszy podaje puste hasło, program przerywa się
  • Podczas pisania Escapeprogram przerywa się.
  • Testowany na najnowszych implementacjach SBCL i CCL
  • Wymaga zaklęć cl, które otaczają Ncursesa. Jest to najprostszy sposób na przechwycenie surowych danych wejściowych.
  • Jest to inspirowane (ale nie skopiowane z) oryginalną wersją znalezioną przez squeamish-ossifrage

Bonus dinozaura

Powinienem mieć bonus, bo wszyscy wiedzą, że „ Common Lisp to konający dinozaur ”.

rdzeń rdzeniowy
źródło
Czy możesz przejść na blok kodu zamiast bloku cytowania? (dla Twojego kodu)
Optymalizator
@Optimizer zrobić
Coredump