Skip to content

Commit

Permalink
Add support for Xiangqi (#93)
Browse files Browse the repository at this point in the history
* Add Fairy-Stockfish (fork) to submodules

* Add xiangqi support for training

* Add xiangqi support for MCTS

* Fix duplicated main.cpp and .nnue file

* Delete killed Fairy-Stockfish submodule

* Reintroduce Fairy-Stockfish fork

* Add tests for xiangqi planes

* Add preprocessing support for xiangqi (data is given as CSV)

* Fix rank and file lookups

* Disable NNUE evaluation

* Update fairystate.h

* added init() to fairystate.cpp

* Update state.h

* Update optionsuci.cpp (use model/xiangqi as Model_Directory)

* Update CMakeLists.txt

* use MODE_CRAYZHOUSE as default

Co-authored-by: Johannes Czech <QueensGambit@users.noreply.github.com>
  • Loading branch information
mlanher and QueensGambit authored Apr 27, 2021
1 parent 411c4fe commit 5e5eee5
Show file tree
Hide file tree
Showing 33 changed files with 9,586 additions and 59 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "engine/3rdparty/Stockfish"]
path = engine/3rdparty/Stockfish
url = https://github.com/QueensGambit/Stockfish.git
[submodule "engine/3rdparty/Fairy-Stockfish"]
path = engine/3rdparty/Fairy-Stockfish
url = https://github.com/xsr7qsr/Fairy-Stockfish.git
59 changes: 58 additions & 1 deletion DeepCrazyhouse/src/domain/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
MODE,
MODE_LICHESS,
MODE_CRAZYHOUSE,
MODE_XIANGQI,
LABELS_XIANGQI,
CHANNEL_MAPPING_CONST,
CHANNEL_MAPPING_POS,
MAX_NB_MOVES,
Expand All @@ -27,6 +29,10 @@
)


# file lookup for vertically mirrored xiangqi boards
mirrored_files_lookup = {'a': 'i', 'b': 'h', 'c': 'g', 'd': 'f', 'e': 'e', 'f': 'd', 'g': 'c', 'h': 'b', 'i': 'a'}


def get_row_col(position, mirror=False):
"""
Maps a value [0,63] to its row and column index
Expand Down Expand Up @@ -139,6 +145,24 @@ def get_numpy_arrays(pgn_dataset):
return start_indices, x, y_value, y_policy, entries[0], entries[1]


def get_x_y_and_indices(dataset):
"""
Loads the content of the given dataset into numpy arrays.
:param dataset: dataset file handle
:return: numpy-arrays:
starting_idx - defines the index where each game starts
x - the board representation for all games
y_value - the game outcome (-1,0,1) for each board position
y_policy - the movement policy for the next_move played
"""
start_indices = np.array(dataset["start_indices"])
x = np.array(dataset["x"])
y_value = np.array(dataset["y_value"])
y_policy = np.array(dataset["y_policy"])
return start_indices, x, y_value, y_policy


def normalize_input_planes(x):
"""
Normalizes input planes to range [0,1]. Works in place / meaning the input parameter x is manipulated
Expand All @@ -161,13 +185,21 @@ def normalize_input_planes(x):
mat_pos[channel, :, :] /= MAX_NB_PRISONERS
# the prison for black begins 5 channels later
mat_pos[channel + POCKETS_SIZE_PIECE_TYPE, :, :] /= MAX_NB_PRISONERS
# xiangqi has 7 piece types (king/general is excluded as prisoner)
elif MODE == MODE_XIANGQI:
for p_type in range(6):
channel = CHANNEL_MAPPING_POS["prisoners"] + p_type
mat_pos[channel, :, :] /= MAX_NB_PRISONERS
# the prison for opponent begins 6 channels later
mat_pos[channel + POCKETS_SIZE_PIECE_TYPE, :, :] /= MAX_NB_PRISONERS

# Total Move Count
# 500 was set as the max number of total moves
mat_const[CHANNEL_MAPPING_CONST["total_mv_cnt"], :, :] /= MAX_NB_MOVES
# No progress count
# after 40 moves of no progress the 40 moves rule for draw applies
mat_const[CHANNEL_MAPPING_CONST["no_progress_cnt"], :, :] /= MAX_NB_NO_PROGRESS
if MODE != MODE_XIANGQI:
mat_const[CHANNEL_MAPPING_CONST["no_progress_cnt"], :, :] /= MAX_NB_NO_PROGRESS

return x

Expand All @@ -177,6 +209,31 @@ def normalize_input_planes(x):
MATRIX_NORMALIZER = normalize_input_planes(np.ones((NB_CHANNELS_TOTAL, BOARD_HEIGHT, BOARD_WIDTH)))


def augment(x, y_policy):
"""
Augments a given set of planes and their corresponding policy targets.
The returned planes are vertically mirrored. The returned policy targets
are adjusted, so that they correspond to the new planes.
Works in-place.
:param x: Input planes
:param y_policy: Policy targets
"""
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i][j] = np.fliplr(x[i][j])

idx_mv = np.where(y_policy[i] == 1)[0][0]
y_policy[i][idx_mv] = 0
ucci = LABELS_XIANGQI[idx_mv]

from_square_aug = mirrored_files_lookup[ucci[0]] + ucci[1]
to_square_aug = mirrored_files_lookup[ucci[2]] + ucci[3]
ucci_aug = from_square_aug + to_square_aug

idx_mv_aug = LABELS_XIANGQI.index(ucci_aug)
y_policy[i][idx_mv_aug] = 1


def customize_input_planes(x):
"""
Reverts normalization back to integer values. Works in place.
Expand Down
Loading

0 comments on commit 5e5eee5

Please sign in to comment.