Hello. I'm writing a game-playing program in common lisp for a class,
and I'd like to have a process run while waiting for the user to enter
input (so the program can do more thinking while the user is thinking).
I'm familiar with read-char, but I believe this halts everything until
input is read. Is there any way to just poll an input buffer or
something every once in a while? I'd prefer not to start playing with
threads, but if anyone knows of a straightforward option like that I'd
be interested in learning about it.
Thanks for any help.
- Jake
"jakemiles" <···········@gmail.com> writes:
> Hello. I'm writing a game-playing program in common lisp for a class,
> and I'd like to have a process run while waiting for the user to enter
> input (so the program can do more thinking while the user is thinking).
> I'm familiar with read-char, but I believe this halts everything until
> input is read. Is there any way to just poll an input buffer or
> something every once in a while? I'd prefer not to start playing with
> threads, but if anyone knows of a straightforward option like that I'd
> be interested in learning about it.
>
> Thanks for any help.
>
> - Jake
Will PEEK-CHAR work for you?
jakemiles <···········@gmail.com> wrote:
+---------------
| Hello. I'm writing a game-playing program in common lisp for a class,
| and I'd like to have a process run while waiting for the user to enter
| input (so the program can do more thinking while the user is thinking).
| I'm familiar with read-char, but I believe this halts everything until
| input is read. Is there any way to just poll an input buffer or
| something every once in a while? I'd prefer not to start playing with
| threads, but if anyone knows of a straightforward option like that I'd
| be interested in learning about it.
+---------------
If you're willing to wait until the user has typed a complete line --
or some other form of "push", such as a ^D under Unix/Linux[1] --
then the CL standard function LISTEN may be what you want. That's
what I use when I'm running a long CPU-bound test and don't want to
use interrupts to stop it. Example:
> (loop for i from 0
until (listen)
do (format t "tick...~%")
(sleep 0.1)
finally (format t "i = ~d~%" i))
tick...
tick...
tick...
tick...
tick...
tick...
<== [typed <CR> here]
i = 6
NIL
>
The <CR> has been read at this point, but it's just leading
whitespace and won't interfere with your next REPL input.
The same thing, but without the (SLEEP 0.1):
> (loop for i from 0
until (listen)
do (format t "tick...~%")
finally (format t "i = ~d~%" i))
tick...
tick...
tick...
...[many, many lines omitted]...
tick...
tick...
tick...
<== [typed <CR> here]
i = 7984
NIL
>
-Rob
[1] Picky details: Most people who haven't dug into it don't understand
that ^D [or whatever character "stty -a" reports for "eof ="]
*isn't* really an "EOF" character at all!! It's a "push" character.
The reason it's normally considered to be the EOF character is that
it's *usually* typed at the beginning of the line, when there is
nothing to read. So the Unix/Linux "read()" call returns 0, which
is the convention for EOF. But if you type one or more characters
other than #\newline and then type your "EOF" character (normally
<Ctrl-D> or ^D), then you will "push" those characters into the
"read()" and it will return however many you had typed. Common
Lisp's READ-LINE lets you tell the difference with its second
return value:
> (read-line)
hello!
"hello!"
NIL
> (read-line)
hel^D <== Note: With CMUCL you may have to type 2 ^D's.
"hel"
T
>
Here's an example of that:
> (loop for i from 0
until (listen)
do (format t "tick...~%")
(sleep 1)
finally (format t "i = ~d~%" i)
(return (multiple-value-list (read-line))))
tick...
tick...
Atick... <== [Typed A here]
tick...
Btick... <== [Typed B here]
tick...
tick...
Ctick... <== [typed C then ^D here]
i = 8
("ABC" T)
>
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
Thank you for the help. (listen) seems perfect, but I'm afraid I've
run into another problem. The function below works as expected in
LispWorks, but not in Lisp In a Box (EMACS+SLIME+CLISP) on a PC. I
have a Mac at home, so I will try it under CMUCL, but I'm trying it on
my PC at work and though it sort of reacts to the input, it just keeps
looping.
(defun think-and-listen ()
(sleep 0.2)
(print "Thinking...")
(if (listen)
(read)
(think-and-listen)))
It doesn't stop no matter what keys I hit - CR, Ctrl-D, Ctrl-C, a
complete lisp expression like (1 1). I have to kill Emacs with Ctrl-X
Ctrl-C to exit the loop. In LispWorks, typing an open-paren pauses the
loop as expected, and I can complete the lisp expression with 1 1) and
it returns (1 1) as expected. In SLIME/CLISP, when I type (1) and hit
enter, it prints some of the "Thinking..."s in bold, which is what
SLIME does when you enter a complete lisp expression and hit CR, but
then keeps looping and printing "Thinking...".
One clue: when I type anything, the minibuffer says
pipelined request... swank:listener-eval "(
My thought is that this is a SLIME problem, not a Lisp problem, but any
thoughts? I need to get this to work in SLIME+CLISP on a PC, because
that's what my instructor will be running it in.
Also, where on earth is clisp's reference manual? Where can I look up
that ext:with-keyboared function?
- Jake
Rob Warnock wrote:
> jakemiles <···········@gmail.com> wrote:
> +---------------
> | Hello. I'm writing a game-playing program in common lisp for a class,
> | and I'd like to have a process run while waiting for the user to enter
> | input (so the program can do more thinking while the user is thinking).
> | I'm familiar with read-char, but I believe this halts everything until
> | input is read. Is there any way to just poll an input buffer or
> | something every once in a while? I'd prefer not to start playing with
> | threads, but if anyone knows of a straightforward option like that I'd
> | be interested in learning about it.
> +---------------
>
> If you're willing to wait until the user has typed a complete line --
> or some other form of "push", such as a ^D under Unix/Linux[1] --
> then the CL standard function LISTEN may be what you want. That's
> what I use when I'm running a long CPU-bound test and don't want to
> use interrupts to stop it. Example:
>
> > (loop for i from 0
> until (listen)
> do (format t "tick...~%")
> (sleep 0.1)
> finally (format t "i = ~d~%" i))
> tick...
> tick...
> tick...
> tick...
> tick...
> tick...
> <== [typed <CR> here]
> i = 6
> NIL
> >
>
> The <CR> has been read at this point, but it's just leading
> whitespace and won't interfere with your next REPL input.
>
> The same thing, but without the (SLEEP 0.1):
>
> > (loop for i from 0
> until (listen)
> do (format t "tick...~%")
> finally (format t "i = ~d~%" i))
> tick...
> tick...
> tick...
> ...[many, many lines omitted]...
> tick...
> tick...
> tick...
> <== [typed <CR> here]
> i = 7984
> NIL
> >
>
>
> -Rob
>
> [1] Picky details: Most people who haven't dug into it don't understand
> that ^D [or whatever character "stty -a" reports for "eof ="]
> *isn't* really an "EOF" character at all!! It's a "push" character.
> The reason it's normally considered to be the EOF character is that
> it's *usually* typed at the beginning of the line, when there is
> nothing to read. So the Unix/Linux "read()" call returns 0, which
> is the convention for EOF. But if you type one or more characters
> other than #\newline and then type your "EOF" character (normally
> <Ctrl-D> or ^D), then you will "push" those characters into the
> "read()" and it will return however many you had typed. Common
> Lisp's READ-LINE lets you tell the difference with its second
> return value:
>
> > (read-line)
> hello!
>
> "hello!"
> NIL
> > (read-line)
> hel^D <== Note: With CMUCL you may have to type 2 ^D's.
> "hel"
> T
> >
>
> Here's an example of that:
>
> > (loop for i from 0
> until (listen)
> do (format t "tick...~%")
> (sleep 1)
> finally (format t "i = ~d~%" i)
> (return (multiple-value-list (read-line))))
> tick...
> tick...
> Atick... <== [Typed A here]
> tick...
> Btick... <== [Typed B here]
> tick...
> tick...
> Ctick... <== [typed C then ^D here]
> i = 8
> ("ABC" T)
> >
>
> -----
> Rob Warnock <····@rpw3.org>
> 627 26th Avenue <URL:http://rpw3.org/>
> San Mateo, CA 94403 (650)572-2607
"jakemiles" <···········@gmail.com> writes:
> Thank you for the help. (listen) seems perfect, but I'm afraid I've
> run into another problem. The function below works as expected in
> LispWorks, but not in Lisp In a Box (EMACS+SLIME+CLISP) on a PC.
Obviously, emacs do some buffering, and differently from the unix
terminal driver.
> I
> have a Mac at home, so I will try it under CMUCL, but I'm trying it on
> my PC at work and though it sort of reacts to the input, it just keeps
> looping.
>
> (defun think-and-listen ()
> (sleep 0.2)
> (print "Thinking...")
> (if (listen)
> (read)
> (think-and-listen)))
>
> It doesn't stop no matter what keys I hit - CR, Ctrl-D, Ctrl-C, a
> complete lisp expression like (1 1). I have to kill Emacs with Ctrl-X
> Ctrl-C to exit the loop.
Try to send some data to the lisp process! (Type RET)
With inferior-list:
[769]> (think-and-listen)
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..."
"Thinking..." das RET
"Thinking..." DAS
[770]>
> One clue: when I type anything, the minibuffer says
>
> pipelined request... swank:listener-eval "(
>
> My thought is that this is a SLIME problem, not a Lisp problem, but any
> thoughts? I need to get this to work in SLIME+CLISP on a PC, because
> that's what my instructor will be running it in.
Of course.
You cannot do synchrone I/O thru emacs+slime+swank+clisp.
There are two processes, and the protocol implemented by slime+swank
doesn't allow to send a single character.
slime-net-send
Function: Send a SEXP to Lisp over the socket PROC.
slime-repl-resend
Command: (not documented)
slime-repl-send-input
Function: Goto to the end of the input and send the current input.
slime-repl-send-string
Function: (not documented)
slime-send
Function: Send SEXP directly over the wire on the current connection.
slime-send-sigint
Command: (not documented)
slime-repl-send-string make the form read from the string be evaluated.
Instead, you could do some kind of RPC between emacs and clisp, or
between clisp and emacs, thru slime+swank. Have a look at:
http://darcs.informatimago.com/emacs/slime-rpc.el
> Also, where on earth is clisp's reference manual? Where can I look up
> that ext:with-keyboared function?
Not necessarily too readable, but the url is in here somewhere:
[770]> (describe 'ext:with-keyboard)
EXT:WITH-KEYBOARD is the symbol EXT:WITH-KEYBOARD, lies in #<PACKAGE EXT>, is
accessible in 7 packages EXT, FFI, POSIX, READLINE, SCREEN, SYSTEM, ZLIB, names
a macro, has 1 property SYSTEM::DOC.
;; running ["emacsclient" "-n" "-e"
"(w3-fetch \"http://clisp.cons.org/impnotes/terminal.html#with-kbd\")"
]...done
For more information, evaluate (SYMBOL-PLIST 'EXT:WITH-KEYBOARD).
#<PACKAGE EXT> is the package named EXT.
It imports the external symbols of 7 packages POSIX, SOCKET, GSTREAM, GRAY,
I18N, COMMON-LISP, CUSTOM and exports 723 symbols to 7 packages ZLIB,
READLINE, POSIX, FFI, CS-COMMON-LISP-USER, SCREEN, SYSTEM.
#<MACRO #<COMPILED-FUNCTION EXT:WITH-KEYBOARD>> is a macro expander.
For more information, evaluate
(DISASSEMBLE (MACRO-FUNCTION 'EXT:WITH-KEYBOARD))
.
Documentation:
SYSTEM::IMPNOTES:
"terminal.html#with-kbd"
SYSTEM::FILE:
(#P"/tmp/clisp-2.39-pjb1-regexp-build/keyboard.fas" 10 12)
--
__Pascal Bourguignon__ http://www.informatimago.com/
Litter box not here.
You must have moved it again.
I'll poop in the sink.
Wow, thank you very much. That's a lot of information.
I don't have enough time to start playing with the slime/swank
interface, but I found a workaround for my situation. I installed
clisp on its own via Cygwin, and this function behaves correctly there.
So I will just have the instructor do that; he seemed willing to
accommodate.
Thanks again. Long live lisp.
- Jake
Pascal Bourguignon wrote:
> "jakemiles" <···········@gmail.com> writes:
>
> > Thank you for the help. (listen) seems perfect, but I'm afraid I've
> > run into another problem. The function below works as expected in
> > LispWorks, but not in Lisp In a Box (EMACS+SLIME+CLISP) on a PC.
>
> Obviously, emacs do some buffering, and differently from the unix
> terminal driver.
>
>
> > I
> > have a Mac at home, so I will try it under CMUCL, but I'm trying it on
> > my PC at work and though it sort of reacts to the input, it just keeps
> > looping.
> >
> > (defun think-and-listen ()
> > (sleep 0.2)
> > (print "Thinking...")
> > (if (listen)
> > (read)
> > (think-and-listen)))
> >
> > It doesn't stop no matter what keys I hit - CR, Ctrl-D, Ctrl-C, a
> > complete lisp expression like (1 1). I have to kill Emacs with Ctrl-X
> > Ctrl-C to exit the loop.
>
> Try to send some data to the lisp process! (Type RET)
>
> With inferior-list:
>
> [769]> (think-and-listen)
>
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..."
> "Thinking..." das RET
>
> "Thinking..." DAS
> [770]>
>
>
> > One clue: when I type anything, the minibuffer says
> >
> > pipelined request... swank:listener-eval "(
> >
> > My thought is that this is a SLIME problem, not a Lisp problem, but any
> > thoughts? I need to get this to work in SLIME+CLISP on a PC, because
> > that's what my instructor will be running it in.
>
> Of course.
> You cannot do synchrone I/O thru emacs+slime+swank+clisp.
>
> There are two processes, and the protocol implemented by slime+swank
> doesn't allow to send a single character.
>
>
> slime-net-send
> Function: Send a SEXP to Lisp over the socket PROC.
> slime-repl-resend
> Command: (not documented)
> slime-repl-send-input
> Function: Goto to the end of the input and send the current input.
> slime-repl-send-string
> Function: (not documented)
> slime-send
> Function: Send SEXP directly over the wire on the current connection.
> slime-send-sigint
> Command: (not documented)
>
>
> slime-repl-send-string make the form read from the string be evaluated.
>
>
> Instead, you could do some kind of RPC between emacs and clisp, or
> between clisp and emacs, thru slime+swank. Have a look at:
> http://darcs.informatimago.com/emacs/slime-rpc.el
>
>
> > Also, where on earth is clisp's reference manual? Where can I look up
> > that ext:with-keyboared function?
>
> Not necessarily too readable, but the url is in here somewhere:
>
> [770]> (describe 'ext:with-keyboard)
>
> EXT:WITH-KEYBOARD is the symbol EXT:WITH-KEYBOARD, lies in #<PACKAGE EXT>, is
> accessible in 7 packages EXT, FFI, POSIX, READLINE, SCREEN, SYSTEM, ZLIB, names
> a macro, has 1 property SYSTEM::DOC.
> ;; running ["emacsclient" "-n" "-e"
> "(w3-fetch \"http://clisp.cons.org/impnotes/terminal.html#with-kbd\")"
> ]...done
>
> For more information, evaluate (SYMBOL-PLIST 'EXT:WITH-KEYBOARD).
>
> #<PACKAGE EXT> is the package named EXT.
> It imports the external symbols of 7 packages POSIX, SOCKET, GSTREAM, GRAY,
> I18N, COMMON-LISP, CUSTOM and exports 723 symbols to 7 packages ZLIB,
> READLINE, POSIX, FFI, CS-COMMON-LISP-USER, SCREEN, SYSTEM.
>
> #<MACRO #<COMPILED-FUNCTION EXT:WITH-KEYBOARD>> is a macro expander.
> For more information, evaluate
> (DISASSEMBLE (MACRO-FUNCTION 'EXT:WITH-KEYBOARD))
> .
>
> Documentation:
> SYSTEM::IMPNOTES:
> "terminal.html#with-kbd"
> SYSTEM::FILE:
> (#P"/tmp/clisp-2.39-pjb1-regexp-build/keyboard.fas" 10 12)
>
>
> --
> __Pascal Bourguignon__ http://www.informatimago.com/
> Litter box not here.
> You must have moved it again.
> I'll poop in the sink.
"jakemiles" <···········@gmail.com> writes:
> Hello. I'm writing a game-playing program in common lisp for a class,
> and I'd like to have a process run while waiting for the user to enter
> input (so the program can do more thinking while the user is thinking).
> I'm familiar with read-char, but I believe this halts everything until
> input is read. Is there any way to just poll an input buffer or
> something every once in a while?
READ-CHAR-NO-HANG
But this won't remove the buffering done on the keyboard by the OS.
For this, you need implementation dependant stuff.
In clisp, you can use EXT:WITH-KEYBOARD and EXT:*KEYBOARD-INPUT*
(and the SCREEN package might be useful for terminal output).
--
__Pascal Bourguignon__ http://www.informatimago.com/
HEALTH WARNING: Care should be taken when lifting this product,
since its mass, and thus its weight, is dependent on its velocity
relative to the user.