diff --git a/board.py b/board.py index 0f403ed..0306a22 100644 --- a/board.py +++ b/board.py @@ -42,13 +42,14 @@ class Board: # quack-fat @staticmethod def board_features_quack_fat(board, player): - board = list(board) - positives = [x if x > 0 else 0 for x in board] - negatives = [x if x < 0 else 0 for x in board] - board.append( 15 - sum(positives)) - board.append(-15 - sum(negatives)) - board += ([1, 0] if np.sign(player) > 0 else [0, 1]) - return np.array(board).reshape(1,30) + return np.array(quack.board_features_quack_fat(board,player)).reshape(1,30) + # board = list(board) + # positives = [x if x > 0 else 0 for x in board] + # negatives = [x if x < 0 else 0 for x in board] + # board.append( 15 - sum(positives)) + # board.append(-15 - sum(negatives)) + # board += ([1, 0] if np.sign(player) > 0 else [0, 1]) + # return np.array(board).reshape(1,30) # quack-fatter diff --git a/network.py b/network.py index f6e4914..5ea80cd 100644 --- a/network.py +++ b/network.py @@ -216,10 +216,7 @@ class Network: :return: A pair of the best state to go to, together with the score of that state """ legal_moves = list(Board.calculate_legal_states(board, player, roll)) - - legal_states = [list(tmp) for tmp in legal_moves] - - legal_states = np.array([self.board_trans_func(tmp, player)[0] for tmp in legal_states]) + legal_states = np.array([self.board_trans_func(move, player)[0] for move in legal_moves]) scores = self.model.predict_on_batch(legal_states) transformed_scores = [x if np.sign(player) > 0 else 1 - x for x in scores] @@ -260,12 +257,7 @@ class Network: # find all legal states from the given board and the given roll init_legal_states = Board.calculate_legal_states(board, player, roll) - - legal_moves = list(Board.calculate_legal_states(board, player, roll)) - - legal_states = [list(tmp) for tmp in legal_moves] - - legal_states = np.array([self.board_trans_func(tmp, player)[0] for tmp in legal_states]) + legal_states = np.array([self.board_trans_func(state, player)[0] for state in init_legal_states]) scores = self.calc_vals(legal_states) scores = [score.numpy() for score in scores] diff --git a/quack/quack.c b/quack/quack.c index 8292f39..0c72ae5 100644 --- a/quack/quack.c +++ b/quack/quack.c @@ -34,7 +34,7 @@ int *idxs_with_checkers_of_player(int board[], int player) { int *idxs = malloc((1 + ctr) * sizeof(int)); if (idxs == NULL) { - fprintf(stderr, "malloc failed\n"); + PyErr_NoMemory(); abort(); } @@ -79,15 +79,22 @@ int can_bear_off(int board[], int player, int from_idx) { if (player == 1) { for (int i = 1; i <= checker_idxs[0]; i++) { if ( !((checker_idxs[i] >= from_idx) && - (checker_idxs[i] >= 19)) ) return 0; + (checker_idxs[i] >= 19)) ) { + free(checker_idxs); + return 0; + } } } else { for (int i = 1; i <= checker_idxs[0]; i++) { if ( !((checker_idxs[i] <= from_idx) && - (checker_idxs[i] <= 6)) ) return 0; + (checker_idxs[i] <= 6)) ) { + free(checker_idxs); + return 0; + } } } - + + free(checker_idxs); return 1; } @@ -149,7 +156,7 @@ void do_move(int board[], int player, int move[]) { int* do_move_clone(int board[], int player, int move[]) { int* new_board = malloc(sizeof(int) * 26); if (new_board == NULL) { - fprintf(stderr, "malloc failed\n"); + PyErr_NoMemory(); abort(); } @@ -161,9 +168,9 @@ int* do_move_clone(int board[], int player, int move[]) { return new_board; } -PyObject* store_board_to_pyobject(int board[]) { - PyObject* board_tuple = PyTuple_New(26); - for (int i = 0; i < 26; i++) { +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; @@ -175,8 +182,9 @@ board_list calc_moves(int board[], int player, int face_value) { if (checker_idxs[0] == 0) { boards.size = 1; - PyObject* board_tuple = store_board_to_pyobject(board); + PyObject* board_tuple = store_board_to_pytuple(board, 26); boards.list[0] = board_tuple; + free(checker_idxs); return boards; } @@ -188,7 +196,7 @@ board_list calc_moves(int board[], int player, int face_value) { if (is_move_valid(board, player, face_value, move)) { int* new_board = do_move_clone(board, player, move); - PyObject* board_tuple = store_board_to_pyobject(new_board); + PyObject* board_tuple = store_board_to_pytuple(new_board, 26); // segfault maybe :'( free(new_board); @@ -198,10 +206,40 @@ board_list calc_moves(int board[], int player, int face_value) { } } + 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; @@ -233,7 +271,7 @@ int extract_move(int *move, PyObject* move_tuple_obj) { move_val_obj = PyTuple_GetItem(move_tuple_obj, i); move[i] = PyLong_AsLong(move_val_obj); } - + return 0; } @@ -244,8 +282,6 @@ quack_is_move_valid(PyObject *self, PyObject *args) { int face_value; int move[2]; - int validity; - PyObject* board_tuple_obj; PyObject* move_tuple_obj; @@ -258,9 +294,9 @@ quack_is_move_valid(PyObject *self, PyObject *args) { if (extract_board(board, board_tuple_obj)) return NULL; if (extract_move(move, move_tuple_obj)) return NULL; - - validity = is_move_valid(board, player, face_value, move); - return Py_BuildValue("i", validity); + + if (is_move_valid(board, player, face_value, move)) Py_RETURN_TRUE; + else Py_RETURN_FALSE; } static PyObject* @@ -281,14 +317,17 @@ quack_idxs_with_checkers_of_player(PyObject *self, PyObject *args) { if (extract_board(board, board_tuple_obj)) return NULL; idxs = idxs_with_checkers_of_player(board, player); - PyObject* idxs_list = PyList_New(0); + PyObject* idxs_list = PyList_New(idxs[0]); - for (int i = 1; i <= idxs[0]; i++) { - PyList_Append(idxs_list, Py_BuildValue("i", idxs[i])); + for (int i = 0; i < idxs[0]; i++) { + PyList_SetItem(idxs_list, i, Py_BuildValue("i", idxs[i+1])); } free(idxs); + + PyObject *result = Py_BuildValue("O", idxs_list); + Py_DECREF(idxs_list); - return Py_BuildValue("O", idxs_list); + return result; } static PyObject* @@ -310,10 +349,15 @@ quack_do_move(PyObject *self, PyObject *args) { if (extract_move(move, move_tuple_obj)) return NULL; do_move(board, player, move); + PyObject* board_tuple = store_board_to_pytuple(board, 26); - PyObject* board_tuple = store_board_to_pyobject(board); + // This is shaky + Py_DECREF(board); + + PyObject *result = Py_BuildValue("O", board_tuple); + Py_DECREF(board_tuple); - return Py_BuildValue("O", board_tuple); + return result; } static PyObject* @@ -333,16 +377,43 @@ quack_calc_moves(PyObject *self, PyObject *args) { if (extract_board(board, board_tuple_obj)) return NULL; board_list boards = calc_moves(board, player, face_value); - PyObject* boards_list = PyList_New(0); + PyObject* boards_list = PyList_New(boards.size); for (int i = 0; i < boards.size; i++) { - if (PyList_Append(boards_list, boards.list[i])) { + if (PyList_SetItem(boards_list, i, boards.list[i])) { printf("list insertion failed at index %i\n",i); abort(); } } - return Py_BuildValue("O", boards_list); + PyObject *result = Py_BuildValue("O", boards_list); + Py_DECREF(boards_list); + + return result; +} + +static PyObject* +quack_board_features_quack_fat(PyObject *self, PyObject *args) { + int board[26]; + int player; + + PyObject* board_tuple_obj; + + if (! PyArg_ParseTuple(args, "O!i", + &PyTuple_Type, &board_tuple_obj, + &player)) + return NULL; + + if (extract_board(board, board_tuple_obj)) return NULL; + + int* new_board = board_features_quack_fat(board, player); + PyObject* board_tuple = store_board_to_pytuple(new_board, 30); + free(new_board); + + PyObject *result = Py_BuildValue("O", board_tuple); + Py_DECREF(board_tuple); + + return result; } @@ -363,6 +434,10 @@ static PyMethodDef quack_methods[] = { "calc_moves", quack_calc_moves, METH_VARARGS, "Calculates all legal moves from board with specified face value" }, + { + "board_features_quack_fat", quack_board_features_quack_fat, METH_VARARGS, + "Transforms a board to the quack-fat board representation" + }, {NULL, NULL, 0, NULL} };