#include #include #include "kalaha.h" /* Allocate and init a new Kalaha game. */ kalaha_state * kalaha_create (int start_player) { int i; kalaha_state * state = malloc (sizeof (kalaha_state)); assert (state); for (i = 0; i<14; ++i) { if ((i == A7) || (i == B7)) { state->holes[i] = 0; } else { state->holes[i] = 6; } } assert ((start_player == PLAYERA) || (start_player == PLAYERB)); state->player = start_player; return state; } /* Check if more moves are possible. * Return Values: * 1 No more moves possible * 0 More moves possible */ int kalaha_end (kalaha_state * state) { int min, max; switch (state->player) { case PLAYERA: min = A1; max = A6; break; case PLAYERB: min = B1; max = B6; break; default: assert (0); } for (; min <= max; ++min) { if (state->holes[min]) return 0; } state->player = PLAYERNONE; return 1; } /* Collect the remaining pieces into A7 or B7 if no more moves are possible. */ void kalaha_collect (kalaha_state * state) { int i; for (i=A1; i<=A6; ++i) { state->holes[A7] += state->holes[i]; state->holes[i] = 0; } for (i=B1; i<=B6; ++i) { state->holes[B7] += state->holes[i]; state->holes[i] = 0; } state->player = PLAYERNONE; } /* Make a kalaha move. Number is an integer in the range 1..6. * Which hole is emptied depends on the current player (state->player) * Return Values: * 0: Illeagal move * 1: Ok. */ int kalaha_move (kalaha_state * state, int number) { int start, count, skip, my, mymin, mymax; if ((1 > number) || (number > 6)) return 0; switch (state->player) { case PLAYERA: start = A1-1+number; skip = B7; my = A7; mymin = A1; mymax = A6; break; case PLAYERB: start = B1-1+number; skip = A7; my = B7; mymin = B1; mymax = B6; break; default: assert (0); } if (state->holes[start] == 0) return 0; count = state->holes[start]; state->holes[start] = 0; for (; count; count--) { do { start++; start %= 14; } while (start == skip); state->holes[start]++; } /* Last piece in one of our holes an that hole was previously empty? */ if ((start >= mymin) && (start <= mymax ) && (state->holes[start] == 1)) { state->holes[my] += state->holes[start]; state->holes[start] = 0; state->holes[my] += state->holes[12-start]; state->holes[12-start] = 0; } if (start != my) { state->player = 1-state->player; } return 1; } /* Evaluate the situation from the perspective of the current player. * The return value measures the goodness of the current situation. * If *bestmove will suggest a move that is considered best. * depth is the number of moves that we look ahead in order to * evaluate the current situation. A good value to use here is 6. * Larger values will leed to more accurate evaluation but also to * significantly increased calculation times. Increasing the value of * depth by one will increase the calculation time by a factor of 5-10. */ int Score (kalaha_state * state, int * bestmove, int depth) { int best = -100, i, score; kalaha_state * tmp; if (bestmove) (*bestmove) = -1; if (!depth) { switch (state->player) { case PLAYERA: return state->holes[A7] - state->holes[B7]; break; case PLAYERB: return state->holes[B7] - state->holes[A7]; break; default: assert (0); } } tmp = malloc (sizeof (kalaha_state)); assert (tmp); for (i=1; i<=6; ++i) { memcpy (tmp, state, sizeof (kalaha_state)); if (!kalaha_move (tmp, i)) continue; if (tmp->player == state->player) { score = Score (tmp, NULL, depth); } else { score = - Score (tmp, NULL, depth - 1); } if (score > best) { best = score; if (bestmove) (*bestmove) = i; } } if (best == -100) { memcpy (tmp, state, sizeof (kalaha_state)); kalaha_collect (tmp); switch (state->player) { case PLAYERA: best = tmp->holes[A7] - tmp->holes[B7]; break; case PLAYERB: best = tmp->holes[B7] - tmp->holes[A7]; break; default: assert (0); } } free (tmp); return best; }