bug-gnubg
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Backgammon Protocol


From: Frank Berger
Subject: Backgammon Protocol
Date: Sun, 26 Nov 2023 19:02:30 +0100

Hi all,

> what do you mean with "you should do error handling for http“?
> 
> You only have to handle the errors the API specify. My API draft specifies 
> exactly two error codes:
> - 400 for client errors like „opponent has 16 checkers on the board“
> - 500 for server errors like „could not find neural network files“
I may be bit pedantic, but http has a lot of features. If some sends a correct 
request or response and it doesn’t get handled properly I don’t like that.
If I ignore everything and only look for the error code, why use http in the 
first place? What value http adds what we don’t get with pure streams? 

> 
> Every other error code returned by the server is then basically an 
> implementation bug.
> 
> Those errors need to be handled anyway. I believe HTTP and it’s libraries 
> make this super easy to implement, otherwise you had to reinvent it.
Reinvention are about 10 lines of code (o.k. maybe 20). And if I have two way 
to communicate an error either by status code or in the body doesn’t look right 
to me.

> 
> I also believe that there should be two mechanisms. The binary one could be 
> simple C function calls, similar to what Øystein wrote in his email. Pure 
> functions preferred.
> 
> Something in that direction exists in wildbg:
> https://github.com/carsten-wenderdel/wildbg/blob/main/docs/user/wildbg-c.md
> https://github.com/carsten-wenderdel/wildbg/blob/main/crates/wildbg-c/wildbg.h
I would delay that until we have a clearer picture what we need. Nevertheless I 
would strongly prefer somethings that is not tied to a language.

> 
> This could also be the first step for GnuBG. The engine and the GUI could 
> communicate via those C calls, no new library needed, everything compiled to 
> one binary.
> External developers could then be able to compile GnuBG without the GUI and 
> its GTK dependencies and use it as a C library.
> 
> Around this C library we could then write a wrapper with a text based 
> protocol. As so many languages support interfacing with C, we could even pick 
> the language here.
> 
> I don’t believe in two text based protocols building on top of each other. If 
> there is a telnet/string based one and a HTTP based one, they would be two 
> different protocols.
> If both had to be implemented, the implementations would be independent from 
> each other.
I believe that the typical use case will be on a local machine and stdin/stdout 
has a lot of advantages, e.g. you can start several instances without conflicts.
When you have a port, you must negotiate the port. What if there are two 
clients? What if two servers? What if two version of the same server?
I don’t believe that the average user even understands what a port is. And 
creating a server socket usually brings up a warning from the firewall, at 
least some people might be irritated.
That all vanishes with stdin/stdout.

Nevertheless it is a charming idea not to have install something, just enter an 
URL and try something out that is not on my computer.
But as Guido already pointed out: it would be trivial to forward from 
stdin/stdout to a server.

What I don’t understand is why there should be different protocols for raw 
network (socket, not telnet or ssh) vs. stdin/stdout?
In both cases I send either text and read text (let’s say JSON or its binary 
counterpart).

> 
> There is a lot we seem to have in common with our ideas. Is this right?
> - It’s a good idea to separate engine and GUI.
> - It should be a server/client model, so the GUI asks, the engine answers.
> - Stateless, no match information in the engine.

I think that is out of question.


As a proposal for a protocol see the following stuff. It is part from the 
protocol that is used by BGBlitz server. I cut off some parts that make no 
sense here, and what’s missing is feature negotiation.
Due to the customer demand their might be further things to be removed (e.g. to 
handle droppers the move number is given ). It is both XML and JSON, I just 
kept the XML stuff to avoid accidentally delete some info, I wont suggest XML. 
Given that this protocol has served about 20 customers some of them large, it 
probably contains what we need, it might just be trimmed down a bit.

The Evaluation Request is probably not important, because it s mostly about 
evaluation a position and calculating payments if a game is interrupted.
The TutorRequest gives back a cube decision or a list of moves, depending 
whether dice are set. I don’t expect you to read that, just scroll down to see 
the requests and if you think it looks reasonable you may read the fine print.

If there is some acceptance I would rewrite the spec to JSON only and think 
about better names in this context and what is necessary. I would also provide 
some example code, maybe it is not daily business for one or the other to use 
sockets, but it is not much more difficult than handling a file.

ciao
Frank



The Evaluation Request
======================
the evaluation request gets a position, described by the given parameters. A 
position might be 
from money game or a match, the dice may be given. 

The EvalRequest may contain an id-attribute. It is optional (and only in the 
response if present in the request) 
and may be used to match request and response. 

Explanation of the tags:

- <comment> a comment can be added to any request. This may be helpful for 
debugging purposes. 
            This tag is optional.

- <attr name="cubeful" value="true"/>  Shall BGBlitz use cubeful or cubeless 
evaluations for
            this request. Cubeful evaluations have the advantage, that they 
reflect the reality
            more precise, so a Dropper can't exploit the settlement algorithm 
in any way. The 
            advantage of cubeless equity is, that the player can easily 
calculate the values so
            it might lead to less calls at the hotline.                  
            This tag is optional; if missing the value is taken from the setup 
            
- <attr name="ply" value="2"/>  the number of half moves BGBlitz evaluates. 
Valid values are 1,2 
            and 3. On a regular PC BGBlitz evaluates more than 100.000 
positions per second. Each additional ply costs
            about the factor of 30.  E.g. 3-ply with no dice costs around 1 
second. I recommend 2-ply as default. Even 
            on 1-ply the evaluations will be very good.           
            This tag is optional; if missing the value is taken from the setup 
           
- <attr name="limit" value="5"/>  A limit may be set for money games, i.e. the 
maximum amount 
            that can be lost in a single money game. This tag is optional.

- <attr name="basicBet" value="1"/>  The monetary amount played for. This is 
paid if a single 
            game with the doubling cube on 1 was played.
            
- <attr name="gammons" value="on"/>  Shall gammons count? Values "on" (default) 
and "off".
                                     This tag is optional.
                                     
- <attr name="backgammons" value="off"/> Shall backgammons count? Values "on" 
(default) and "off".
                                     This tag is optional.
                                     
- <attr name="MoveNumber" value="1"/>  A special and important parameter is the 
"MoveNumber": 
            Imagine in a post Crawford game, the trailer will always double. 
But what happens 
            if the leading player drops e.g. at a score -1:-2? The normal 
evaluation don't 
            take into account that a double will follow, so the dropping player 
has an 
            incentive to drop. Therefore if the move number is < 4 (I assume 
the trailer forgets 
            to double if not doubled after 3 moves) BGBlitz treats it as if the 
double has been 
            made. This tag is optional, but highly recommended.

  
- <position>                             describes the complete position
      
- <dice red='0' green='0'/>              the dice for the red and green player 

- <score red='0' green='0'/>             the score in a match. Optional in 
money game; 
                                         mandatory in match 

- <attr name='matchLength' value='0'/>   The length of a match. If matchLength 
is 0 then it 
                                         is regarded as a a money game.
            
- <attr name='whosOn' value='green'/>    Who's turn is it? Values are red, 
green. Mandatory

- <attr name='crawford' value='false'/>  In matchplay. Is this the Crawford 
game?

- <attr name='usecube' value='true'/>    Shall the doubling cube be used? 
Optional.

- <attr name="jacoby" value="true"/>     Usually only in money games. Is the 
Jacoby 
                                         rule enabled (i.e. undoubled 
gammons/backgammons 
                                         don't count)?
                                         
- <attr name='board'>                    describes the positions physical board

- <board cube='1' cubeowner='none'>      who owns the cube? cube owner may be 
red, green or none. 
                                         What value shows the cube. cube may be 
1, 2, 4, ...

- <points>-2,-2,-2,-2,-3,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0</points> The 
checkers.
                                         "green" checkers are coded as negative 
integers, "red" 
                                         checkers as positive integers. points 
starts with greens 
                                         1 point resp. reds 24 point. bar and 
off are encoded
                                         separately.

- <bar red='1' green='0'/>               Checkers on the bar. Optional if no 
checkers are on 
                                         the bar. The same encoding as in 
points is used, 
                                         so green checkers are negative.
                                         
- <off red='9' green='-2'/>              Checkers played off. Optional if all 
checkers are in 
                                         play. The same encoding as in points 
is used, 
                                         so green checkers are negative.

If a parameter isn't set, reasonable defaults are taken.

An complete request:
    <?xml version="1.0" encoding="UTF-8" ?>
    <EvalRequest id='1234ab'>
      <comment>A comment describing the request, just for debugging</comment>
      <attr name="cubeful" value="true"/>                     
      <attr name="ply" value="2"/>                     
      <attr name="limit" value="5"/>                   
      <attr name="basicBet" value="1"/>              
      <attr name="gammons" value="on"/>
      <attr name="MoveNumber" value="1"/>
      <attr name="backgammons" value="off"/>
      <position>
        <dice red='0' green='0'/>
        <score red='0' green='0'/>
        <attr name='matchLength' value='0'/>           
        <attr name='whosOn' value='green'/>
        <attr name='crawford' value='false'/>
        <attr name='usecube' value='true'/>
        <attr name="jacoby" value="true"/>             
        <attr name='board'>
          <board cube='1' cubeowner='none'>            
            
<points>-2,-2,-2,-2,-3,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0</points>
            <bar red='1' green='0'/>                   
            <off red='9' green='-2'/>                  
          </board>
        </attr>
      </position>
    </EvalRequest>
    
    or
    { 
    "type":"EvalRequest",
    "id":"1234ab",
    "comment":"A comment describing the request, just for debugging",
    "basicBet":1,
    "cubeful":true,
    "ply":2,
    "limit":5,
    "MoveNumber":1,
    "gammons":"on",
    "backgammons":"off",
    "position": {
      "matchLength":0,
      "whosOn":"green",
      "usecube":true,
      "jacoby":true,
      "crawford":false,
      "dice":  { "red":0, "green":0 },  
      "score": { "red":0, "green":0 },  
      "board" : {
        "cube":2, 
        "cubeowner":"red",
        "points": [-2,-2,-2,-2,-3,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0],
        "bar": { "red":1, "green":0 }
        "off": { "red":9, "green":-2 }
      }
    }}
    

response:

Explanation of the tags:

- <probabilities  redwin="13.4" redwing="0.1" redwinbg="0.0" greenwin="86.6" 
greenwing="11.3" greenwinbg="0.2" />
   redwin:     The probability that red wins in percent.
   redwing:    The probability that red wins a gammon or backgammon in percent.
   redwinbg:   The probability that red wins a backgammon in percent.
   greenwin:   The probability that green wins in percent.
   greenwing:  The probability that green wins a gammon or backgammon in 
percent.
   greenwinbg: The probability that green wins a backgammon in percent.
   
   Look out: The gammons and backgammons are included in the wins. The 
backgammons are included in the gammons.
   greenwin and redwin add up to 100.0.
     
- <MWP red="42.2" green="57.8"> In the case of a match gives the probability of 
the match win in percent.

- <amountWon red="-0.78" green="0.78"/> Contains the amount the players 
get/have to pay in currency if the
           game would be interrupted in this moment. This value can be used 
directly, just the rake has to be added.

An complete request:
<?xml version="1.0" encoding="UTF-8" ?>
<EvalResult id='1234ab'>
  <probabilities  redwin="13.4" redwing="0.1" redwinbg="0.0" greenwin="86.6" 
greenwing="11.3" greenwinbg="0.2" />
  <MWP red="42.2" green="57.8”/>        <!-- in the case of a match -->
  <EMG green="-0.123" red="0.123"/>     <!-- in the case of a match && EMG is 
set-->
  <amountWon red="-0.78" green="0.78"/>
</EvalResult>

or

{ 
  "type":"EvalResult",
  "probabilities": {"greenWin":0.442, "greenWinG":0.099, "greenWinBG":0.004, 
"redWin":0.558, "redWinG":0.145, "redWinBG":0.006 },
  "MWP":{"green":0.155,"red":0.845},
  "EMG": {"green":0.270, "red":-0.270}
  "amountWon" : {  "green":-0.69, "red":0.69  }
}

in the case of an error an error response is send:

<?xml version="1.0" encoding="UTF-8" ?>
<Error>
  unstructured text containing an explanation what went wrong.
</Error>

or

{"type":"Error","message":"unstructured text"}


The Tutor Request
=================
The tutor request is similar to the EvalRequest. When the dice are given, a 
list 
of best moves with their evaluation is given back.
When no dice is given, the current evaluation is given and if the cube is 
usable, 
with the appropriate cube action. Only the attributes different to the 
EvalRequest 
are described here.

- <TutorRequest id='1234ab'> The starting tag is different.
        The id-attribute is optional (and only in the response if present in 
the request) 
        and may be used to match request and response. 

- <attr name="noOfMoves"   value="5"/>  How many moves should be returned? Any 
value greater 0 is valid.

- <attr name="beaver" value="true"/>    Are beavers allowed?

- <attr name="noise" value="0.05"/> If you use BGBlitz server as opponent, 
BGBlitz is even with 1-ply 
much to strong for most opponents. Therefore you can add some noise to the 
evaluations. 
Values might be in the range between 0.05 up to 0.5. Greater values mean a 
weaker level of play.
The noise tag is optional.

- <attr name="longCubeDecision"  value="true"/> shall the equity values for the 
possible
   cube decisions are shown (only if not a mandatory double etc.)?

- <attr name="ply" value="2"/>  different than in the evaluation request, you 
can use ply = 0 here.
   ply=0 selects the weak AI
    
request:
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <TutorRequest id='1234ab'>
      <comment>A comment describing the request, just for debugging</comment>
      <attr name="noise" value="0.05"/>
      <attr name="cubeful" value="true"/>                     
      <attr name="gammons" value="on"/>
      <attr name="backgammons" value="off"/>
      <attr name="noOfMoves"   value="5"/>        <!-- any value greater 0 -->
      <attr name="ply" value="2"/>                <!-- optional; if missing 
taking the value from setup -->
      <position>
        <dice red='0' green='0'/>
        <score red='0' green='0'/>
        <attr name='matchLength' value='0'/>       <!-- if matchLength is 0 
then it is a money game -->
        <attr name='whosOn' value='green'/>
        <attr name='crawford' value='false'/>
        <attr name='usecube' value='true'/>
        <attr name="jacoby" value="true"/>
        <attr name="beaver" value="true"/>
        <attr name='board'>
          <board cube='1' cubeowner='none'>       <!-- cubeowner may be 
red,green or none -->
            
<points>-2,-2,-2,-2,-3,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0</points>
            <bar red='1' green='0'/>              <!--optional if no checker on 
the bar -->
            <off red='9' green='-2'/>             <!--optional if no checker 
off -->
          </board>
       </attr>
     </position>
   </TutorRequest>

or

{
"type":"TutorRequest",
"id":"yz234",
"comment":"A comment describing the request, just for debugging<",
"noise":0.05,
"cubeful":true,  
"gammons":"on",
"backgammons":"on",
"noOfMoves":"3",
"ply":2,
  "position": {
    "whosOn":"red",
    "usecube":true,
    "crawford":true,
    "jacoby":true,    
    "dice":  { "red":1, "green":2},    
    "score": { "red":0, "green":0},        
    "board" : {
      "cube":2, 
      "cubeowner":"green",      
      "points": [2,0,0,0,0,-5,0,-4,0,0,0,5,-4,0,0,0,3,0,5,-1,0,0,0,-1]
    }
  }
}

   
response in the case of several moves:

- <TutorResult>   The response tag.

- <move rank="1"> rank tells the rank of the move. Not strictly necessary 
because the moves
                  always come in order from the best decreasing.

- <movePart from="8" to="5" /> A move consists out of several move parts. The 
points are 
                               always from the viewpoint of the moving player.
                               Special field names are "bar" and "off".
                               
- <probabilities>  exactly as in the evaluation response

- <moneyEquity green="0.073" red="-0.73"> The evaluation of the position it is 
a money game.

- <MWP  red="42.2" green="57.8">          The evaluation of the position it is 
a match play.


<?xml version="1.0" encoding="UTF-8" ?>
<TutorResult id='1234ab'>
  <move rank="1">
    <movePart from="8" to="5" />             <!-- points always from the 
viewpoint of the moving player -->
    <movePart from="6" to="5" />             <!-- special filed names are "bar" 
and "off" -->
    <probabilities  redwin="52.5" redwing="14.4" redwinbg="0.004" 
greenwin="47.5" greenwing="12.2" greenwinbg="0.03" />
    <moneyEquity green="0.073" red="-0.73"/> <!-- if money game -->
    <MWP  red="42.2" green="57.8"/>          <!-- if matchplay -->
    <EMG green="-0.123" red="0.123"/>        <!-- in the case of a match && EMG 
is set-->
  </move>
  <move rank="2">
    <movePart from="8" to="5" />
    <movePart from="24" to="23" />
    <probabilities  redwin="46.3" redwing="11.0" redwinbg="0.003" 
greenwin="53.7" greenwing="14.1" greenwinbg="0.04" />
    <moneyEquity green="0.059" red="-0.059"/> <!-- if money game -->
    <MWP  red="42.2" green="57.8"/>           <!-- if matchplay -->
    <EMG green="-0.123" red="0.123"/>         <!-- in the case of a match && 
EMG is set-->
  </move>
  .... up to noOfMoves
</TutorResult>

or

{ 
"type":"TutorResult",
"id":"yz234",
"move": [
{
  "rank":1
  "movePart": [ {"from":"6",  "to":"5"}, {"from":"13", "to":"11"} ],
  "probabilities": {
    "greenWin":0.518, "greenWinG":0.154, "greenWinBG":0.009, "redWin":0.482, 
"redWinG":0.118, "redWinBG":0.004 },
    "MWP": {"green":0.335, "red":0.665},
    "EMG": {"green":-0.280, "red":0.280}
    "moneyEquity": {"green":0.076, "red":-0.076}},    
{
  "rank":2,\n" +
  "movePart": [ {"from":"6",  "to":"5"}, {"from":"24", "to":"22"} ],
  "probabilities": {
    "greenWin":0.531, "greenWinG":0.138, "greenWinBG":0.006, "redWin":0.469, 
"redWinG":0.106, "redWinBG":0.004 },
    "moneyEquity": {"green":0.097, "red":-0.097}
    "EMG": {"green":0.-270, "red":0.270}
    "MWP": {"green":0.332, "red":0.668},
},
  .... up to noOfMoves


response in the case of a evaluation/cube decision:

- <cubeDecision owner='hold' other='take' />  Owner is the one, having the cube 
in access.
                                              Possible values in cube decision 
are
                                              owner: togood/hold/double/redouble
                                              other: accept/resign/beaver
                                              

<TutorResult>
    <probabilities  redwin="46.3" redwing="11.0" redwinbg="0.003" 
greenwin="53.7" greenwing="14.1" greenwinbg="0.04" />
    <moneyEquity value="0.073"/>             <!-- if money game -->
    <matchWinningProb value="0.073"/>        <!-- if matchplay -->
    <EMG green="-0.123" red="0.123"/>       <!-- in the case of a match && EMG 
is set-->
    <cubeDecision owner='hold' other='take' />
</TutorResult>

as JSON

{ 
"type":"TutorResult",
"probabilities": {
  "greenWin":0.512, "greenWinG":0.123, "greenWinBG":0.005, "redWin":0.488, 
"redWinG":0.112, "redWinBG":0.004 },
  "MWP": {"green":0.318, "red":0.682},
  "cubeDecision": {"owner":"hold", "other":"accept"}
}

or if "longCubeDecision" is set
<TutorResult>
    <probabilities  redwin="46.3" redwing="11.0" redwinbg="0.003" 
greenwin="53.7" greenwing="14.1" greenwinbg="0.04" />
    <moneyEquity value="0.073"/>           <!-- if money game -->
    <matchWinningProb value="0.073"/>      <!-- if matchplay -->
    <EMG green="-0.123" red="0.123"/>     <!-- in the case of a match && EMG is 
set-->
    <cubeDecision owner='hold' other='accept' noDouble='-0.036' 
doubleTake='-0.385' doubleDrop='1.000' />
</TutorResult>

or as JSON
{ 
"type":"TutorResult",
"probabilities": {
  "greenWin":0.512, "greenWinG":0.123, "greenWinBG":0.005, "redWin":0.488, 
"redWinG":0.112, "redWinBG":0.004 },
  "MWP": {"green":0.318, "red":0.682},
  "cubeDecision": {"owner":"double", "other":"resign", "noDouble":0.596, 
"doubleTake":0.755, "doubleDrop":0.596}
}



in the case of a problem an error response is send:

<?xml version="1.0" encoding="UTF-8" ?>
<Error>
  unstructured text
</Error>

or as JSON

{"type":"Error","message":"unstructured text"}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]