Critcl example with the Mancala game

Sarnold 2005-09-02 - After playing some time with Mancala, I improved the AI, but did encounter some perf issues.

So I decided to write the most used function in C with Critcl.

Here is the C procedure for the move :

 critcl::cproc cmove {char* vboard int player int pit} string {
     int go_again=0;
     int side;
     int board26;
     int store2;
     int i;
     int orig_side,orig_pit,their_pit,opp;
     int stones=0;
     char temp;
     int walk=0; /* to walk through the result */
     char error10="error";
     char result45+1; /* 12 for the holes, 2 for the stores, 1 for go_again and 1 null-termination */
     
     /* initialization of the board */
     orig_side=side=player;
     orig_pit=pit;
     i=walk=0;
     while (vboardi!='\0') {
         temp=0;
         while (vboardi!=' '&& vboardi!='\0') {
             if (vboardi<'0'||vboardi>'9') {
                 return "error";
             }
             temp=temp*10+vboardi-'0';
             i++;
             if (temp>36) {
                 return "error";
             }
         }
         if (walk<12) {
             boardwalk/6walk%6=(int) temp;
         } else  {
             store13-walk=(int) temp;
         }
         walk++;
         i++;
         if (walk>13) {
             break;
         }
     }
     for (i=0;i<sizeof(result);i++) {resulti='\0';}
     /* initialization ending */
     /* begin moving */
     stones=boardsidepit;
     if (stones == 0) {
         /* for debugging */
         /* error5=vboardside*6+pit;
         error6='a'+(char)(side*6+pit); */
         return "error";
     }
     pit++;
     while (stones > 0) {
         /* decrement the origin from one stone */
         stones--;
         boardorig_sideorig_pit--;
         if (pit >= 6) {
             /* in that case we put a stone into one of the 2 stores. */
             storeside++;
             /* when we put the last stone into the store of the opponent,
             ** it should be the turn of the opponent */
             go_again= (side==player || stones!=0);
             pit=0;
             side=!side;
         } else {
             go_again =0;
             i = ++boardsidepit;
             /* See if we captured any opponent stones */
             if (stones==0 && player==side && i == 1) {
                 opp=!side;
                 their_pit=5-pit;
                 /* capture_opposite */
                 if (boardopptheir_pit!=0) {
                     storeside+=1+boardopptheir_pit;
                     boardsidepit=0;
                     boardopptheir_pit=0;
                 }
                 break;
             }
             pit++;
         }
     }
     /* converting board, go_again and store for returning a string list-like */
     temp=(char)go_again;
     if (temp>10) {
         return "error";
     }
     temp='0'+temp;
     result0=temp;
     walk=1;
     for (i=0;i<14;i++) {
         if (i<12) {
             temp=(char)boardi/6i%6;
         } else  {
             temp=(char)store13-i;
         }
         resultwalk=' ';
         walk++;
         if (temp>99) {
             return "error";
         }
         if (temp>=10) {
             resultwalk='0'+(temp/10);
             walk++;
             temp=temp%10;
         }
         resultwalk='0'+temp;
         walk++;
     }
     resultwalk='\0';
     return result;
 }

It takes as arguments : the board, which is a list of numbers converted to a string (char* type), and two integers : side and go_again, both booleans. The result returned is a string containing :

  • go_again at index 0
  • the modified board from index 1 to end

as if it was a list. (numbers are separated by a space)

Here is the tcl wrapping :

  • into the move proc :
 # cmove is our C proc
 set res [cmove $board $player $pit]
 if {[string equal $res error]} {
     error "error in move"
 }
 return [list [lindex $res 0] [lrange $res 1 end]]

Of course I created a similar cproc which simulates the other game mode.



Compiling issues

Having MinGW/MSys on my WindowsME machine, what was needed for compiling under the DOS console is :

 set PATH=%PATH%;d:\mingw\bin;d:\tcl\bin
 tclsh critcl.kit -lib cmancala cmancala.tcl

, where cmancala.tcl is our cproc definition. Then to access the cmove proc :

 tclsh
 % load cmancala.dll
 % cmove [split 33333333333300 ""] 0 0
 0 0 4 4 4 3 3 3 3 3 3 3 3 0 0

That's it !