From: Joel Reymont
Subject: Please help me express myself ... with closures
Date: 
Message-ID: <1107877671.280551.253730@c13g2000cwb.googlegroups.com>
Folks,

I'm trying to write the game logic for my poker. I tried it using a
state machine but found it needlessly complex. The issue at hand is
that I need to periodically ask user for input. I would like to write
something like this:

ask first player to bet
 make sure the bet is the right amount

ask second player to bet and receive response
 make sure the best

for al players in the game
 ask for a bet
   make sure

proceed to next round

The closest example of what I'm looking to do is the "using scheme
continuations in web apps" paper but I'm coding in Lisp.

I would like to somehow create a closure representing the state of my
function whenever I'm asking for a bet so that this state can be
invoked when a bet arrives from the user. Whenever I ask for a bet I
send a network packet and when a packet arrives I post it to the
incoming queue. A response may not arrive within the alloted time in
which case I will call the closure with nil.

How do I create a closure of "where I am right now" whenever I ask for
a bet and let the function continue from that point on once a bet
arrives?

    Thanks, Joel

From: Kaz Kylheku
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107880660.262873.266190@o13g2000cwo.googlegroups.com>
Joel Reymont wrote:
> How do I create a closure of "where I am right now" whenever I ask
for
> a bet and let the function continue from that point on once a bet
> arrives?

That isn't what lexical closures do. They capture lexical bindings
"where I am right now" along with a function body which can access
those bindings and can take additional parameters too. Invoking that
function isn't the same as resuming the original function where the
closure was created. When that closure function returns, it goes back
to whatever invoked it.

You can use closures as callback handles that are invoked when
responses arrive, and maintain the state of the multicast transation in
the same lexical scope, e.g. pseudocode:

  (let ((reply-count number-of-players))
    (block all-done
      (for-all-players (p)
        (ask-for-bet p (lambda (bet)
                         (if bet
                           (process-bet p bet)
                           (process-timeout p))
                         (when (zerop (decf reply-count))
                           (return-from all-done)))))
      (protocol-event-loop)))

The idea here is that we asynchronously ask the players for bets with
this FOR-ALL-PLAYERS construct, assumed to be some macro for processing
the list of players. For each player, we register a callback closure
that is invoked with a NIL argument for a timeout, or with an object
representing the bet information otherwise. After we fire all these
requests, we park the calling thread in the event loop by calling
PROTOCOL-EVENT-LOOP, some function in our game that waits for network
packets and dispatches the corresponding events. This function will
call back into the closures as replies are matched with requests in the
queue, or timeouts occur. As the closures are invoked, they decrement
the local variable REPLY-COUNT (they have it captured!). When that
variable drops to zero---all replies have either timed out or
arrived---a nonlocal exit is performed out of the surrounding block
ALL-DONE, which unwinds out through the in-progress call to
PROTOCOL-EVENT-LOOP.

Hope you get some ideas out of this sketch.
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107882051.036937.193410@f14g2000cwb.googlegroups.com>
Thanks Kaz! I get the idea but...

Why the need for that non-local exit if you are not processing bets
sequentially? And then...

What do I do if I have to make sure bets are ordered? I cannot ask for
the next bet before I process this one. Say this was a sequence of
dialogs that I'm progressing through. Or something like that. I do want
to code this in a sequential style.

I think the non-local exit with return-from is what I was missing.

Let me see if I get this right...

I should frame all my bet requests in a BLOCK form and return a lambda
which does a return-from BLOCK once it's called. Better yet, can I
abstract the ask-for-bet into a separate function that returns a
closure which when called does a non-local exit to the block inside it
and returns from ask-for-bet?

Would something like this work?

(defun ask-for-bet ()
 (block all-done
    (lambda (bet)
      ;; process bet
      (return-from all-done))
   ))

I'll go try this as I'm not sure block and return-from exist in Lisp :-)
From: Peter Seibel
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <m3zmyf9dmv.fsf@javamonkey.com>
"Joel Reymont" <······@gmail.com> writes:

> Would something like this work?
>
> (defun ask-for-bet ()
>  (block all-done
>     (lambda (bet)
>       ;; process bet
>       (return-from all-done))
>    ))

No, because the name of a BLOCK only has dynamic-extent--if you invoke
that LAMBDA after it has been returned from the BLOCK you'll get an
error.

> I'll go try this as I'm not sure block and return-from exist in Lisp :-)

They do but they're incidental to the problem you're trying to solve.
You can check the HyperSpec to learn more about them or for a more
narrative version, this chapter from my book:

  <http://www.gigamonkeys.com/book/the-special-operators.html>

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107883838.344868.72000@g14g2000cwa.googlegroups.com>
Thank you guys! I think you caught me so I have to lay my cards on the
table. I want continuations! I want to do web programming with
continuations but in Lisp. It doesn't matter that mine is a poker game,
the goal is exactly the same.

I want to write the game logic code using sequential processing. It's
like I had to proceed through a series of dialogs (web pages?) and was
asking users for input on each one. I would do some processing before
advancing to the next dialog. There's no need for me to let the user go
back as this is a poker game.

I basically want to save my stack at the point of asking the user for
input and return a continuation that can be invoked once the input is
available. I would process that input, advance and ask for input again.

How would I achieve this with Lisp? I'm gonna go back to OnLisp and see
if coding in CPS is what I need.

Wade, I noticed that you are calling (get-betting-event queue). I
really need it the other way around. Unless that get-betting-event
function saves its stack, which I don't think is something that you
intend.

    Thanks, Joel
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107884576.567513.81890@z14g2000cwz.googlegroups.com>
The reason for my asking for help is that I can't figure out how to do
what I'm
trying to do with closures. And continuations are not available in
Lisp.

It's not that I need to close over a series of variables, I just need
to save the stack and return to this point of processing later on.

Here is some pseudo code:

ask player 1 to post small blind

some checking and processing

ask player 2 to post big blind

more processing

ask player 3 to match player 2's bet or raise it

more processing

...

repeat until I get back to player 2 who posted the big blind

ask player 2 (who posted bb) if they want to raise. if they do not
then proceed to the next round of the game by dealing one card to each
player, etc...

I want to see if I can code my logic just like above, with a loop for
the rest of the players from big bling on. This is the logic for a
poker game. The complication is that I ask player will not receive its
input for a while after I send the request, so I want to move on and
process other games.

I would take a packet from my network queue and see what game it
belongs to. Then I would look up the "continuation" in a hash table,
call it with the event and move on.
From: Eric Daniel
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <scidnZkB1qEUr5TfRVn-iQ@newedgenetworks.com>
In article <·······················@z14g2000cwz.googlegroups.com>, Joel Reymont wrote:
[...]
>  I want to see if I can code my logic just like above, with a loop for
>  the rest of the players from big bling on. This is the logic for a
>  poker game. The complication is that I ask player will not receive its
>  input for a while after I send the request, so I want to move on and
>  process other games.
>  
>  I would take a packet from my network queue and see what game it
>  belongs to. Then I would look up the "continuation" in a hash table,
>  call it with the event and move on.
>  

Has anybody used CMUCL's MP extension for this kind of stuff?
It looks like this would fit Joel's needs. Highly non-portable,
of course.

-- 
Eric Daniel
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107899855.539025.77220@z14g2000cwz.googlegroups.com>
I'm currently using Allegro CL and do want to deliver on Windows, Mac
OSX and Linux so CMUCL won't work for me.
From: Rahul Jain
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <87d5v8grv8.fsf@nyct.net>
"Joel Reymont" <······@gmail.com> writes:

> I'm currently using Allegro CL and do want to deliver on Windows, Mac
> OSX and Linux so CMUCL won't work for me.

Allegro CL's MP package is mostly similar to CMUCL's, except that ACL's
is implemented better. :)

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Alan Shutko
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <87y8dzt0b7.fsf@vera.springies.com>
"Joel Reymont" <······@gmail.com> writes:

> Thank you guys! I think you caught me so I have to lay my cards on the
> table. I want continuations! I want to do web programming with
> continuations but in Lisp. 

Take a look at http://common-lisp.net/project/ucw/

-- 
Alan Shutko <···@acm.org> - I am the rocks.
"The male body needs sex at all times... it's a living hell."
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107885613.500914.91090@f14g2000cwb.googlegroups.com>
Alan,

Thanks for the pointer, I'll take a look. I did not mean I want to do
web programming per se. I just see that the methodology of what I'm
trying to do (sequential processing of card game logic) is the same as
it requires asynchronous input as well as saving the stack.

    Thanks, Joel
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107885828.882502.196610@g14g2000cwa.googlegroups.com>
Also, I need to emphasize that I _do not_ need backtracking.
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107895290.444221.230820@o13g2000cwo.googlegroups.com>
To put some closure to the discussion (pun intended!)... Franz support
came through for me again. After about 20 messages between Duane Rettig
and I over the course of 4 hours my answer appeared on page 93 of PG's
OnLisp PDF (p80 of the book) where he describes a network compiler.

I originally started doing my logic using a state machine based on CLOS
and this could be what's killing me. PG's approach is much simpler. He
uses closures to represent states and links those closures dynamically
by compiling the network.

Card game logic can be represented with a network and the this network
can be  built just before a game starts since the number of players and
the game type are known at that point. The network representing game
logic should not capture any data so as to be reused.

There can be between 2 and 10 players at each table so the 9 networks
for each game type can be built in advance. There will be as many
states as in each round as there are players since every player has to
act. Each subsequent state can check the previous bet amount and other
information stored in the context.

The parameters into each state/closure will be the event, event
arguments and context and each state would return the following state
depending on how the arguments check out.

I think I'm done :-)!

    Thanks, Joel
From: Kaz Kylheku
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107912420.640699.202470@z14g2000cwz.googlegroups.com>
Joel Reymont wrote:
> and this could be what's killing me. PG's approach is much simpler.
He
> uses closures to represent states and links those closures
dynamically
> by compiling the network.

[ snip ]

> I think I'm done :-)!
>
>     Thanks, Joel

Not so fast, that was just the prototype! Boss wants it in C++ by next
Monday.

:)
From: William D Clinger
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107901743.605695.195980@z14g2000cwz.googlegroups.com>
Joel Reymont wrote:
> How would I achieve this with Lisp? I'm gonna go back to OnLisp
> and see if coding in CPS is what I need.

It sounds like the good folks here on comp.lang.lisp have
pointed you toward at least two good solution frameworks.
If you had to do it from scratch, CPS would be one way to
do it, but you wouldn't have to write your entire program
in CPS; you'd just have to identify the program points at
which the conceptual control passes between the server and
the client, and make sure that the conceptual continuation
at each such point is represented by a Lisp closure.

Have fun.

Will
From: Thomas F. Burdick
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <xcv1xbnii3p.fsf@conquest.OCF.Berkeley.EDU>
"William D Clinger" <··········@verizon.net> writes:

> Joel Reymont wrote:
> > How would I achieve this with Lisp? I'm gonna go back to OnLisp
> > and see if coding in CPS is what I need.
> 
> It sounds like the good folks here on comp.lang.lisp have
> pointed you toward at least two good solution frameworks.
> If you had to do it from scratch, CPS would be one way to
> do it, but you wouldn't have to write your entire program
> in CPS; you'd just have to identify the program points at
> which the conceptual control passes between the server and
> the client, and make sure that the conceptual continuation
> at each such point is represented by a Lisp closure.
> 
> Have fun.

This is the way to go, IMO.  How comfortable it is to switch to CPS
style here and there depends on what style you normally use.  But, if
your code looks anything like mine (lots and lots of labels), it's
pretty painless to write your control operations as functions that
take one or more continuations.  Of course, these are one-use
continuations, but that's generally all you need.
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1108136527.997056.251640@o13g2000cwo.googlegroups.com>
I decided in the end to go with a state machine approach where every
state is a  closure or a simple function. The reason for this is that I
can write separate test cases for each state whereas I'm not sure how
to test code based on continuations.

BTW, with the CPS transformer built into UCW you can code as you
normally would.

Marco Baringer sent me this code:

(defun play-texas-hold-em ()
 (with-call/cc
   (let ((players '())
         (effective-players '()))
     (dotimes (num-players (ask-number-of-players))
       (push (get-player-info) players))
     (dolist (player players)
       (when (ask-for-ante +ante+)
         (decf (wallet player) +ante+)
         (incf *pot* +ante+))
         (push player effective-players))
     (deal-blind)
     (dolist (player players)
       (ask-for-bet player))
     ;; flop
     (deal 2)
     (dolist (player players)
       (ask-for-bet player))
     ;; turn
     (deal 1)
     (dolist (player players)
       (ask-for-bet player))
     ;; river
     (deal 1)
     (dolist (player players)
       (ask-for-bet player))
     (show-all-cards))))

I want to check the results at each stage of processing so I would need
to separate each "stage" (look for code between dolist-s above) into a
separate function with a matching test case. Processing stages then
become states except I run through them sequentially, instead of
switching from one state to another based on events. I suppose I would
go with continuations if I needed backtracking but I do not.

    Thanks, Joel
From: Andras Simon
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <vcd8y5ydbn8.fsf@csusza.math.bme.hu>
"Joel Reymont" <······@gmail.com> writes:

> Thank you guys! I think you caught me so I have to lay my cards on the
> table. I want continuations! I want to do web programming with
> continuations but in Lisp. It doesn't matter that mine is a poker game,
> the goal is exactly the same.

"Joel Reymont" <······@gmail.com> writes:
 
> Thanks for the pointer, I'll take a look. I did not mean I want to do
> web programming per se. I just see that the methodology of what I'm
> trying to do (sequential processing of card game logic) is the same as
> it requires asynchronous input as well as saving the stack.
 
You can have with-call/cc and call-cc (if you're into that kind of
thing) without the rest of ucw. They're part of arnesi, a library that
comes with ucw.

Andras
From: Joel Reymont
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <1107898858.924866.34490@g14g2000cwa.googlegroups.com>
Andras,

How is the performance of that code? Any hits due to CPS transforms?

    Thanks, Joel
From: Andras Simon
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <vcd3bw6d6vm.fsf@csusza.math.bme.hu>
"Joel Reymont" <······@gmail.com> writes:

> Andras,
> 
> How is the performance of that code? Any hits due to CPS transforms?

No idea. The parenthetical "if you're into that kind of thing" was
meant to convey that I'm not (or not yet, anyway).

Andras

> 
>     Thanks, Joel
From: Wade Humeniuk
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <Q96Od.30964$L_3.18833@clgrps13>
Joel Reymont wrote:

> How do I create a closure of "where I am right now" whenever I ask for
> a bet and let the function continue from that point on once a bet
> arrives?
> 

Something like this,

(defun betting-loop (round players queue network-connection)
   (handler-case
       (let ((betting-function (start-betting round players 
network-connection)))
         (loop for betting-event = (get-betting-event queue)
           do (multiple-value-bind (player bet next-betting-function)
                  (funcall #'betting-function betting-event)
                (update-round round player bet)
                (setf betting-function next-betting-function))))
     ((betting-complete () (return-from betting-loop round)))))

(defun start-betting (round players network-connection)
   (unless players (signal (make-condition 'betting-complete)))
   (let ((player (car players)))
     (ask-player-to-bet player network-connection)
     (lambda (event)
       (cond
        ((and (isa-bet-p event) (proper-amount-bet event))
         (values player bet
                 (start-betting round
                                (cdr players network-connection)
                                network-connection)))
        (...))))) ; other conditions

Wade
From: Rahul Jain
Subject: Re: Please help me express myself ... with closures
Date: 
Message-ID: <87hdkkgrwv.fsf@nyct.net>
"Joel Reymont" <······@gmail.com> writes:

> I would like to somehow create a closure representing the state of my
> function [...]

If you need to represent state abstractly, you're better off using OOP. 
You can even encode the states as classes and use CHANGE-CLASS to go
from one state to another, defining legal operations at one state using
defmethod. The unspecialized method could simply notify the user that
the action is not legal at this time.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist