challenge 制限時間内のキー入力検査

ユーザーの入力を検査する関数 InputCheckerを作成して 3回連続実行してください。

関数 InputCheckerは、以下の仕様を満たしてください。

  1. 引数に 時間(秒) Nと文字列 Sを受け取ること。
  2. ユーザからの標準入力とあらかじめ指定された S を比較し、 一致すれば「OK」、一致しなければ「NG」を標準出力へ出力すること。
  3. ユーザーの入力がN秒以内に完了しなかった場合は、比較せず 「TIME OUT」を標準出力へ出力すること。
  4. 経過時間はユーザーが入力を開始した地点から計測すること。
  5. ENTER キーの入力によってユーザー入力が完了したと仮定すること。
  6. ユーザの入力が完了するまでは、完了を待ち続けること

たとえば、「InputCheker(5, "ABCDEF")」と指定した場合、 出力例はこんな感じです。

  1. input(ABCDEF) =>ABCDEF<ENTER>
    1. result => OK
    2. result => NG
    3. result => TIME OUT

1. input(ABCDEF) =>

と出力して入力待ちをし、ユーザーが「ABCDEF<ENTER>」を入力したとき、 入力開始から5秒以内ならば「OK」、5秒をこえていれば「TIME OUT」を出力します。 このとき、ユーザーがキーを押下しなければ1. を出力してから たとえ10秒たっていても「TIME OUT」にはならないので注意してください。 時間計測はあくまでユーザーが入力を開始してからです。


このお題はraynstardさんの投稿です。ご協力ありがとうございます。

Posted feedbacks - Common Lisp

Movitzとか変なものも色々試してみましたが、結局無難にncursesを
使うことにしました。
sbclで動作確認しています。
cl-ncursesが使えれば他の処理系でも大丈夫じゃないかと思うのですが、
未確認です。
cl-ncursesで外部から使いたい関数がexportされていないようなので、
exportしなおしたりしていますが、これで良いのかは自信がありません…。

何回目かを指定すると結果をずらして表示するようにしてみました。

(dotimes (i 3)
  (input-checker "ABCDEF" 5 i))

=>
 input(ABCDEF) => ABCDEF
 1. result => OK
 2. result => NG
 3. result => TIME OUT

               Time: 9.585 sec.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
;; 不要?
(in-package #:cl-ncurses)
(defpackage #:cl-ncurses 
  (:use :cl :uffi)
  (:export initscr endwin printw refresh move clrtoeol clrtobot getch)) 
;; 

(defpackage #:doukaku-64 
  (:use #:cl #:cl-ncurses)
  (:export #:input-checker))

(in-package #:doukaku-64)

;; 補助関数/マクロ群
(defmacro with-curses (&body body)
  `(unwind-protect 
        (progn
          (initscr)
          ,@body)
     (endwin)))

(defmacro formatw (&rest format-args)
  `(printw (format nil ,@format-args)))

(defun get-input/time (&optional (term-char #\Newline))
  (let ((term-char-code (char-code term-char))
	start-time input-chars)
    (do ((ch 
          (prog1 (getch) (setq start-time (get-internal-real-time))) ;start time
          (getch))) 
        ((= ch term-char-code)
         (values 
          (coerce (nreverse (mapcar #'code-char input-chars)) 'string)
          start-time
          (get-internal-real-time)))    ;end time
      (push ch input-chars))))

;; 本体
(defun input-checker (string limit-sec &optional (n 0))
  (let (start-time end-time (time-res internal-time-units-per-second))
    (with-curses
      (let ((x-pos 5) (y-pos 10))
        (move x-pos y-pos)
        (formatw "input(~A) => " string)
        (clrtoeol)
        (multiple-value-bind (input-string start-time end-time) (get-input/time)
          (move (+ 1 x-pos n) y-pos)
          (formatw "~A. result => ~A" (1+ n)
                   (let ((limit-time (+ start-time (* limit-sec time-res))))
                     (if (< limit-time end-time)
                         "TIME OUT"
                         (if (string= string input-string) "OK" "NG"))))
          (clrtobot)
          (move (+ 5 x-pos n) (+ 5 y-pos))
          (formatw "Time: ~F sec." (/ (- end-time start-time) time-res))
          (refresh))))))

標準入力からリアルタイムキー入力をするためには、rawモードにする必要があるということで、raynstardさんの#3232のコードとほぼ同じことを、cffiを使ってやってみました。所々、定数を直書きしてしまったのでポータブルじゃないかもしれません。RAWモードへの切り替えがキー入力の度に発生してしまいます。
cygwin と clisp で、なんとか動きましたが、自信はないので、よろしければ添削おねがいします。
(input-checker 5 "ABCDEF" 3)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(load "/usr/lib/clisp/asdf.lisp")

(setf asdf:*central-registry*
      '(*default-pathname-defaults*
         #p"/usr/lib/clisp/system/"))
(asdf:oos 'asdf:load-op :cffi :verbose nil)

(defpackage :raw-mode (:use :common-lisp :cffi ))
(in-package :raw-mode)

(export '(get-char-with-row-mode))
(cffi:defcstruct termio
                 (c_iflag :unsigned-int)
                 (c_oflag :unsigned-int)
                 (c_cflag :unsigned-int)
                 (c_lflag :unsigned-int)
                 (c_line :char)
                 (c_cc :int :count 18)
                 (c_ispeed :int)
                 (c_ospeed :int))
(cffi:defcfun ("ioctl" ioctl) :int
              (fd :int) (request :int) (termio :pointer))
(cffi:defcfun ("read" %read) :int
              (fd :int) (buf :string) (count :int))

(defun get-char ()
  (character
    (with-foreign-pointer-as-string (str 2)
      (loop 
        (let ((c (%read 0 str 1)))
          (if (not (eq c -1))
            (return str)))))))

(defmacro get-char-with-row-mode (&body body)
  `(let (ch)
     (with-foreign-objects
       ((backup 'termio)
         (term 'termio))
         (unwind-protect
           (ioctl 0 5 backup)
           (progn
             (ioctl 0 5 term)
             (setf (foreign-slot-value term 'termio 'c_lflag)
                     (logand
                       (foreign-slot-value term 'termio 'c_lflag)
                       (parse-integer "FFFFFFF9" :radix 16))
                   (mem-aref (foreign-slot-value term 'termio 'c_cc) :int 9) 1
                   (mem-aref (foreign-slot-value term 'termio 'c_cc) :int 16) 0)
             (ioctl 0 8 term)
             (setq ch (get-char)))
           (ioctl 0 8 backup)))
     ch))

(in-package :common-lisp-user)
(use-package :raw-mode)

(setq count 0)
(defun input-checker (limit test-str &optional (n 1))
  (format t "~d. input(~a) =>" (incf count) test-str)
  (dotimes (i n)
  (loop with start 
        for c = (get-char-with-row-mode)
        until (equal #\NewLine c)
        do (if (null start) (setq start (get-universal-time)))
        collect c into str
        finally (format t "~%     ~d. result => " (1+ i))
                (cond 
                  ((and start (< limit (- (get-universal-time) start)))
                   (format t "~a" 'TIMEOUT))
                  ((equal test-str (coerce str 'string))
                   (format t "~a" 'OK))
                  ( t
                    (format t "~a" 'NG)))))
  (terpri))

Index

Feed

Other

Link

Pathtraq

loading...