迈克尔·克里顿的捕鼠器


9

1984年,迈克尔·克里顿(Michael Crichton)用BASIC编写了一个安全程序,该程序已发布在《创意计算》杂志上。该程序将要求用户键入自己选择的短语,记录击键之间的间隔,然后要求她重新键入该短语。如果时间差异太大,则程序会将用户识别为冒名顶替者。

您的任务:以您选择的语言创建Crichton程序的版本。

规则:

  1. 与用户通信的短语(“请输入关键词”,“请再次输入关键词”等)无论实际长度如何,均计为一个字节。这仅用于用户通信,请勿尝试在字符串中隐藏程序代码。

  2. 通过/失败测试应基于与原始间隔的百分比差异的平均绝对值。如果字符串不匹配,请自行决定返回失败还是允许用户重试。

  3. 关键字短语不应包含空字符串。如果关键字对于您的字符串数据类型太长,则可以自行决定是截断还是不允许重新开始。

  4. 测试的灵敏度(通过/失败测试的阈值)应在源代码中进行调整。

  5. 如果您的源代码可以格式化为可识别的类似于恐龙,那么我最初会提供总字节数的20%的奖励。已经指出,这是高度主观的,也许更适合于流行竞赛,所以我删除了此奖金。但是,我仍然衷心鼓励恐龙格式化,如果将代码格式化为看起来像恐龙,则可以从字节总数中扣除任何纯修饰性的注释,换行符或空格字符。

  6. 最短的字节数获胜,具体取决于字符串长度和恐龙格式调整。

请注意,我上面的说明与Crichton代码的操作不完全匹配,可以在网上找到其副本。请遵循规范,不要尝试克隆原始文件。


5
“这是迈克尔·克里顿(Michael Crichton),因此,如果您的源代码可以格式化为可识别的类似于恐龙,则可以从总字节数中减去20%。” -嗯...不 这个规则太主观了。请删除。除此之外,请继续。
约翰·德沃夏克

4
@JanDvorak我不认为它的“主观”。将某些ASCII艺术称为或不是恐龙是一个相当容易的调用
Optimizer

3
@Optimizer并非在所有情况下都如此。希腊字母lambda看起来像恐龙吗?我很确定。
John Dvorak 2014年

3
其他一些小意见:是"Please type the key phrase"计数为1个字节,还是仅短语计数和带引号的短语计数为3个字节(",短语,")?是否有意让更长的时间间隔和更短的时间间隔“抵消”并再次变得均匀?程序是否必须验证两个关键短语是否匹配?
门把手

Answers:


9

红宝石, 171167 157字节

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

输出true,如果平均方差为20%以上,否则输出false

恐龙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}###########\   \
            `-._    ,___...----...__,   ,__\  \
                |   |_|           |_|   |    \ \
                |___|               |___|      \\/)

取消高尔夫:

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' 在某些Ruby REPL中运行时,可以删除该库,因为该库已加载。


4

Java 768字节

什么?Java的?打码高尔夫?

这可能是最糟糕的事情,但是我还是尝试了。

它在控制台窗口中显示所有消息,但实际的键入发生在JTextField中。不太好看。哦,要节省5个字节,您必须自己调整JFrame的大小。另外,它不会第二次检查字符串的正确性。不确定是否违反规格。

使用方法:

在文本字段中键入密钥。

不要按Enter键,请进入控制台并输入内容。它将显示另一条消息

在文本字段中键入相同的内容(现在应清除)。

转到控制台,然后再次按一下。它将显示您是否是入侵者。

松散:

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

打高尔夫球:

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

无法通过Java在原始模式下设置TTY(除非您准备使用JNI)。所以我明白为什么您需要一个JFrame。但是,实际上,这是我多年来所见过的最不友好的程序:-)我不确定是否要赞成或反对这个答案。
coredump 2014年

我为用户的不友好程度表示赞成(甚至连一个字都说出来?)。这基本上是艺术。
IngoBürk'14

我相信通过扩大班级可以更好地打高尔夫球JFrame,因此您不需要f
PurkkaKoodari 2014年

3

HTML,JavaScript(ES6),328

该代码的总字节数为402字节,并且消息与用户交互:

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

总共有78个字节,所以总分=> 402-78 + 4 = 328

最新的Firefox中运行以下代码段,然后在输入框中键入密钥,然后按Enter键。

代码检查输入的密钥和重新输入的密钥是否相同(如果不一致,则提示再次输入),计算平均绝对差百分比,并检查它是否小于变量的值 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)

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

与编译-DY=(y=clock())-x-DZ=a[d]-DE=getch()-DW=);while-DU=i++[d]=Y-DP=puts(。出于演示目的而添加的换行符可能会被删除(给出的字节数不包括在内)。

取消评论+:

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
}

这不会检查重新键入的短语是否相同,如果未将用户标识为冒名顶替者,也不会输出任何内容。

这也不会考虑在第一次按键之前提示后所花费的时间。


不应该getch打赌getc还是getchar?我有一个未定义的对'getch'的引用,如果我没记错的话,哪个引用已被弃用?
coredump 2014年

我也有“ file.c:1:1:警告:数据定义没有类型或存储类”(gcc)。我char在全局声明之前添加了,现在,这在运行时给出了分段错误。您能否提供有关如何构建的详细信息?您正在使用什么编译器?谢谢。
coredump 2014年

@coredump警告是无害的;但是,如果您要删除警告,则应输入int,并将其初始化为0。我已经在Windows上使用gcc(使用Window的getch)对它进行了测试。getch使用而不是getcgetchar因为getch不需要在处理任何字符之前按下返回键(getch在Windows上确实已弃用,尽管此处使用弃用的功能没有错)。
es1024 2014年

我正在Linux上进行测试,并求助于stackoverflow.com/questions/7469139/…以便使其工作。谢谢。
coredump

2

斯卡拉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

删除所有间距后,您将:

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

我敢肯定,有谁比我更有能力成为恐龙!

简要说明:

l方法读取字符并保留nanoTime键入每个字符时的。

m方法打印出来"Enter"l按Enter键中断该方法(字符13),然后将其映射到nanoTimes,然后获得每个字符之间的时间间隔。

接下来的2行读取2个字符串,将其压缩,然后找到第二个间隔与第一个间隔之间的百分比差的平均绝对值,最后打印此平均值是否小于0.2


1

普通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)

不打高尔夫球

(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

补充说明

  • 符合所有规则
  • 当用户首次输入空密码时,程序会干净地中止
  • 键入时Escape,程序将干净终止。
  • 经过最新的SBCL和CCL实施测试
  • 需要cl-charms,这是Ncurses的包装。这是捕获原始输入的最简单方法。
  • 这是受squeamish-ossifrage找到的原始版本的启发(但不能复制)

恐龙加成

我应该有一个奖金,因为每个人都知道“ 普通Lisp是垂死的恐龙 ”。


您可以切换到代码块而不是引号块吗?(针对您的代码)
Optimizer

@Optimizer完成
coredump
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.