From 03e61a59cf3cd76f7c855d5adf5867ee1073803c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoffer=20M=C3=BCller=20Madsen?= Date: Fri, 11 May 2018 17:29:22 +0200 Subject: [PATCH] quack --- board.py | 96 +------- quack/quack.c | 241 ++++++++++++++++++++ quack/setup.py | 9 + test.py | 598 ++++++++++++++++++++++++------------------------- 4 files changed, 552 insertions(+), 392 deletions(-) create mode 100644 quack/quack.c create mode 100644 quack/setup.py diff --git a/board.py b/board.py index 56c2737..5f438ae 100644 --- a/board.py +++ b/board.py @@ -1,3 +1,4 @@ +import quack import numpy as np import itertools @@ -12,11 +13,7 @@ class Board: @staticmethod def idxs_with_checkers_of_player(board, player): - idxs = [] - for idx, checker_count in enumerate(board): - if checker_count * player >= 1: - idxs.append(idx) - return idxs + return quack.idxs_with_checkers_of_player(board, player) # TODO: Write a test for this @@ -132,90 +129,7 @@ class Board: @staticmethod def is_move_valid(board, player, face_value, move): - if face_value == 0: - return True - else: - def sign(a): - return (a > 0) - (a < 0) - - from_idx = move[0] - to_idx = move[1] - to_state = None - from_state = board[from_idx] - delta = to_idx - from_idx - direction = sign(delta) - bearing_off = None - - # FIXME: Use get instead of array-like indexing - if to_idx >= 1 and to_idx <= 24: - to_state = board[to_idx] - bearing_off = False - else: # Bearing off - to_state = 0 - bearing_off = True - - # print("_"*20) - # print("board:", board) - # print("to_idx:", to_idx, "board[to_idx]:", board[to_idx], "to_state:", to_state) - # print("+"*20) - - def is_forward_move(): - return direction == player - - def face_value_match_move_length(): - return abs(delta) == face_value - - def bear_in_if_checker_on_bar(): - if player == 1: - bar = 0 - else: - bar = 25 - - bar_state = board[bar] - - if bar_state != 0: - return from_idx == bar - else: - return True - - def checkers_at_from_idx(): - return sign(from_state) == player - - def no_block_at_to_idx(): - if -sign(to_state) == player: - return abs(to_state) == 1 - else: - return True - - def can_bear_off(): - checker_idxs = Board.idxs_with_checkers_of_player(board, player) - def is_moving_backmost_checker(): - if player == 1: - return all([(idx >= from_idx) for idx in checker_idxs]) - else: - return all([(idx <= from_idx) for idx in checker_idxs]) - - def all_checkers_in_last_quadrant(): - if player == 1: - return all([(idx >= 19) for idx in checker_idxs]) - else: - return all([(idx <= 6) for idx in checker_idxs]) - - return all([ is_moving_backmost_checker(), - all_checkers_in_last_quadrant() ]) - - # TODO: add switch here instead of wonky ternary in all - # print("is_forward:",is_forward_move()) - # print("face_value:",face_value_match_move_length()) - # print("Checkes_at_from:",checkers_at_from_idx()) - # print("no_block:",no_block_at_to_idx()) - - return all([ is_forward_move(), - face_value_match_move_length(), - bear_in_if_checker_on_bar(), - checkers_at_from_idx(), - no_block_at_to_idx(), - can_bear_off() if bearing_off else True ]) + return quack.is_move_valid(board, player, face_value, move) @staticmethod def any_move_valid(board, player, roll): @@ -393,7 +307,3 @@ class Board: board[to_idx] += player return tuple(board) - - @staticmethod - def flip(board): - return tuple((-x for x in reversed(board))) diff --git a/quack/quack.c b/quack/quack.c new file mode 100644 index 0000000..b09e722 --- /dev/null +++ b/quack/quack.c @@ -0,0 +1,241 @@ +#include + +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