This commit is contained in:
Christoffer Müller Madsen 2018-05-11 17:29:22 +02:00
parent 93224864a4
commit 03e61a59cf
4 changed files with 552 additions and 392 deletions

View File

@ -1,3 +1,4 @@
import quack
import numpy as np import numpy as np
import itertools import itertools
@ -12,11 +13,7 @@ class Board:
@staticmethod @staticmethod
def idxs_with_checkers_of_player(board, player): def idxs_with_checkers_of_player(board, player):
idxs = [] return quack.idxs_with_checkers_of_player(board, player)
for idx, checker_count in enumerate(board):
if checker_count * player >= 1:
idxs.append(idx)
return idxs
# TODO: Write a test for this # TODO: Write a test for this
@ -132,90 +129,7 @@ class Board:
@staticmethod @staticmethod
def is_move_valid(board, player, face_value, move): def is_move_valid(board, player, face_value, move):
if face_value == 0: return quack.is_move_valid(board, player, face_value, move)
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 ])
@staticmethod @staticmethod
def any_move_valid(board, player, roll): def any_move_valid(board, player, roll):
@ -393,7 +307,3 @@ class Board:
board[to_idx] += player board[to_idx] += player
return tuple(board) return tuple(board)
@staticmethod
def flip(board):
return tuple((-x for x in reversed(board)))

241
quack/quack.c Normal file
View File

@ -0,0 +1,241 @@
#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;
}

9
quack/setup.py Normal file
View File

@ -0,0 +1,9 @@
from distutils.core import setup, Extension
quack = Extension('quack',
sources = ['quack.c'])
setup (name = 'quack',
version = '0.1',
description = 'Quack Backgammon Tools',
ext_modules = [quack])

598
test.py
View File

@ -552,372 +552,372 @@ class TestLegalMoves(unittest.TestCase):
self.assertEqual(Board.calculate_legal_states(board, -1, (4,3)), expected_board_set) self.assertEqual(Board.calculate_legal_states(board, -1, (4,3)), expected_board_set)
class TestBoardFlip(unittest.TestCase): # class TestBoardFlip(unittest.TestCase):
def test_flip_board(self): # def test_flip_board(self):
board = (0, # board = (0,
-14, -1, 0, 0, 0, 0, # -14, -1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, # 0, 0, 0, 0, 0, 1,
0) # 0)
expected_board = ( 0, # expected_board = ( 0,
-1, 0, 0, 0, 0, 0, # -1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 14, # 0, 0, 0, 0, 1, 14,
0 ) # 0 )
self.assertEqual(Board.flip(board), expected_board) # self.assertEqual(Board.flip(board), expected_board)
def test_flip_board_bar(self): # def test_flip_board_bar(self):
board = (2, # board = (2,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
-7) # -7)
expected_board = (7, # expected_board = (7,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0,
-2) # -2)
self.assertEqual(Board.flip(board), expected_board) # self.assertEqual(Board.flip(board), expected_board)
def test_flip_board_extensive(self): # def test_flip_board_extensive(self):
board = (4, # board = (4,
-5, -1, 0, 4, 3, 0, # -5, -1, 0, 4, 3, 0,
0, -1, 0, -5, 0, 0, # 0, -1, 0, -5, 0, 0,
0, 3, 0, 0, 0, 0, # 0, 3, 0, 0, 0, 0,
0, 0, 0, -1, 0, 1, # 0, 0, 0, -1, 0, 1,
-2) # -2)
expected_board = (2, # expected_board = (2,
-1, 0, 1, 0, 0, 0, # -1, 0, 1, 0, 0, 0,
0, 0, 0, 0, -3, 0, # 0, 0, 0, 0, -3, 0,
0, 0, 5, 0, 1, 0, # 0, 0, 5, 0, 1, 0,
0, -3, -4, 0, 1, 5, # 0, -3, -4, 0, 1, 5,
-4) # -4)
self.assertEqual(Board.flip(board), expected_board) # self.assertEqual(Board.flip(board), expected_board)
def test_inverse(self): # def test_inverse(self):
board = (4, # board = (4,
-5, -1, 0, 4, 3, 0, # -5, -1, 0, 4, 3, 0,
0, -1, 0, -5, 0, 0, # 0, -1, 0, -5, 0, 0,
0, 3, 0, 0, 0, 0, # 0, 3, 0, 0, 0, 0,
0, 0, 0, -1, 0, 1, # 0, 0, 0, -1, 0, 1,
-2) # -2)
self.assertEqual(Board.flip(Board.flip(board)), board) # self.assertEqual(Board.flip(Board.flip(board)), board)
def test_tesauro_initial(self): # def test_tesauro_initial(self):
board = Board.initial_state # board = Board.initial_state
expected = (1,1,0,0, # expected = (1,1,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0.0, # 0.0,
0, # 0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,0,0, # 1,1,0,0,
0.0, # 0.0,
0, # 0,
1, # 1,
0 # 0
) # )
import numpy as np # import numpy as np
self.assertTrue((Board.board_features_tesauro(board, 1) == # self.assertTrue((Board.board_features_tesauro(board, 1) ==
np.array(expected).reshape(1, 198)).all()) # np.array(expected).reshape(1, 198)).all())
def test_tesauro_bars(self): # def test_tesauro_bars(self):
board = list(Board.initial_state) # board = list(Board.initial_state)
board[1] = 0 # board[1] = 0
board[0] = 2 # board[0] = 2
board[24] = 0 # board[24] = 0
board[25] = -2 # board[25] = -2
board = tuple(board) # board = tuple(board)
expected = (0,0,0,0, # expected = (0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1.0, # 1.0,
0, # 0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1.0, # 1.0,
0, # 0,
1, # 1,
0 # 0
) # )
import numpy as np # import numpy as np
self.assertTrue((Board.board_features_tesauro(board, 1) == # self.assertTrue((Board.board_features_tesauro(board, 1) ==
np.array(expected).reshape(1, 198)).all()) # np.array(expected).reshape(1, 198)).all())
def test_tesauro_home(self): # def test_tesauro_home(self):
board = list(Board.initial_state) # board = list(Board.initial_state)
board[1] = 0 # board[1] = 0
board[24] = 0 # board[24] = 0
board = tuple(board) # board = tuple(board)
expected = (0,0,0,0, # expected = (0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0.0, # 0.0,
2, # 2,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0.0, # 0.0,
2, # 2,
1, # 1,
0 # 0
) # )
import numpy as np # import numpy as np
self.assertTrue((Board.board_features_tesauro(board, 1) == # self.assertTrue((Board.board_features_tesauro(board, 1) ==
np.array(expected).reshape(1, 198)).all()) # np.array(expected).reshape(1, 198)).all())
def test_tesauro_black_player(self): # def test_tesauro_black_player(self):
board = Board.initial_state # board = Board.initial_state
expected = (1,1,0,0, # expected = (1,1,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0.0, # 0.0,
0, # 0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
1,1,1,0, # 1,1,1,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,1,1, # 1,1,1,1,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
0,0,0,0, # 0,0,0,0,
1,1,0,0, # 1,1,0,0,
0.0, # 0.0,
0, # 0,
0, # 0,
1 # 1
) # )
import numpy as np # import numpy as np
self.assertTrue((Board.board_features_tesauro(board, -1) == # self.assertTrue((Board.board_features_tesauro(board, -1) ==
np.array(expected).reshape(1, 198)).all()) # np.array(expected).reshape(1, 198)).all())
if __name__ == '__main__': if __name__ == '__main__':