Ratoeira de Michael Crichton

9

Em 1984, Michael Crichton escreveu um programa de segurança no BASIC, publicado na revista Creative Computing. O programa solicita que o usuário digite uma frase de sua escolha, registre os intervalos entre as teclas e, em seguida, peça para redigitar a frase. Se os horários diferissem demais, o programa identificaria o usuário como um impostor.

Sua tarefa: crie uma versão do programa de Crichton no idioma de sua escolha.

Regras:

  1. As frases para comunicação com o usuário ("Digite a frase-chave", "Digite a frase-chave novamente" etc.) contam como um byte cada, independentemente do tamanho real. Isso é apenas para comunicação do usuário, não tente ocultar o código do programa nas strings.

  2. O teste de aprovação / reprovação deve ser baseado no valor absoluto médio das variações percentuais dos intervalos originais. Se as seqüências não corresponderem, retorne a falha ou permita que o usuário tente novamente, a seu critério.

  3. A frase-chave não deve permitir uma cadeia nula. No caso de a frase-chave ser muito longa para o seu tipo de dados de string, truncar ou não permitir e reiniciar, a seu critério.

  4. A sensibilidade do teste (o limite para o teste de aprovação / reprovação) deve ser ajustável no código fonte.

  5. Originalmente, forneci um bônus de 20% da contagem total de bytes, se o código-fonte puder ser formatado para se parecer com um dinossauro. Foi apontado que isso é altamente subjetivo e talvez mais apropriado para um concurso de popularidade; portanto, removi esse bônus. No entanto , ainda encorajo com entusiasmo a formatação de dinossauros, e se você formatar seu código para se parecer com um dinossauro, poderá deduzir quaisquer comentários puramente cosméticos, quebra de linha ou caracteres de espaço em branco do total de bytes.

  6. Ganhos mais curtos de contagem de bytes, sujeitos a ajustes de formatação de comprimento de string e dinossauros.

Observe que minha especificação acima não corresponde exatamente à operação do código de Crichton, cujas cópias podem ser encontradas online. Siga as especificações, não tente clonar o original.

Michael Stern
fonte
5
"É Michael Crichton, então subtraia 20% da contagem total de bytes se o seu código-fonte puder ser formatado para se parecer com um dinossauro". - Umm não. Esta regra é muito subjetiva. Por favor remova. Fora isso, por favor, vá em frente.
John Dvorak
4
@JanDvorak Eu não acho que seja "muito" subjetivo. É um apelo bastante fácil de chamar um pouco de arte ASCII como dino ou não
Optimizer
3
@Optimizer Nem sempre. A letra grega lambda parece um dinossauro? Tenho certeza que sim.
John Dvorak
3
Alguns outros comentários menores: "Please type the key phrase"conta como 1 byte ou apenas a frase conta e a frase entre aspas conta como 3 bytes ( ", frase ")? É intencional que um intervalo muito mais longo e um intervalo muito mais curto "cancelem" e se tornem iguais novamente? O programa precisa verificar se as duas frases-chave correspondem?
Maçaneta
3
Foi em junho de 1984. Você pode ver o programa original aqui.
R3mainer

Respostas:

9

Rubi, 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

Resultados truese a variação média estiver acima de 20%, caso contrário, resultados false.

Tentativa de arte ASCII de dinossauro:

(_=/\
  \ \
   \ \
    \ \              _...---..__
     \ \          .∕` #{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' pode ser removido quando executado em alguns REPLs do Ruby, pois a biblioteca já está carregada.

agosto
fonte
4

Java 768 bytes

que? Java? para o código de golfe?

Esta é provavelmente a pior coisa a fazer, mas tentei mesmo assim.

Ele exibe todas as mensagens na janela do console, mas a digitação real ocorre no JTextField. Não é exatamente bonito. Ah, e para salvar 5 bytes, você deve redimensionar o JFrame. Além disso, ele não verifica a exatidão das seqüências de caracteres pela segunda vez. Não tenho certeza se isso é contra as especificações.

Usar:

Digite sua chave no campo de texto.

Não pressione enter, vá para o console e digite algo. Ele exibirá outra mensagem

Digite a mesma coisa no campo de texto (que agora deve ser limpo).

Vá para o console e pressione algo novamente. Ele exibirá se você é um intruso ou não.

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!");}}
Stretch Maniac
fonte
Não há como definir o TTY no modo bruto a partir do java (a menos que você esteja pronto para usar o JNI). Então eu entendo por que você precisa de um JFrame. Mas, na verdade, este é o programa menos amigável que eu já vi em idades :-) Não tenho certeza se quero votar ou diminuir essa resposta.
Coredump
Voto pela quantidade total de hostilidade do usuário (isso é mesmo uma palavra?). É basicamente arte.
Ingo Bürk
Acredito que isso poderia ser mais praticado se a classe JFramefosse estendida , para que você não precisasse f.
PurkkaKoodari
3

HTML, JavaScript (ES6), 328

A contagem total de bytes do código é 402 bytes e as mensagens para interagir com o usuário:

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

são 78 bytes totais, então a pontuação total => 402 - 78 + 4 = 328

Execute o trecho abaixo em um Firefox mais recente e digite a chave na caixa de entrada seguida pela tecla Enter.

O código verifica se as chaves inseridas e reinseridas são iguais (solicita a reinserção, se não for o caso), calcula a porcentagem média da diferença absoluta e verifica se é menor que o valor da variável 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>

Optimizer
fonte
3

C, 154 (86 + 68 para bandeiras)

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 com -DY=(y=clock())-x, -DZ=a[d], -DE=getch(), -DW=);while, -DU=i++[d]=Ye -DP=puts(. Novas linhas adicionadas para fins de apresentação e podem ser removidas (a contagem de bytes fornecida é sem).

Ungolfed + comentários:

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
}

Isso não verifica se a frase redigitada é idêntica, nem produz nada se o usuário não for identificado como um impostor.

Isso também não considera o tempo gasto após o prompt antes do primeiro pressionamento de tecla.

es1024
fonte
Não deveria getchapostar getcou getchar? Eu tenho uma referência indefinida ao `getch ', que, se bem me lembro, está obsoleta?
Coredump #
Eu também tive "file.c: 1: 1: aviso: a definição de dados não tem tipo ou classe de armazenamento" (gcc). Eu adicionei charantes das declarações globais e, agora, isso causa uma falha de segmentação no tempo de execução. Você pode dar detalhes sobre como construí-lo? Qual compilador você está usando? Obrigado.
Coredump #
@coredump Os avisos são inofensivos; embora se você deseja remover os avisos, ele deve ser do tipo inte inicializado para 0. Eu testei isso usando o gcc no Windows (usando o Windows getch). getché usado em vez de getcou getcharporque getchnão exige que a tecla de retorno seja pressionada antes de processar qualquer caractere (na getchverdade, foi preterido no Windows, embora não haja nada de errado em usar funções preteridas aqui).
es1024
Estou testando no Linux e recorri ao stackoverflow.com/questions/7469139/… para fazê-lo funcionar. Obrigado.
Coredump13
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

Com todo o espaçamento removido, você tem:

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

Que eu tenho certeza que alguém mais talentoso do que eu posso transformar em dinossauro!

Breve explicação:

O lmétodo lê caracteres e mantém o controle nanoTimede quando cada caractere foi digitado.

O mmétodo imprime "Enter", quebra o lmétodo ao pressionar enter (caractere 13), mapeia-o apenas para o nanoTimese obtém os intervalos de tempo entre cada caractere.

As próximas 2 linhas lêem em 2 strings, as compactam e depois encontram o valor absoluto médio da diferença percentual entre o segundo intervalo e o primeiro e, finalmente, imprimem se essa média foi ou não menor que 0.2 .

Ben Reich
fonte
1

Lisp comum: 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

Observações adicionais

  • Cumpre todas as regras
  • Quando o usuário primeiro fornece uma senha vazia, o programa aborta de forma limpa
  • Ao digitar Escape, o programa é interrompido de maneira limpa.
  • Testado em implementações recentes de SBCL e CCL
  • Requer cl-charms, que é um invólucro em torno de Ncurses. Essa é a maneira mais fácil de capturar informações brutas.
  • Isso é inspirado (mas não copiado) na versão original encontrada pelo squeamish-ossifrage

Bônus de dinossauro

Eu deveria ter um bônus, porque todo mundo sabe que " Common Lisp é um dinossauro moribundo ".

coredump
fonte
Você pode mudar para o bloco de código em vez do bloco de cotação? (para o seu código)
Otimizador
@Optimizer done
coredump