Version 1 of Critcl example with the Mancala game

Updated 2005-09-02 07:17:04

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 board[2][6];
     int store[2];
     int i;
     int orig_side,orig_pit,their_pit,opp;
     int stones=0;
     char temp;
     char error[10]="error";
     char result[15+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;
     for (i=0;i<14;i++) {
         temp=vboard[i];
         if (temp>='0' && temp<='9') {
             temp=temp-'0';
         } else  {
             temp=temp+10-'a';
         }
         if (i<12) {
             board[i/6][i%6]=(int) temp;
         } else  {
             store[13-i]=(int) temp;
         }
     }
     for (i=0;i<16;i++) {result[i]='\0';}
     stones=board[side][pit];
     if (stones == 0) {
         /* for debugging */
         /* error[5]=vboard[side*6+pit];
         error[6]='a'+(char)(side*6+pit); */
         return "error";
     }
     pit++;
     while (stones > 0) {
         /* decrement the origin from one stone */
         stones--;
         board[orig_side][orig_pit]--;
         if (pit >= 6) {
             /* in that case we put a stone into one of the 2 stores. */
             store[side]++;
             /* 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 = ++board[side][pit];
             /* See if we captured any opponent stones */
             if (stones==0 && player==side && i == 1) {
                 opp=!side;
                 their_pit=5-pit;
                 /* capture_opposite */
                 if (board[opp][their_pit]!=0) {
                     store[side]+=1+board[opp][their_pit];
                     board[side][pit]=0;
                     board[opp][their_pit]=0;
                 }
                 temp=(char)go_again;
                 temp=(temp<10)?'0'+temp:'a'-10+temp;
                 result[0]=temp;
                 for (i=0;i<14;i++) {
                     if (i<12) {
                         temp=(char)board[i/6][i%6];
                     } else  {
                         temp=(char)store[13-i];
                     }
                     result[i+1]=(temp<10)?'0'+temp:'a'-10+temp;
                 }
                 return result;
             }
             pit++;
         }
     }

     /* converting board, go_again and store for returning a base-36 string */
     temp=(char)go_again;
     temp=(temp<10)?'0'+temp:'a'-10+temp;
     result[0]=temp;
     for (i=0;i<14;i++) {
         if (i<12) {
             temp=(char)board[i/6][i%6];
         } else  {
             temp=(char)store[13-i];
         }
         result[i+1]=(temp<10)?'0'+temp:'a'-10+temp;
     }
     return result;
 }

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

  • go_again, converted to base-36
  • the modified board, appended as a string

Here is the tcl wrapping :

  • into the global level:
 set t "0123456789abcdefghijklmnopqrstuvwxyz"
 for {set i 0} {$i<36} {incr i} {
     set coding([string index $t $i]) $i
 }
  • into the move proc :
 set bvalue ""
 set code "0123456789abcdefghijklmnopqrstuvwxyz"
 foreach i $board {
     append bvalue [string index $code $i]
 }
 # cmove is our C proc
 set res [cmove $bvalue $player $pit]
 if {[string equal $res error]} {
     error "error in move"
 }
 set go_again [string first [string index $res 0] $code]
 set l [string length [set res [string range $res 1 end]]]
 for {set i 0} {$i<$l} {incr i} {
     lset board $i $::coding([string index $res $i])
 }
 return [list $go_again $board]

Compiling issues

Having MinGW/MSys on my WindowsME machine, it was needed for compiling that I was in the Msys environment. So I ran Msys and launch this command :

 /d/tcl/bin/tclsh.exe critcl.kit -force -lib cmancala cmancala.tcl

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

 tclsh
 % load cmancala.dll
 % cmove 33333333333300 0 0
 004443333333300

That's it !


Category Critcl