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 :
as if it was a list. (numbers are separated by a space)
Here is the tcl wrapping :
# 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 !