#include static PyObject* QuackError; typedef struct board_list board_list; struct board_list { int size; PyObject* list[16]; }; /* Utility functions */ int sign(int x) { return (x > 0) - (x < 0); } int abs(int x) { if (x >= 0) return x; else return -x; } /* end utility functions */ /* Helper functions */ int *idxs_with_checkers_of_player(int board[], int player) { int idxs_tmp[26]; int ctr = 0; for (int i = 0; i < 26; i++) { if (board[i] * player >= 1) { idxs_tmp[ctr] = i; ctr++; } } int *idxs = malloc((1 + ctr) * sizeof(int)); if (idxs == NULL) { PyErr_NoMemory(); abort(); } idxs[0] = ctr; for (int i = 0; i < ctr; i++) { idxs[i+1] = idxs_tmp[i]; } return idxs; } int is_forward_move(int direction, int player) { return direction == player; } int face_value_match_move_length(int delta, int face_value) { return abs(delta) == face_value; } int bear_in_if_checker_on_bar(int board[], int player, int from_idx) { int bar; if (player == 1) bar = 0; else bar = 25; if (board[bar] != 0) return from_idx == bar; else return 1; } int checkers_at_from_idx(int from_state, int player) { return sign(from_state) == player; } int no_block_at_to_idx(int to_state, int player) { if (-sign(to_state) == player) return abs(to_state) == 1; else return 1; } int can_bear_off(int board[], int player, int from_idx, int to_idx) { int* checker_idxs = idxs_with_checkers_of_player(board, player); int moving_backmost_checker = 1; int bearing_directly_off = 0; int all_checkers_in_last_quadrant = 1; /* Check if bearing directly off */ if (player == 1 && to_idx == 25) bearing_directly_off = 1; else if (player == -1 && to_idx == 0) bearing_directly_off = 1; for (int i = 1; i <= checker_idxs[0]; i++) { if (player == 1 ) { /* Check if all checkers are in last quardrant */ if (checker_idxs[i] < 19) { all_checkers_in_last_quadrant = 0; break; } /* Check if moving backmost checker */ if (checker_idxs[i] < from_idx) { moving_backmost_checker = 0; if (!bearing_directly_off) break; } } else { if (checker_idxs[i] > 6) { all_checkers_in_last_quadrant = 0; break; } if (checker_idxs[i] > from_idx) { moving_backmost_checker = 0; if (!bearing_directly_off) break; } } } free(checker_idxs); if (all_checkers_in_last_quadrant && (bearing_directly_off || moving_backmost_checker)) return 1; else return 0; } /* end helper functions */ int is_move_valid(int board[], int player, int face_value, int move[]) { int from_idx = move[0]; int to_idx = move[1]; int to_state; int from_state = board[from_idx]; int delta = to_idx - from_idx; int direction = sign(delta); int bearing_off; if (to_idx >= 1 && to_idx <= 24) { to_state = board[to_idx]; bearing_off = 0; } else { to_state = 0; bearing_off = 1; } return is_forward_move(direction, player) && face_value_match_move_length(delta, face_value) && bear_in_if_checker_on_bar(board, player, from_idx) && checkers_at_from_idx(from_state, player) && no_block_at_to_idx(to_state, player) && (!bearing_off || can_bear_off(board, player, from_idx, to_idx)) ; } void do_move(int board[], int player, int move[]) { int from_idx = move[0]; int to_idx = move[1]; /* "lift" checker */ board[from_idx] -= player; /* Return early if bearing off */ if (to_idx < 1 || to_idx > 24) return; /* Hit opponent checker */ if (board[to_idx] * player == -1) { /* Move checker to bar */ if (player == 1) board[25] -= player; else board[0] -= player; board[to_idx] = 0; } /* Put down checker */ board[to_idx] += player; return; } int* do_move_clone(int board[], int player, int move[]) { int* new_board = malloc(sizeof(int) * 26); if (new_board == NULL) { PyErr_NoMemory(); abort(); } for (int i = 0; i < 26; i++) { new_board[i] = board[i]; } do_move(new_board, player, move); return new_board; } PyObject* store_board_to_pytuple(int board[], int size) { PyObject* board_tuple = PyTuple_New(size); for (int i = 0; i < size; i++) { PyTuple_SetItem(board_tuple, i, Py_BuildValue("i", board[i])); } return board_tuple; } board_list calc_moves(int board[], int player, int face_value) { int* checker_idxs = idxs_with_checkers_of_player(board, player); board_list boards = { .size = 0 }; if (checker_idxs[0] == 0) { boards.size = 1; PyObject* board_tuple = store_board_to_pytuple(board, 26); boards.list[0] = board_tuple; free(checker_idxs); return boards; } int ctr = 0; for (int i = 1; i <= checker_idxs[0]; i++) { int move[2]; move[0] = checker_idxs[i]; move[1] = checker_idxs[i] + (face_value * player); if (is_move_valid(board, player, face_value, move)) { int* new_board = do_move_clone(board, player, move); PyObject* board_tuple = store_board_to_pytuple(new_board, 26); // segfault maybe :'( free(new_board); boards.list[ctr] = board_tuple; ctr++; } } free(checker_idxs); boards.size = ctr; return boards; } int* board_features_quack_fat(int board[], int player) { int* new_board = malloc(sizeof(int) * 30); if (new_board == NULL) { PyErr_NoMemory(); abort(); } int pos_sum = 0; int neg_sum = 0; for (int i = 0; i < 26; i++) { new_board[i] = board[i]; if (sign(new_board[i] > 0)) pos_sum += new_board[i]; else neg_sum += new_board[i]; } new_board[26] = 15 - pos_sum; new_board[27] = -15 - neg_sum; if (player == 1) { new_board[28] = 1; new_board[29] = 0; } else { new_board[28] = 0; new_board[29] = 1; } return new_board; } /* Meta definitions */ int extract_board(int *board, PyObject* board_tuple_obj) { long numValuesBoard; numValuesBoard = PyTuple_Size(board_tuple_obj); if (numValuesBoard != 26) { PyErr_SetString(QuackError, "Board tuple must have 26 entries"); return 1; } PyObject* board_val_obj; // Iterate over tuple to retreive positions for (int i=0; i