242 lines
5.5 KiB
C
242 lines
5.5 KiB
C
#include <Python.h>
|
|
|
|
static PyObject* QuackError;
|
|
|
|
/* 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) {
|
|
fprintf(stderr, "malloc failed\n");
|
|
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* checker_idxs = idxs_with_checkers_of_player(board, player);
|
|
|
|
if (player == 1) {
|
|
for (int i = 1; i <= checker_idxs[0]; i++) {
|
|
if ( !((checker_idxs[i] >= from_idx) &&
|
|
(checker_idxs[i] >= 19)) ) return 0;
|
|
}
|
|
} else {
|
|
for (int i = 1; i <= checker_idxs[0]; i++) {
|
|
if ( !((checker_idxs[i] <= from_idx) &&
|
|
(checker_idxs[i] <= 6)) ) return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* 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))
|
|
;
|
|
}
|
|
|
|
/* Meta definitions */
|
|
static PyObject*
|
|
quack_is_move_valid(PyObject *self, PyObject *args) {
|
|
int board[26];
|
|
int player;
|
|
int face_value;
|
|
int move[2];
|
|
|
|
int validity;
|
|
|
|
PyObject* board_tuple_obj;
|
|
PyObject* move_tuple_obj;
|
|
|
|
if (! PyArg_ParseTuple(args, "O!iiO!",
|
|
&PyTuple_Type, &board_tuple_obj,
|
|
&player,
|
|
&face_value,
|
|
&PyTuple_Type, &move_tuple_obj))
|
|
return NULL;
|
|
|
|
long numValuesBoard;
|
|
numValuesBoard = PyTuple_Size(board_tuple_obj);
|
|
if (numValuesBoard != 26) {
|
|
PyErr_SetString(QuackError, "Board tuple must have 26 entries");
|
|
return NULL;
|
|
}
|
|
|
|
PyObject* board_val_obj;
|
|
// Iterate over tuple to retreive positions
|
|
for (int i=0; i<numValuesBoard; i++) {
|
|
board_val_obj = PyTuple_GetItem(board_tuple_obj, i);
|
|
board[i] = PyLong_AsLong(board_val_obj);
|
|
}
|
|
|
|
long numValuesMove;
|
|
numValuesMove = PyTuple_Size(move_tuple_obj);
|
|
if (numValuesMove != 2) {
|
|
PyErr_SetString(QuackError, "Move tuple must have exactly 2 entries");
|
|
return NULL;
|
|
}
|
|
PyObject* move_val_obj;
|
|
for (int i=0; i<numValuesMove; i++) {
|
|
move_val_obj = PyTuple_GetItem(move_tuple_obj, i);
|
|
move[i] = PyLong_AsLong(move_val_obj);
|
|
}
|
|
|
|
validity = is_move_valid(board, player, face_value, move);
|
|
return Py_BuildValue("i", validity);
|
|
}
|
|
|
|
static PyObject*
|
|
quack_idxs_with_checkers_of_player(PyObject *self, PyObject *args) {
|
|
|
|
int board[26];
|
|
int player;
|
|
|
|
int* idxs;
|
|
|
|
PyObject* board_tuple_obj;
|
|
|
|
if (! PyArg_ParseTuple(args, "O!i",
|
|
&PyTuple_Type, &board_tuple_obj,
|
|
&player))
|
|
return NULL;
|
|
|
|
long numValuesBoard;
|
|
numValuesBoard = PyTuple_Size(board_tuple_obj);
|
|
if (numValuesBoard != 26) {
|
|
PyErr_SetString(QuackError, "Board tuple must have 26 entries");
|
|
return NULL;
|
|
}
|
|
|
|
PyObject* board_val_obj;
|
|
// Iterate over tuple to retreive positions
|
|
for (int i=0; i<numValuesBoard; i++) {
|
|
board_val_obj = PyTuple_GetItem(board_tuple_obj, i);
|
|
board[i] = PyLong_AsLong(board_val_obj);
|
|
}
|
|
|
|
idxs = idxs_with_checkers_of_player(board, player);
|
|
PyObject* idxs_list = PyList_New(0);
|
|
|
|
for (int i = 1; i <= idxs[0]; i++) {
|
|
PyList_Append(idxs_list, Py_BuildValue("i",idxs[i]));
|
|
}
|
|
free(idxs);
|
|
|
|
return Py_BuildValue("O", idxs_list);
|
|
}
|
|
|
|
|
|
static PyMethodDef quack_methods[] = {
|
|
{
|
|
"is_move_valid", quack_is_move_valid, METH_VARARGS,
|
|
"Evaluates the validity of the proposed move."
|
|
},
|
|
{
|
|
"idxs_with_checkers_of_player", quack_idxs_with_checkers_of_player, METH_VARARGS,
|
|
"Returnes a list of indexes with checkers of the specified player"},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
static struct PyModuleDef quack_definition = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"quack",
|
|
"A Python module that provides various useful Backgammon-related functions.",
|
|
-1,
|
|
quack_methods
|
|
};
|
|
|
|
PyMODINIT_FUNC PyInit_quack(void) {
|
|
PyObject* module;
|
|
|
|
module = PyModule_Create(&quack_definition);
|
|
if (module == NULL)
|
|
return NULL;
|
|
|
|
QuackError = PyErr_NewException("quack.error", NULL, NULL);
|
|
Py_INCREF(QuackError);
|
|
PyModule_AddObject(module, "error", QuackError);
|
|
|
|
return module;
|
|
}
|