Skip to content

Commit

Permalink
has_action optmimization
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasBorboleta committed May 29, 2021
1 parent 86768cb commit b043ac7
Showing 1 changed file with 87 additions and 27 deletions.
114 changes: 87 additions & 27 deletions jersi_certu/jersi_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ class JersiState:

__max_credit = 40
__king_end_distances = None
__central_hexagon_indices = None
__center_hexagon_indices = None

__slots__ = ('__cube_status', '__hexagon_bottom', '__hexagon_top',
'__credit', '__player', '__turn',
Expand Down Expand Up @@ -1006,7 +1006,7 @@ def __init__(self, play_reserve=True):
self.__init_hexagon_top_and_bottom(play_reserve)
self.__init_cube_status(play_reserve)
self.__init_king_end_distances()
self.__init__central_hexagon_indices()
self.__init__center_hexagon_indices()


def __fork(self):
Expand Down Expand Up @@ -1132,19 +1132,19 @@ def __init_king_end_distances(self):



def __init__central_hexagon_indices(self):
def __init__center_hexagon_indices(self):

if JersiState.__central_hexagon_indices is None:
if JersiState.__center_hexagon_indices is None:

central_names = ['c3', 'c4', 'c5',
center_names = ['c3', 'c4', 'c5',
'd3', 'd4', 'd5', 'd6',
'e3', 'e4', 'e5', 'e6', 'e7',
'f3', 'f4', 'f5', 'f6',
'g3', 'g4', 'g5',
]

JersiState.__central_hexagon_indices = array.array('b',
[Hexagon.get(name).index for name in central_names])
JersiState.__center_hexagon_indices = array.array('b',
[Hexagon.get(name).index for name in center_names])


def __set_cube_at_hexagon_by_names(self, cube_name, hexagon_name):
Expand Down Expand Up @@ -1229,8 +1229,8 @@ def get_king_end_distances(self):
return king_distances


def get_central_hexagon_indices(self):
return JersiState.__central_hexagon_indices
def get_center_hexagon_indices(self):
return JersiState.__center_hexagon_indices


def get_capture_counts(self):
Expand Down Expand Up @@ -1428,7 +1428,7 @@ def is_terminal(self):
self.__terminal_case = TerminalCase.ZERO_CREDIT
self.__rewards = [Reward.DRAW for _ in Player]

elif len(self.get_actions()) == 0:
elif not self.has_action():
# the current player looses and the other player wins
self.__terminated = True
self.__rewards = [Reward.DRAW for _ in Player]
Expand All @@ -1443,7 +1443,7 @@ def is_terminal(self):
self.__rewards[Player.WHITE] = Reward.WIN

return self.__terminated


def get_actions(self, shuffle=True):
if self.__actions is None:
Expand All @@ -1454,6 +1454,24 @@ def get_actions(self, shuffle=True):
return self.__actions


def has_action(self):

moves = self.__find_cube_first_moves(find_one=True)
if len(moves) != 0:
king_relocation_moves = self.__find_king_relocations(moves)
if len(king_relocation_moves) != 0:
return True

drops = self.__find_drops(find_one=True)
if len(drops) != 0:
return True

if len(moves) != 0 and len(king_relocation_moves) == 0:
return len(self.get_actions(shuffle=False)) != 0
else:
return False


def __create_action_by_names(self):
self.__actions_by_names = {}
self.__actions_by_simple_names = {}
Expand Down Expand Up @@ -1491,14 +1509,22 @@ def get_action_by_simple_name(self, action_name):

### Action finders

def __find_drops(self):
def __find_drops(self, find_one=False):

action_appender = JersiActionAppender()
found_one = False

for cube_1_index in self.__find_droppable_cubes():
if find_one and found_one:
break

for destination_1 in Hexagon.get_all_active_indices():
action_1 = self.__try_drop(cube_1_index, destination_1)
if action_1 is not None:
action_appender.append(action_1)
if find_one:
found_one = True
break

state_1 = action_1.state.__fork()

Expand Down Expand Up @@ -1542,17 +1568,24 @@ def __find_king_relocations(self, move_actions):
return actions


def __find_cube_first_moves(self):
def __find_cube_first_moves(self, find_one=False):
actions = []
found_one = False

for source_1 in self.__find_hexagons_with_movable_cube():

if find_one and found_one:
break

for direction_1 in HexagonDirection:
destination_1 = Hexagon.get_next_fst_indices(source_1, direction_1)
if destination_1 != Null.HEXAGON:
action_1 = self.__try_move_cube(source_1, destination_1)
if action_1 is not None:
actions.append(action_1)
if find_one:
found_one = True
break

state_1 = action_1.state.__fork()
if state_1.__is_hexagon_with_movable_stack(destination_1):
Expand All @@ -1574,17 +1607,25 @@ def __find_cube_first_moves(self):
return actions


def __find_stack_first_moves(self):
def __find_stack_first_moves(self, find_one=False):

actions = []
found_one = False

for source_1 in self.__find_hexagons_with_movable_stack():

if find_one and found_one:
break

for direction_1 in HexagonDirection:
destination_11 = Hexagon.get_next_fst_indices(source_1, direction_1)
if destination_11 != Null.HEXAGON:
action_11 = self.__try_move_stack(source_1, destination_11)
if action_11 is not None:
actions.append(action_11)
if find_one:
found_one = True
break

state_11 = action_11.state.__fork()

Expand Down Expand Up @@ -2265,6 +2306,8 @@ def jersiRandomPolicy(state):


class HumanSearcher():

__slots__ = ('__name', '__action_simple_name', '__use_command_line')


def __init__(self, name):
Expand Down Expand Up @@ -2321,6 +2364,8 @@ def __search_using_command_line(self, state):


class RandomSearcher():

__slots__ = ('__name')


def __init__(self, name):
Expand Down Expand Up @@ -2358,13 +2403,18 @@ def search(self, state):

class MinimaxSearcher():

__slots__ = ('__name', '__max_depth', '__max_children', '__capture_weight', '__center_weight')


def __init__(self, name, max_depth=1, max_children=None, capture_factor=50):
def __init__(self, name, max_depth=1, max_children=None, capture_weight=50_000, center_weight=200):

assert max_depth >= 1

self.__name = name
self.__max_depth = max_depth
self.__max_children = max_children
self.__capture_factor = capture_factor
self.__capture_weight = capture_weight
self.__center_weight = center_weight


def get_name(self):
Expand Down Expand Up @@ -2439,38 +2489,38 @@ def state_value(self, state):
reserve_difference = player_sign*(reserve_counts[Player.WHITE] - reserve_counts[Player.BLACK])

# white and black movable cubes in the central zone
white_central_count = 0
black_central_count = 0
white_center_count = 0
black_center_count = 0

hexagon_bottom = jersi_state.get_hexagon_bottom()
hexagon_top= jersi_state.get_hexagon_bottom()

for hexagon_index in jersi_state.get_central_hexagon_indices():
for hexagon_index in jersi_state.get_center_hexagon_indices():

for cube_index in [hexagon_bottom[hexagon_index], hexagon_top[hexagon_index]]:

if cube_index != Null.CUBE:
cube = Cube.all[cube_index]
if cube.sort != CubeSort.MOUNTAIN:
if cube.player == Player.WHITE:
white_central_count += 1
white_center_count += 1

elif cube.player == Player.BLACK:
black_central_count += 1
black_center_count += 1
else:
break

central_difference = player_sign*(white_central_count - black_central_count)
center_difference = player_sign*(white_center_count - black_center_count)

# synthesis
distance_weight = 1_000
capture_weight = distance_weight*self.__capture_factor
central_weight = 200
capture_weight = self.__capture_weight
center_weight = self.__center_weight
reserve_weight = 0

value += distance_weight*distance_difference
value += capture_weight*capture_difference
value += central_weight*central_difference
value += center_weight*center_difference
value += reserve_weight*reserve_difference

return value
Expand Down Expand Up @@ -2579,6 +2629,8 @@ def score_action(action):

class MctsSearcher():

__slots__ = ('__name', '__time_limit', '__iteration_limit', '__capture_weight', '__searcher')


def __init__(self, name, time_limit=None, iteration_limit=None, rolloutPolicy=mcts.randomPolicy):
self.__name = name
Expand Down Expand Up @@ -2642,6 +2694,8 @@ def search(self, state):

class SearcherCatalog:

__slots__ = ('__catalog')


def __init__(self):
self.__catalog = {}
Expand Down Expand Up @@ -2673,12 +2727,18 @@ def get(self, name):
SEARCHER_CATALOG.add( MinimaxSearcher("minimax2", max_depth=2) )
SEARCHER_CATALOG.add( MinimaxSearcher("minimax2-400", max_depth=2, max_children=400) )

SEARCHER_CATALOG.add( MinimaxSearcher("minimax3", max_depth=3) )
SEARCHER_CATALOG.add( MinimaxSearcher("minimax3-400", max_depth=3, max_children=400) )

SEARCHER_CATALOG.add( MctsSearcher("mcts-2s", time_limit=2_000) )
SEARCHER_CATALOG.add( MctsSearcher("mcts-10s-jrp", time_limit=10_000, rolloutPolicy=jersiRandomPolicy) )
SEARCHER_CATALOG.add( MctsSearcher("mcts-50i-jrp", iteration_limit=50, rolloutPolicy=jersiRandomPolicy) )


class Game:

__slots__ = ('__searcher', '__jersi_state', '__log', '__turn', '__last_action', '__turn_duration')


def __init__(self):
self.__searcher = [None, None]
Expand Down Expand Up @@ -2894,8 +2954,8 @@ def test_game_between_minimax_players():
for game_index in range(game_count):
game = Game()

old_searcher = MinimaxSearcher("old", max_depth=1, capture_factor=50)
new_searcher = MinimaxSearcher("new", max_depth=1, capture_factor=60)
old_searcher = MinimaxSearcher("old", max_depth=1, center_weight=200)
new_searcher = MinimaxSearcher("new", max_depth=2, center_weight=0)

if game_index % 2 == 0:
game.set_white_searcher(old_searcher)
Expand Down

0 comments on commit b043ac7

Please sign in to comment.