制限時間内のキー入力検査
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)
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))
|


raynstard
#3420()
Rating0/2=0.00
関数 InputCheckerは、以下の仕様を満たしてください。
たとえば、「InputCheker(5, "ABCDEF")」と指定した場合、 出力例はこんな感じです。
1. input(ABCDEF) =>と出力して入力待ちをし、ユーザーが「ABCDEF<ENTER>」を入力したとき、 入力開始から5秒以内ならば「OK」、5秒をこえていれば「TIME OUT」を出力します。 このとき、ユーザーがキーを押下しなければ1. を出力してから たとえ10秒たっていても「TIME OUT」にはならないので注意してください。 時間計測はあくまでユーザーが入力を開始してからです。
このお題はraynstardさんの投稿です。ご協力ありがとうございます。
[ reply ]