graph-games-proto 0.3.1990__py3-none-any.whl → 0.3.2030__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graph_games_proto/fns.py +672 -350
- {graph_games_proto-0.3.1990.dist-info → graph_games_proto-0.3.2030.dist-info}/METADATA +1 -1
- {graph_games_proto-0.3.1990.dist-info → graph_games_proto-0.3.2030.dist-info}/RECORD +5 -5
- {graph_games_proto-0.3.1990.dist-info → graph_games_proto-0.3.2030.dist-info}/WHEEL +0 -0
- {graph_games_proto-0.3.1990.dist-info → graph_games_proto-0.3.2030.dist-info}/top_level.txt +0 -0
graph_games_proto/fns.py
CHANGED
@@ -1936,12 +1936,13 @@ class StateKernel(PClass):
|
|
1936
1936
|
rng = field(type=random.Random)
|
1937
1937
|
game_config = field(type=GameConfig)
|
1938
1938
|
edges = field(type=list) # List[BiEdge]
|
1939
|
+
nodes = field(type=list) # List[Node]
|
1940
|
+
regions = field(type=list) # List[Region]
|
1939
1941
|
decks = field(type=list) # List[Deck]
|
1940
1942
|
piles = field(type=list) # List[Pile]
|
1941
1943
|
players = field(type=list) # List[Player]
|
1942
1944
|
player_idxs = field(type=list) # List[int]
|
1943
1945
|
history = field(type=list) # List[PublicAction]
|
1944
|
-
player_scores = field(type=list) # List[PlayerScore]
|
1945
1946
|
idx2path = field(type=list) # List[Path2]
|
1946
1947
|
pieceuuid2piece = field(type=dict) # Dict[str, Piece]
|
1947
1948
|
edgeuuid2idx = field(type=dict) # Dict[str, int]
|
@@ -1953,17 +1954,20 @@ class StateKernel(PClass):
|
|
1953
1954
|
starting_decks = field(type=list) # List[Deck]
|
1954
1955
|
starting_piles = field(type=list) # List[Pile]
|
1955
1956
|
goals = field(type=list) # List[FrozenGoal]
|
1957
|
+
last_to_play = field(type=(int, type(None)), initial=None)
|
1958
|
+
player_graphs_3 = field(type=list) # List[PlayerGraph]
|
1956
1959
|
def __todict__(self):
|
1957
1960
|
return {
|
1958
1961
|
"rng": rng2json(self.rng),
|
1959
1962
|
"game_config": self.game_config.__todict__(),
|
1960
1963
|
"edges": [edge.__todict__() for edge in self.edges],
|
1964
|
+
"nodes": [node.__todict__() for node in self.nodes],
|
1965
|
+
"regions": [region.__todict__() for region in self.regions],
|
1961
1966
|
"decks": [deck.__todict__() for deck in self.decks],
|
1962
1967
|
"piles": [pile.__todict__() for pile in self.piles],
|
1963
1968
|
"players": [player.__todict__() for player in self.players],
|
1964
1969
|
"player_idxs": self.player_idxs,
|
1965
1970
|
"history": [action.__todict__() for action in self.history],
|
1966
|
-
"player_scores": [score.__todict__() for score in self.player_scores],
|
1967
1971
|
"idx2path": [v.__todict__() for v in self.idx2path],
|
1968
1972
|
"pieceuuid2piece": {k: v.__todict__() for k, v in self.pieceuuid2piece.items()},
|
1969
1973
|
"edgeuuid2idx": self.edgeuuid2idx,
|
@@ -1975,6 +1979,8 @@ class StateKernel(PClass):
|
|
1975
1979
|
"starting_decks": [deck.__todict__() for deck in self.starting_decks],
|
1976
1980
|
"starting_piles": [pile.__todict__() for pile in self.starting_piles],
|
1977
1981
|
"goals": [goal.__todict__() for goal in self.goals],
|
1982
|
+
"last_to_play": self.last_to_play,
|
1983
|
+
"player_graphs_3": [x.__todict__() for x in self.player_graphs_3],
|
1978
1984
|
}
|
1979
1985
|
@staticmethod
|
1980
1986
|
def __fromdict__(d):
|
@@ -1982,12 +1988,13 @@ class StateKernel(PClass):
|
|
1982
1988
|
rng=json2rng(d["rng"]),
|
1983
1989
|
game_config=GameConfig.__fromdict__(d["game_config"]),
|
1984
1990
|
edges=[BiEdge.__fromdict__(edge) for edge in d["edges"]],
|
1991
|
+
nodes=[Node.__fromdict__(n) for n in d["nodes"]],
|
1992
|
+
regions=[Region.__fromdict__(r) for r in d["regions"]],
|
1985
1993
|
decks=[Deck.__fromdict__(deck) for deck in d["decks"]],
|
1986
1994
|
piles=[Pile.__fromdict__(pile) for pile in d["piles"]],
|
1987
1995
|
players=[Player.__fromdict__(player) for player in d["players"]],
|
1988
1996
|
player_idxs=d["player_idxs"],
|
1989
1997
|
history=[PublicAction.__fromdict__(action) for action in d["history"]],
|
1990
|
-
player_scores=[PlayerScore.__fromdict__(score) for score in d["player_scores"]],
|
1991
1998
|
idx2path=[Path2.__fromdict(v) for v in d["idx2path"]],
|
1992
1999
|
pieceuuid2piece={k: Piece.__fromdict(v) for k, v in d["pieceuuid2piece"].items()},
|
1993
2000
|
edgeuuid2idx=d["edgeuuid2idx"],
|
@@ -1999,6 +2006,8 @@ class StateKernel(PClass):
|
|
1999
2006
|
starting_decks=[Deck.__fromdict__(x) for x in d["starting_decks"]],
|
2000
2007
|
starting_piles=[Pile.__fromdict__(x) for x in d["starting_piles"]],
|
2001
2008
|
goals=[FrozenGoal.__fromdict__(goal) for goal in d["goals"]],
|
2009
|
+
last_to_play=d.get("last_to_play"),
|
2010
|
+
player_graphs_3=[PlayerGraph.__fromdict__(x) for x in d["player_graphs_3"]],
|
2002
2011
|
)
|
2003
2012
|
|
2004
2013
|
|
@@ -2007,6 +2016,7 @@ def init_state_kernel(**kwargs):
|
|
2007
2016
|
game_config = kwargs.get('game_config')
|
2008
2017
|
fig = game_config.fig
|
2009
2018
|
board_config = fig.board_config
|
2019
|
+
nodes = kwargs.get('nodes', [])
|
2010
2020
|
edges = kwargs.get('edges', [])
|
2011
2021
|
idx2path = []
|
2012
2022
|
edgeuuid2idx = {edge.uuid: idx for idx, edge in enumerate(edges)}
|
@@ -2041,16 +2051,18 @@ def init_state_kernel(**kwargs):
|
|
2041
2051
|
bonuses = game_config.fig.board_config.bonuses
|
2042
2052
|
bonusuuid2bonusidx = {bonus.uuid: idx for idx, bonus in enumerate(bonuses)}
|
2043
2053
|
|
2054
|
+
player_graphs_3 = calc_player_graphs3(nodes, edges, pieceuuid2piece, game_config)
|
2055
|
+
|
2044
2056
|
return StateKernel(
|
2045
2057
|
rng=rng,
|
2046
2058
|
game_config=game_config,
|
2047
2059
|
edges=edges,
|
2060
|
+
nodes=kwargs.get('nodes'),
|
2048
2061
|
decks=kwargs.get('decks'),
|
2049
2062
|
piles=kwargs.get('piles'),
|
2050
2063
|
players=kwargs.get('players'),
|
2051
2064
|
player_idxs=kwargs.get('player_idxs'),
|
2052
2065
|
history=kwargs.get('history'),
|
2053
|
-
player_scores=kwargs.get('player_scores'),
|
2054
2066
|
idx2path=idx2path,
|
2055
2067
|
edgeuuid2idx=edgeuuid2idx,
|
2056
2068
|
carduuid2card=carduuid2card,
|
@@ -2062,74 +2074,40 @@ def init_state_kernel(**kwargs):
|
|
2062
2074
|
starting_decks=kwargs.get('starting_decks'),
|
2063
2075
|
starting_piles=kwargs.get('starting_piles'),
|
2064
2076
|
goals=kwargs.get('goals'),
|
2077
|
+
player_graphs_3=player_graphs_3,
|
2065
2078
|
)
|
2066
2079
|
|
2067
2080
|
|
2068
2081
|
class State(PClass):
|
2069
|
-
# Identity
|
2070
2082
|
kernel = field(type=StateKernel)
|
2071
|
-
history = field(type=list) # List[Action2]
|
2072
|
-
players = field(type=list) # List[Player]
|
2073
|
-
nodes = field(type=list) # List[Node]
|
2074
|
-
edges = field(type=list) # List[BiEdge]
|
2075
|
-
regions = field(type=list) # List[Region]
|
2076
|
-
piles = field(type=list) # List[Pile]
|
2077
|
-
last_to_play = field(type=(int, type(None)), initial=None)
|
2078
|
-
|
2079
|
-
idx2path = field(type=list) # List[Path2]
|
2080
2083
|
|
2081
|
-
#
|
2082
|
-
|
2083
|
-
|
2084
|
-
rng = field(type=random.Random)
|
2085
|
-
|
2084
|
+
# Identity - these fields should be in the kernel (can only be changed in "get_initial_state" and "get_next_state")
|
2085
|
+
# The struture should be this: uuid2segment, uuid2edge, uuid2path, uuid2region, uuid2node
|
2086
|
+
|
2086
2087
|
# Memoized
|
2087
|
-
player_graphs = field(type=list) # List[PlayerGraph]
|
2088
2088
|
legal_actions_3 = field(type=list) # List[LegalAction]
|
2089
|
+
bonus_statuses_3 = field(type=list) # List[BonusStatus]
|
2090
|
+
player_scores_3 = field(type=list) # List[PlayerScore2]
|
2091
|
+
winners = field(type=list) # List[int]
|
2092
|
+
|
2093
|
+
player_graphs = field(type=list) # List[PlayerGraph]
|
2089
2094
|
player_scores = field(type=list) # List[PlayerScore2]
|
2090
2095
|
bonus_statuses = field(type=list) # List[BonusStatus]
|
2091
|
-
winners = field(type=list) # List[int]
|
2092
2096
|
|
2093
2097
|
def __todict__(self):
|
2094
2098
|
return {
|
2095
2099
|
"kernel": self.kernel.__todict__(),
|
2096
|
-
"
|
2097
|
-
"
|
2098
|
-
"history": [x.__todict__() for x in self.history],
|
2099
|
-
"player_scores": [x.__todict__() for x in self.player_scores],
|
2100
|
-
"player_graphs": [x.__todict__() for x in self.player_graphs],
|
2101
|
-
"nodes": [node.__todict__() for node in self.nodes],
|
2102
|
-
"edges": [edge.__todict__() for edge in self.edges],
|
2103
|
-
"regions": [region.__todict__() for region in self.regions],
|
2100
|
+
"bonus_statuses_3": [status.__todict__() for status in self.bonus_statuses_3],
|
2101
|
+
"player_scores_3": [x.__todict__() for x in self.player_scores_3],
|
2104
2102
|
"legal_actions_3": [x.__todict__() for x in self.legal_actions_3],
|
2105
|
-
"piles": [pile.__todict__() for pile in self.piles],
|
2106
|
-
"players": [player.__todict__() for player in self.players],
|
2107
|
-
"player_idxs": self.player_idxs,
|
2108
|
-
"decks": [deck.__todict__() for deck in self.decks],
|
2109
|
-
"rng": rng2json(self.rng),
|
2110
|
-
"last_to_play": self.last_to_play,
|
2111
|
-
"winners": self.winners,
|
2112
2103
|
}
|
2113
2104
|
@staticmethod
|
2114
2105
|
def __fromdict__(d):
|
2115
2106
|
return State(
|
2116
2107
|
kernel=StateKernel.__fromdict__(d["kernel"]),
|
2117
|
-
|
2118
|
-
|
2119
|
-
history=[Action2.__fromdict__(x) for x in d["history"]],
|
2120
|
-
player_scores=[PlayerScore2.__fromdict__(x) for x in d["player_scores"]],
|
2121
|
-
player_graphs=[PlayerGraph.__fromdict__(x) for x in d["player_graphs"]],
|
2122
|
-
nodes=[Node.__fromdict__(n) for n in d["nodes"]],
|
2123
|
-
edges=[BiEdge.__fromdict__(e) for e in d["edges"]],
|
2124
|
-
regions=[Region.__fromdict__(r) for r in d["regions"]],
|
2108
|
+
bonus_statuses_3=[BonusStatus.__fromdict__(x) for x in d["bonus_statuses_3"]],
|
2109
|
+
player_scores_3=[PlayerScore2.__fromdict__(x) for x in d["player_scores_3"]],
|
2125
2110
|
legal_actions_3=[LegalAction.__fromdict__(x) for x in d["legal_actions_3"]],
|
2126
|
-
piles=[Pile.__fromdict__(p) for p in d["piles"]],
|
2127
|
-
players=[Player.__fromdict__(p) for p in d["players"]],
|
2128
|
-
player_idxs=d["player_idxs"],
|
2129
|
-
decks=[Deck.__fromdict__(deck) for deck in d["decks"]],
|
2130
|
-
rng=json2rng(d["rng"]),
|
2131
|
-
last_to_play=d.get("last_to_play"),
|
2132
|
-
winners=d["winners"],
|
2133
2111
|
)
|
2134
2112
|
|
2135
2113
|
|
@@ -2625,12 +2603,6 @@ def getinitialstate(game_config):
|
|
2625
2603
|
nodeuuid2idx = {node.uuid: idx for idx, node in enumerate(board_config.points)}
|
2626
2604
|
edges = get_edges(rng, board_config, nodeuuid2idx)
|
2627
2605
|
|
2628
|
-
idx2path = []
|
2629
|
-
|
2630
|
-
for edge in edges:
|
2631
|
-
for path in edge.paths:
|
2632
|
-
idx2path.append(path)
|
2633
|
-
|
2634
2606
|
bonuses = game_config.fig.board_config.bonuses
|
2635
2607
|
bonus_statuses = [
|
2636
2608
|
BonusStatus(
|
@@ -2647,57 +2619,81 @@ def getinitialstate(game_config):
|
|
2647
2619
|
)
|
2648
2620
|
for bonus in bonuses
|
2649
2621
|
]
|
2650
|
-
|
2651
|
-
starting_decks=[Deck.__fromdict__(x.__todict__()) for x in decks]
|
2652
|
-
starting_piles=[Pile.__fromdict__(x.__todict__()) for x in piles]
|
2653
|
-
|
2654
|
-
state = State(
|
2655
|
-
idx2path=idx2path,
|
2656
|
-
bonus_statuses=bonus_statuses,
|
2657
|
-
history=[],
|
2658
|
-
player_scores=[PlayerScore2(public_items=[], private_items=[]) for _ in range(game_config.num_players)],
|
2659
|
-
player_graphs=[
|
2660
|
-
PlayerGraph(
|
2661
|
-
player_idx=player_idx,
|
2662
|
-
neighbors=[[] for _ in range(len(nodes))]
|
2663
|
-
) for player_idx in range(game_config.num_players)
|
2664
|
-
],
|
2665
|
-
nodes = nodes,
|
2666
|
-
edges = edges,
|
2667
|
-
regions = get_regions(board_config),
|
2668
|
-
legal_actions_3=[],
|
2669
|
-
piles=piles,
|
2670
|
-
players=[Player(idx=idx, pieces=[], cards=[], discard_tray=[]) for idx in range(game_config.num_players)],
|
2671
|
-
player_idxs=list(range(game_config.num_players)),
|
2672
|
-
decks=decks,
|
2673
|
-
rng=rng,
|
2674
|
-
last_to_play=None,
|
2675
|
-
winners=[],
|
2676
|
-
)
|
2677
2622
|
|
2678
|
-
state =
|
2623
|
+
# state = State(
|
2624
|
+
# bonus_statuses=bonus_statuses,
|
2625
|
+
# history=[],
|
2626
|
+
# player_scores=[PlayerScore2(public_items=[], private_items=[]) for _ in range(game_config.num_players)],
|
2627
|
+
# player_graphs=[
|
2628
|
+
# PlayerGraph(
|
2629
|
+
# player_idx=player_idx,
|
2630
|
+
# neighbors=[[] for _ in range(len(nodes))]
|
2631
|
+
# ) for player_idx in range(game_config.num_players)
|
2632
|
+
# ],
|
2633
|
+
# nodes = nodes,
|
2634
|
+
# edges = edges,
|
2635
|
+
# regions = get_regions(board_config),
|
2636
|
+
# legal_actions_3=[],
|
2637
|
+
# piles=piles,
|
2638
|
+
# players=[Player(idx=idx, pieces=[], cards=[], discard_tray=[]) for idx in range(game_config.num_players)],
|
2639
|
+
# player_idxs=list(range(game_config.num_players)),
|
2640
|
+
# decks=decks,
|
2641
|
+
# rng=rng,
|
2642
|
+
# last_to_play=None,
|
2643
|
+
# winners=[],
|
2644
|
+
# )
|
2679
2645
|
|
2680
2646
|
kernel = init_state_kernel(
|
2681
2647
|
rng=rng,
|
2682
2648
|
game_config=game_config,
|
2683
|
-
edges=
|
2684
|
-
|
2685
|
-
|
2686
|
-
|
2687
|
-
|
2688
|
-
|
2689
|
-
|
2690
|
-
|
2691
|
-
|
2649
|
+
edges=edges,
|
2650
|
+
nodes=nodes,
|
2651
|
+
decks=decks,
|
2652
|
+
piles=piles,
|
2653
|
+
players=[Player(idx=idx, pieces=[], cards=[], discard_tray=[]) for idx in range(game_config.num_players)],
|
2654
|
+
player_idxs=list(range(game_config.num_players)),
|
2655
|
+
history=[],
|
2656
|
+
player_scores=[PlayerScore2(public_items=[], private_items=[]) for _ in range(game_config.num_players)],
|
2657
|
+
starting_decks=[Deck.__fromdict__(x.__todict__()) for x in decks],
|
2658
|
+
starting_piles=[Pile.__fromdict__(x.__todict__()) for x in piles],
|
2692
2659
|
goals=board_config.goals,
|
2693
2660
|
)
|
2694
2661
|
|
2695
|
-
|
2696
|
-
|
2662
|
+
kernel = run_kernel_hooks(kernel, INITIALIZATION_HOOKS, True)
|
2663
|
+
|
2664
|
+
state = State(
|
2665
|
+
kernel=kernel,
|
2666
|
+
legal_actions_3=calc_legal_actions3(kernel),
|
2667
|
+
bonus_statuses_3=calc_bonus_statuses3(kernel),
|
2668
|
+
)
|
2669
|
+
|
2670
|
+
state = state.set(player_scores_3=calc_player_scores3(state))
|
2671
|
+
state = state.set(winners=calc_winners_3(state))
|
2697
2672
|
|
2698
2673
|
return state
|
2699
2674
|
|
2700
2675
|
|
2676
|
+
@dispatch(StateKernel)
|
2677
|
+
def calc_bonus_statuses3(kernel):
|
2678
|
+
bonus_statuses = [
|
2679
|
+
calc_bonus_status3(kernel, bonus)
|
2680
|
+
for bonus in kernel.game_config.fig.board_config.bonuses
|
2681
|
+
]
|
2682
|
+
bonus_statuses = [bs for bs in bonus_statuses if bs is not None]
|
2683
|
+
return bonus_statuses
|
2684
|
+
|
2685
|
+
|
2686
|
+
@dispatch(State)
|
2687
|
+
def calc_player_scores3(state):
|
2688
|
+
player_scores = [
|
2689
|
+
PlayerScore2(
|
2690
|
+
public_items=score_public_items3(state, player_idx),
|
2691
|
+
private_items=score_private_items3(state.kernel, player_idx),
|
2692
|
+
) for player_idx in range(len(state.kernel.players))
|
2693
|
+
]
|
2694
|
+
return player_scores
|
2695
|
+
|
2696
|
+
|
2701
2697
|
def run_state_hooks(state, hooks, log=False):
|
2702
2698
|
return reduce(
|
2703
2699
|
lambda s, h: run_state_hook(s, h, log),
|
@@ -2706,6 +2702,15 @@ def run_state_hooks(state, hooks, log=False):
|
|
2706
2702
|
)
|
2707
2703
|
|
2708
2704
|
|
2705
|
+
@dispatch(StateKernel, list, bool)
|
2706
|
+
def run_kernel_hooks(kernel, hooks, log=False):
|
2707
|
+
return reduce(
|
2708
|
+
lambda k, h: run_kernel_hook(k, h, log),
|
2709
|
+
hooks,
|
2710
|
+
kernel
|
2711
|
+
)
|
2712
|
+
|
2713
|
+
|
2709
2714
|
def run_accept_action_hooks(state, action, hooks, log=False):
|
2710
2715
|
# Just like "run_state_action_hooks", but returns immediately returns True if any hook returns True, otherwise returns False
|
2711
2716
|
for hook in hooks:
|
@@ -2725,6 +2730,42 @@ def run_state_action_hooks(state, action, hooks, log=False):
|
|
2725
2730
|
)
|
2726
2731
|
|
2727
2732
|
|
2733
|
+
@dispatch(State, int)
|
2734
|
+
def score_public_items3(state, player_idx):
|
2735
|
+
items = []
|
2736
|
+
for edge in state.kernel.edges:
|
2737
|
+
for path in edge.paths:
|
2738
|
+
if len(path.segments) > 0:
|
2739
|
+
first_segment = path.segments[0]
|
2740
|
+
if first_segment.pieces and len(first_segment.pieces) > 0:
|
2741
|
+
first_piece = state.kernel.pieceuuid2piece[first_segment.pieces[0]]
|
2742
|
+
if first_piece.player_idx == player_idx:
|
2743
|
+
items.append(
|
2744
|
+
ScoreItem2(
|
2745
|
+
amount=edge.score,
|
2746
|
+
owns_path=ScoreItemOwnsPath(
|
2747
|
+
path_idx=path.idx,
|
2748
|
+
edge_uuid=edge.uuid,
|
2749
|
+
length=len(path.segments),
|
2750
|
+
),
|
2751
|
+
description="Player {} owns edge {}".format(player_idx, edge.uuid),
|
2752
|
+
)
|
2753
|
+
)
|
2754
|
+
for bonus_status in state.bonus_statuses_3:
|
2755
|
+
bonus_idx = state.kernel.bonusuuid2bonusidx.get(bonus_status.bonus_uuid)
|
2756
|
+
bonus = state.kernel.game_config.fig.board_config.bonuses[bonus_idx] if bonus_idx is not None else None
|
2757
|
+
if bonus:
|
2758
|
+
if player_idx in bonus_status.winners:
|
2759
|
+
items.append(
|
2760
|
+
ScoreItem2(
|
2761
|
+
amount=bonus.score,
|
2762
|
+
description="Player {} wins bonus {}".format(player_idx, bonus.code),
|
2763
|
+
bonus=ScoreItemBonus(bonus_uuid=bonus.uuid),
|
2764
|
+
)
|
2765
|
+
)
|
2766
|
+
return items
|
2767
|
+
|
2768
|
+
|
2728
2769
|
@dispatch(State, int)
|
2729
2770
|
def score_public_items(state, player_idx):
|
2730
2771
|
items = []
|
@@ -2789,6 +2830,34 @@ def score_private_items(state, player_idx):
|
|
2789
2830
|
return items
|
2790
2831
|
|
2791
2832
|
|
2833
|
+
@dispatch(StateKernel, int)
|
2834
|
+
def score_private_items3(kernel, player_idx):
|
2835
|
+
items = []
|
2836
|
+
goaluuid2goal = {goal.uuid: goal for goal in kernel.goals}
|
2837
|
+
goal_completions = get_goal_completions3(kernel, player_idx)
|
2838
|
+
complete_goal_uuids = [gc.goal_uuid for gc in goal_completions if gc.complete]
|
2839
|
+
incomplete_goal_uuids = [gc.goal_uuid for gc in goal_completions if not gc.complete]
|
2840
|
+
for complete_goal_uuid in complete_goal_uuids:
|
2841
|
+
goal = goaluuid2goal[complete_goal_uuid]
|
2842
|
+
items.append(
|
2843
|
+
ScoreItem2(
|
2844
|
+
amount=goal.score,
|
2845
|
+
description="Player {} completed goal {}".format(player_idx, complete_goal_uuid),
|
2846
|
+
goal=ScoreItemGoal(goal_uuid=complete_goal_uuid, complete=True)
|
2847
|
+
)
|
2848
|
+
)
|
2849
|
+
for incomplete_goal_uuid in incomplete_goal_uuids:
|
2850
|
+
goal = goaluuid2goal[incomplete_goal_uuid]
|
2851
|
+
items.append(
|
2852
|
+
ScoreItem2(
|
2853
|
+
amount=(-1*goal.score),
|
2854
|
+
description="Player {} incomplete goal {}".format(player_idx, incomplete_goal_uuid),
|
2855
|
+
goal=ScoreItemGoal(goal_uuid=incomplete_goal_uuid, complete=False)
|
2856
|
+
)
|
2857
|
+
)
|
2858
|
+
return items
|
2859
|
+
|
2860
|
+
|
2792
2861
|
def handle_last_to_play(game):
|
2793
2862
|
if game.last_to_play is None:
|
2794
2863
|
# If any player can less than 3 pieces, the game is terminal
|
@@ -2808,6 +2877,14 @@ def getfinalscores(game):
|
|
2808
2877
|
]
|
2809
2878
|
|
2810
2879
|
|
2880
|
+
@dispatch(State)
|
2881
|
+
def getfinalscores3(state):
|
2882
|
+
return [
|
2883
|
+
getpublicplayerscore(state, state.player_scores_3[player_idx]).total
|
2884
|
+
for player_idx in range(len(state.kernel.players))
|
2885
|
+
]
|
2886
|
+
|
2887
|
+
|
2811
2888
|
def handle_calc_winners(game):
|
2812
2889
|
if isterminal(game):
|
2813
2890
|
players_with_highest_score = []
|
@@ -2827,6 +2904,24 @@ def handle_calc_winners(game):
|
|
2827
2904
|
return game
|
2828
2905
|
|
2829
2906
|
|
2907
|
+
@dispatch(State)
|
2908
|
+
def calc_winners_3(state):
|
2909
|
+
if isterminal(state):
|
2910
|
+
players_with_highest_score = []
|
2911
|
+
highest_score = -1000
|
2912
|
+
final_scores = getfinalscores3(state)
|
2913
|
+
for player_idx in range(len(state.kernel.players)):
|
2914
|
+
final_score = final_scores[player_idx]
|
2915
|
+
if final_score > highest_score:
|
2916
|
+
highest_score = final_score
|
2917
|
+
players_with_highest_score = [player_idx]
|
2918
|
+
elif final_score == highest_score:
|
2919
|
+
players_with_highest_score.append(player_idx)
|
2920
|
+
return players_with_highest_score
|
2921
|
+
|
2922
|
+
return []
|
2923
|
+
|
2924
|
+
|
2830
2925
|
def default_handle_terminal(game):
|
2831
2926
|
game = handle_last_to_play(game)
|
2832
2927
|
game = handle_calc_winners(game)
|
@@ -2841,6 +2936,48 @@ def calc_bonus_status(game, bonus):
|
|
2841
2936
|
return None
|
2842
2937
|
|
2843
2938
|
|
2939
|
+
@dispatch(StateKernel, FrozenBonus)
|
2940
|
+
def calc_bonus_status3(kernel, bonus):
|
2941
|
+
if not bonus:
|
2942
|
+
return None
|
2943
|
+
if bonus.code == "longest-trail":
|
2944
|
+
return get_bonus_status_longest_trail3(kernel, bonus)
|
2945
|
+
return None
|
2946
|
+
|
2947
|
+
|
2948
|
+
@dispatch(StateKernel, FrozenBonus)
|
2949
|
+
def get_bonus_status_longest_trail3(kernel, bonus):
|
2950
|
+
longest_trail = 0
|
2951
|
+
winners = []
|
2952
|
+
player_longest_trail_lens = []
|
2953
|
+
|
2954
|
+
for player_idx in range(kernel.game_config.num_players):
|
2955
|
+
trail_length = get_longest_path_length3(kernel, player_idx)
|
2956
|
+
player_longest_trail_lens.append(trail_length)
|
2957
|
+
if trail_length > longest_trail:
|
2958
|
+
longest_trail = trail_length
|
2959
|
+
winners = [player_idx]
|
2960
|
+
elif trail_length == longest_trail and trail_length > 0:
|
2961
|
+
winners.append(player_idx)
|
2962
|
+
|
2963
|
+
player_statuses = [
|
2964
|
+
BonusPlayerStatus(
|
2965
|
+
player_idx=player_idx,
|
2966
|
+
score=bonus.score if player_idx in winners else 0,
|
2967
|
+
longest_trail=LongestTrailBonusPlayerStatus(
|
2968
|
+
length=player_longest_trail_len
|
2969
|
+
),
|
2970
|
+
)
|
2971
|
+
for player_idx, player_longest_trail_len in enumerate(player_longest_trail_lens)
|
2972
|
+
]
|
2973
|
+
|
2974
|
+
return BonusStatus(
|
2975
|
+
bonus_uuid=bonus.uuid,
|
2976
|
+
winners=winners,
|
2977
|
+
player_statuses=player_statuses,
|
2978
|
+
)
|
2979
|
+
|
2980
|
+
|
2844
2981
|
def get_bonus_status_longest_trail(state, bonus):
|
2845
2982
|
longest_trail = 0
|
2846
2983
|
winners = []
|
@@ -2894,137 +3031,142 @@ def default_handle_scoring(game):
|
|
2894
3031
|
return game.set(player_scores=player_scores)
|
2895
3032
|
|
2896
3033
|
|
2897
|
-
|
3034
|
+
@dispatch(StateKernel, Action2)
|
3035
|
+
def default_handle_action(kernel, action):
|
2898
3036
|
if action.legal_action.discard:
|
2899
|
-
return handle_discard_action(
|
3037
|
+
return handle_discard_action(kernel, action)
|
2900
3038
|
if action.legal_action.keep:
|
2901
|
-
return handle_keep_action(
|
3039
|
+
return handle_keep_action(kernel, action)
|
2902
3040
|
if action.legal_action.move_pieces_to_path:
|
2903
|
-
|
2904
|
-
return handle_after_move_pieces_to_path_action(
|
3041
|
+
kernel = handle_move_pieces_to_path_action(kernel, action)
|
3042
|
+
return handle_after_move_pieces_to_path_action(kernel, action)
|
2905
3043
|
if action.legal_action.faceup_draw:
|
2906
|
-
return handle_faceup_draw_action(
|
3044
|
+
return handle_faceup_draw_action(kernel, action)
|
2907
3045
|
if action.legal_action.draw:
|
2908
|
-
return handle_draw_action(
|
3046
|
+
return handle_draw_action(kernel, action)
|
2909
3047
|
if action.legal_action.draw_discard:
|
2910
|
-
return handle_draw_discard_action(
|
3048
|
+
return handle_draw_discard_action(kernel, action)
|
2911
3049
|
|
2912
|
-
return
|
3050
|
+
return kernel
|
2913
3051
|
|
2914
3052
|
|
2915
|
-
|
3053
|
+
@dispatch(StateKernel, Action2)
|
3054
|
+
def default_after_accept_action(kernel, action):
|
2916
3055
|
# Remove all actions for matching player of action
|
2917
|
-
if not
|
2918
|
-
return
|
3056
|
+
if not kernel or not action or not action.legal_action:
|
3057
|
+
return kernel
|
2919
3058
|
player_idx = action.legal_action.player_idx
|
2920
|
-
if player_idx < 0 or player_idx >= len(
|
2921
|
-
return
|
2922
|
-
|
2923
|
-
|
2924
|
-
]
|
2925
|
-
history = game.history + [action]
|
2926
|
-
return game.set(
|
2927
|
-
legal_actions_3=new_legal_actions,
|
3059
|
+
if player_idx < 0 or player_idx >= len(kernel.players):
|
3060
|
+
return kernel
|
3061
|
+
history = kernel.history + [action]
|
3062
|
+
return kernel.set(
|
2928
3063
|
history=history,
|
2929
3064
|
)
|
2930
3065
|
|
2931
3066
|
|
2932
|
-
|
2933
|
-
|
3067
|
+
@dispatch(StateKernel)
|
3068
|
+
def recycle_decks_if_needed(kernel):
|
3069
|
+
for deck_idx in range(len(kernel.decks)):
|
2934
3070
|
print("recycle_decks_if_needed for deck_idx =", deck_idx)
|
2935
|
-
|
2936
|
-
return
|
3071
|
+
kernel = recycle_if_needed(kernel, deck_idx)
|
3072
|
+
return kernel
|
2937
3073
|
|
2938
3074
|
|
2939
|
-
|
2940
|
-
|
2941
|
-
|
2942
|
-
|
3075
|
+
@dispatch(StateKernel)
|
3076
|
+
def replenish_decks_if_needed(kernel):
|
3077
|
+
for deck_idx in range(len(kernel.decks)):
|
3078
|
+
kernel = replenish_faceup_if_needed(kernel, deck_idx)
|
3079
|
+
return kernel
|
2943
3080
|
|
2944
3081
|
|
2945
|
-
|
2946
|
-
|
3082
|
+
@dispatch(StateKernel, int, int)
|
3083
|
+
def replenish_faceup_spot_if_needed(kernel, deck_idx, spot_idx):
|
3084
|
+
deck = kernel.decks[deck_idx]
|
2947
3085
|
|
2948
3086
|
if deck.faceup_spots[spot_idx] is not None:
|
2949
|
-
return
|
3087
|
+
return kernel
|
2950
3088
|
|
2951
3089
|
if len(deck.facedown_stack) == 0:
|
2952
|
-
return
|
3090
|
+
return kernel
|
2953
3091
|
|
2954
3092
|
deck.faceup_spots[spot_idx] = deck.facedown_stack.pop()
|
2955
3093
|
|
2956
|
-
|
3094
|
+
kernel = ensure_faceup_spots_valid(kernel)
|
2957
3095
|
|
2958
|
-
return
|
2959
|
-
decks=
|
3096
|
+
return kernel.set(
|
3097
|
+
decks=kernel.decks,
|
2960
3098
|
)
|
2961
3099
|
|
2962
3100
|
|
2963
|
-
|
2964
|
-
|
3101
|
+
@dispatch(StateKernel, int)
|
3102
|
+
def replenish_faceup_if_needed(kernel, deck_idx):
|
3103
|
+
deck = kernel.decks[deck_idx]
|
2965
3104
|
for spot_idx in range(len(deck.faceup_spots)):
|
2966
|
-
|
2967
|
-
return
|
3105
|
+
kernel = replenish_faceup_spot_if_needed(kernel, deck_idx, spot_idx)
|
3106
|
+
return kernel
|
2968
3107
|
|
2969
3108
|
|
2970
|
-
|
2971
|
-
|
3109
|
+
@dispatch(StateKernel, int)
|
3110
|
+
def recycle_if_needed(kernel, deck_idx):
|
3111
|
+
deck = kernel.decks[deck_idx]
|
2972
3112
|
if len(deck.facedown_stack) == 0:
|
2973
3113
|
shuffled_discards = list(deck.discard)
|
2974
|
-
|
3114
|
+
kernel.rng.shuffle(shuffled_discards)
|
2975
3115
|
deck = deck.set(
|
2976
3116
|
facedown_stack = shuffled_discards,
|
2977
3117
|
discard = []
|
2978
3118
|
)
|
2979
|
-
|
2980
|
-
return
|
3119
|
+
kernel = set_deck(kernel, deck.idx, deck)
|
3120
|
+
return kernel
|
2981
3121
|
|
2982
3122
|
|
2983
|
-
|
2984
|
-
|
2985
|
-
|
3123
|
+
@dispatch(StateKernel, Action2)
|
3124
|
+
def handle_draw_action(kernel, action):
|
3125
|
+
if not kernel or not action or not action.legal_action or not action.legal_action.draw:
|
3126
|
+
return kernel
|
2986
3127
|
legal_action = action.legal_action
|
2987
3128
|
draw = legal_action.draw
|
2988
|
-
player =
|
2989
|
-
deck =
|
3129
|
+
player = kernel.players[legal_action.player_idx]
|
3130
|
+
deck = kernel.decks[draw.deck_idx]
|
2990
3131
|
|
2991
3132
|
if len(deck.facedown_stack) == 0:
|
2992
|
-
return
|
3133
|
+
return kernel # No cards to draw
|
2993
3134
|
|
2994
3135
|
for _ in range(draw.quantity):
|
2995
3136
|
drawn_card = deck.facedown_stack.pop()
|
2996
3137
|
player.cards.append(drawn_card)
|
2997
|
-
|
3138
|
+
kernel = recycle_if_needed(kernel, draw.deck_idx)
|
2998
3139
|
|
2999
|
-
|
3000
|
-
players=
|
3140
|
+
kernel = kernel.set(
|
3141
|
+
players=kernel.players,
|
3001
3142
|
)
|
3002
3143
|
|
3003
3144
|
# TODO: extract this out to user-defined function/hook
|
3004
3145
|
# if draw.quantity == 1:
|
3005
3146
|
# game = append_follow_up_draw_legal_actions(game, action)
|
3006
3147
|
|
3007
|
-
return
|
3148
|
+
return kernel
|
3008
3149
|
|
3009
3150
|
|
3010
|
-
|
3011
|
-
|
3012
|
-
|
3151
|
+
@dispatch(StateKernel, Action2)
|
3152
|
+
def handle_draw_discard_action(kernel, action):
|
3153
|
+
if not kernel or not action or not action.legal_action or not action.legal_action.draw_discard:
|
3154
|
+
return kernel
|
3013
3155
|
legal_action = action.legal_action
|
3014
3156
|
draw_discard = legal_action.draw_discard
|
3015
|
-
player =
|
3016
|
-
deck =
|
3157
|
+
player = kernel.players[legal_action.player_idx]
|
3158
|
+
deck = kernel.decks[draw_discard.deck_idx]
|
3017
3159
|
|
3018
3160
|
if len(deck.facedown_stack) == 0:
|
3019
|
-
return
|
3161
|
+
return kernel # No cards to draw
|
3020
3162
|
|
3021
3163
|
for _ in range(draw_discard.quantity):
|
3022
3164
|
drawn_card = deck.facedown_stack.pop()
|
3023
3165
|
player.discard_tray.append(drawn_card)
|
3024
3166
|
|
3025
|
-
return
|
3026
|
-
players=
|
3027
|
-
decks=
|
3167
|
+
return kernel.set(
|
3168
|
+
players=kernel.players,
|
3169
|
+
decks=kernel.decks,
|
3028
3170
|
)
|
3029
3171
|
|
3030
3172
|
|
@@ -3071,26 +3213,29 @@ def get_follow_up_draw_legal_actions(game, action):
|
|
3071
3213
|
return to_return
|
3072
3214
|
|
3073
3215
|
|
3074
|
-
|
3075
|
-
|
3216
|
+
@dispatch(StateKernel, int)
|
3217
|
+
def get_num_faceup_wilds(kernel, deck_idx):
|
3218
|
+
deck = kernel.decks[deck_idx]
|
3076
3219
|
non_empty_spots = [spot for spot in deck.faceup_spots if spot]
|
3077
3220
|
if not non_empty_spots:
|
3078
3221
|
return 0
|
3079
|
-
return sum(1 for card_uuid in non_empty_spots if
|
3222
|
+
return sum(1 for card_uuid in non_empty_spots if kernel.carduuid2card[card_uuid].is_wild)
|
3080
3223
|
|
3081
3224
|
|
3082
|
-
|
3083
|
-
|
3225
|
+
@dispatch(StateKernel)
|
3226
|
+
def ensure_faceup_spots_valid(kernel):
|
3227
|
+
for deck in kernel.decks:
|
3084
3228
|
max_iters = 5
|
3085
3229
|
i = 0
|
3086
|
-
while i < max_iters and get_num_faceup_wilds(
|
3087
|
-
|
3230
|
+
while i < max_iters and get_num_faceup_wilds(kernel, deck.idx) >= 3:
|
3231
|
+
kernel = discardfaceup_shuffle_flip(kernel, deck.idx)
|
3088
3232
|
i += 1
|
3089
|
-
return
|
3233
|
+
return kernel
|
3090
3234
|
|
3091
3235
|
|
3092
|
-
|
3093
|
-
|
3236
|
+
@dispatch(StateKernel, int)
|
3237
|
+
def discardfaceup_shuffle_flip(kernel, deck_idx):
|
3238
|
+
deck = kernel.decks[deck_idx]
|
3094
3239
|
num_faceup_spots = len(deck.faceup_spots)
|
3095
3240
|
for faceup_spot in deck.faceup_spots:
|
3096
3241
|
if faceup_spot:
|
@@ -3099,45 +3244,48 @@ def discardfaceup_shuffle_flip(game, deck_idx):
|
|
3099
3244
|
while len(deck.discard) > 0:
|
3100
3245
|
card = deck.discard.pop()
|
3101
3246
|
deck.facedown_stack.append(card)
|
3102
|
-
deck = shuffle(
|
3103
|
-
|
3104
|
-
|
3105
|
-
return
|
3247
|
+
deck = shuffle(kernel.rng, deck)
|
3248
|
+
kernel = set_deck(kernel, deck.idx, deck)
|
3249
|
+
kernel = flip_cards(kernel, deck.idx, num_faceup_spots)
|
3250
|
+
return kernel
|
3106
3251
|
|
3107
3252
|
|
3108
|
-
|
3109
|
-
|
3110
|
-
|
3253
|
+
@dispatch(StateKernel, Action2)
|
3254
|
+
def handle_faceup_draw_action(kernel, action):
|
3255
|
+
if not kernel or not action or not action.legal_action or not action.legal_action.faceup_draw:
|
3256
|
+
return kernel
|
3111
3257
|
legal_action = action.legal_action
|
3112
3258
|
faceup_draw = legal_action.faceup_draw
|
3113
|
-
player =
|
3114
|
-
deck =
|
3259
|
+
player = kernel.players[legal_action.player_idx]
|
3260
|
+
deck = kernel.decks[faceup_draw.deck_idx]
|
3115
3261
|
# find the faceup spot idx with uuid "faceup_draw.card_uuid"
|
3116
3262
|
spot_idx = next((i for i, card_uuid in enumerate(deck.faceup_spots) if card_uuid == faceup_draw.card_uuid), None)
|
3117
3263
|
drawn_card = deck.faceup_spots[spot_idx] if spot_idx is not None else None
|
3118
3264
|
player.cards.append(drawn_card)
|
3119
3265
|
|
3120
3266
|
deck.faceup_spots[spot_idx] = None
|
3121
|
-
|
3122
|
-
|
3267
|
+
kernel = kernel.set(decks=kernel.decks)
|
3268
|
+
kernel = replenish_faceup_spot_if_needed(kernel, faceup_draw.deck_idx, spot_idx)
|
3123
3269
|
|
3124
3270
|
# Prevent an extra draw if last draw was a face-wild-draw
|
3125
3271
|
# if not game.kernel.carduuid2card[drawn_card].is_wild:
|
3126
3272
|
# # TODO: extract this out to user-defined function/hook
|
3127
3273
|
# game = append_follow_up_draw_legal_actions(game, action)
|
3128
3274
|
|
3129
|
-
return
|
3130
|
-
players=
|
3275
|
+
return kernel.set(
|
3276
|
+
players=kernel.players,
|
3131
3277
|
)
|
3132
3278
|
|
3133
3279
|
|
3134
|
-
|
3135
|
-
|
3136
|
-
|
3280
|
+
@dispatch(StateKernel, Action2)
|
3281
|
+
def handle_after_move_pieces_to_path_action(kernel, action):
|
3282
|
+
kernel = handle_move_bonus_cards(kernel, action)
|
3283
|
+
return kernel
|
3137
3284
|
|
3138
3285
|
|
3139
|
-
|
3140
|
-
|
3286
|
+
@dispatch(StateKernel, Action2)
|
3287
|
+
def handle_move_bonus_cards(kernel, action):
|
3288
|
+
return handle_move_longest_path_card(kernel, action)
|
3141
3289
|
|
3142
3290
|
|
3143
3291
|
# Take a random walk on the player's graph. A node can be visiting more than once, but an edge cannot be visited more than once.
|
@@ -3181,12 +3329,53 @@ def random_player_graph_walk(game, player_idx):
|
|
3181
3329
|
return walk([random_start_node_idx], set([]))
|
3182
3330
|
|
3183
3331
|
|
3184
|
-
|
3332
|
+
@dispatch(StateKernel, int)
|
3333
|
+
def random_player_graph_walk3(kernel, player_idx):
|
3334
|
+
player_graph = kernel.player_graphs_3[player_idx]
|
3335
|
+
nodes_indices_with_neighbors = [i for i, neighbors in enumerate(player_graph.neighbors) if neighbors]
|
3336
|
+
|
3337
|
+
if not nodes_indices_with_neighbors:
|
3338
|
+
return None
|
3339
|
+
|
3340
|
+
rng = random.Random()
|
3341
|
+
random_start_node_idx = rng.choice(nodes_indices_with_neighbors)
|
3342
|
+
|
3343
|
+
def choose_next_node(visited_nodes, visited_edges):
|
3344
|
+
current_node_idx = visited_nodes[-1]
|
3345
|
+
neighbors = player_graph.neighbors[current_node_idx]
|
3346
|
+
if not neighbors:
|
3347
|
+
return None
|
3348
|
+
|
3349
|
+
shuffled_neighbors = neighbors.copy()
|
3350
|
+
rng.shuffle(shuffled_neighbors)
|
3351
|
+
|
3352
|
+
for neighbor in shuffled_neighbors:
|
3353
|
+
edge = (min(current_node_idx, neighbor), max(current_node_idx, neighbor))
|
3354
|
+
if edge not in visited_edges:
|
3355
|
+
return neighbor
|
3356
|
+
|
3357
|
+
return None
|
3358
|
+
|
3359
|
+
def walk(visited_nodes, visited_edges):
|
3360
|
+
next_node = choose_next_node(visited_nodes, visited_edges)
|
3361
|
+
if next_node is None:
|
3362
|
+
return (visited_nodes, visited_edges)
|
3363
|
+
next_edge = (min(visited_nodes[-1], next_node), max(visited_nodes[-1], next_node))
|
3364
|
+
return walk(
|
3365
|
+
visited_nodes + [next_node],
|
3366
|
+
set(list(visited_edges) + [next_edge])
|
3367
|
+
)
|
3368
|
+
|
3369
|
+
return walk([random_start_node_idx], set([]))
|
3370
|
+
|
3371
|
+
|
3372
|
+
@dispatch(StateKernel)
|
3373
|
+
def find_player_with_longest_path(kernel):
|
3185
3374
|
longest_path_player_idx = None
|
3186
3375
|
longest_path_length = 0
|
3187
3376
|
|
3188
|
-
for player_idx in range(
|
3189
|
-
path_length = get_longest_path_length(
|
3377
|
+
for player_idx in range(kernel.game_config.num_players):
|
3378
|
+
path_length = get_longest_path_length(kernel, player_idx)
|
3190
3379
|
if path_length > longest_path_length:
|
3191
3380
|
longest_path_length = path_length
|
3192
3381
|
longest_path_player_idx = player_idx
|
@@ -3194,7 +3383,7 @@ def find_player_with_longest_path(state):
|
|
3194
3383
|
if longest_path_player_idx is None:
|
3195
3384
|
return None
|
3196
3385
|
|
3197
|
-
return
|
3386
|
+
return kernel.players[longest_path_player_idx]
|
3198
3387
|
|
3199
3388
|
|
3200
3389
|
@dispatch(State, set)
|
@@ -3215,15 +3404,34 @@ def calc_path_len_from_edges(state, edge_tuples):
|
|
3215
3404
|
return sum(edge_lens)
|
3216
3405
|
|
3217
3406
|
|
3218
|
-
|
3407
|
+
@dispatch(StateKernel, set)
|
3408
|
+
def calc_path_len_from_edges3(kernel, edge_tuples):
|
3409
|
+
if edge_tuples is None:
|
3410
|
+
return 0
|
3411
|
+
edge_lens = []
|
3412
|
+
for edge_tuple in edge_tuples:
|
3413
|
+
edge_uuid = kernel.edgetuple2uuid.get(edge_tuple)
|
3414
|
+
edge_idx = kernel.edgeuuid2idx.get(edge_uuid)
|
3415
|
+
if edge_idx is not None:
|
3416
|
+
edge = kernel.edges[edge_idx]
|
3417
|
+
if edge and edge.paths:
|
3418
|
+
first_path = edge.paths[0]
|
3419
|
+
edge_len = len(first_path.segments)
|
3420
|
+
edge_lens.append(edge_len)
|
3421
|
+
|
3422
|
+
return sum(edge_lens)
|
3423
|
+
|
3424
|
+
|
3425
|
+
@dispatch(StateKernel, int)
|
3426
|
+
def get_longest_path_length3(kernel, player_idx):
|
3219
3427
|
longest_path_length = 0
|
3220
3428
|
num_iters_since_change = 0
|
3221
3429
|
|
3222
3430
|
for _ in range(1000):
|
3223
|
-
walk =
|
3431
|
+
walk = random_player_graph_walk3(kernel, player_idx)
|
3224
3432
|
if not walk:
|
3225
3433
|
return 0
|
3226
|
-
path_len =
|
3434
|
+
path_len = calc_path_len_from_edges3(kernel, walk[1])
|
3227
3435
|
if path_len > longest_path_length:
|
3228
3436
|
longest_path_length = path_len
|
3229
3437
|
num_iters_since_change = 0
|
@@ -3237,25 +3445,49 @@ def get_longest_path_length(game, player_idx):
|
|
3237
3445
|
return longest_path_length
|
3238
3446
|
|
3239
3447
|
|
3240
|
-
|
3241
|
-
|
3448
|
+
@dispatch(StateKernel, int)
|
3449
|
+
def get_longest_path_length(kernel, player_idx):
|
3450
|
+
longest_path_length = 0
|
3451
|
+
num_iters_since_change = 0
|
3452
|
+
|
3453
|
+
for _ in range(1000):
|
3454
|
+
walk = random_player_graph_walk(kernel, player_idx)
|
3455
|
+
if not walk:
|
3456
|
+
return 0
|
3457
|
+
path_len = calc_path_len_from_edges(kernel, walk[1])
|
3458
|
+
if path_len > longest_path_length:
|
3459
|
+
longest_path_length = path_len
|
3460
|
+
num_iters_since_change = 0
|
3461
|
+
else:
|
3462
|
+
num_iters_since_change += 1
|
3463
|
+
# If we haven't found a longer path in 500 iterations, return
|
3464
|
+
if num_iters_since_change > 500:
|
3465
|
+
return longest_path_length
|
3466
|
+
|
3467
|
+
print("longest_path_length: ", longest_path_length)
|
3468
|
+
return longest_path_length
|
3242
3469
|
|
3243
|
-
|
3244
|
-
|
3470
|
+
|
3471
|
+
@dispatch(StateKernel, Action2)
|
3472
|
+
def handle_move_longest_path_card(kernel, action):
|
3473
|
+
player = find_player_with_longest_path(kernel)
|
3474
|
+
|
3475
|
+
if not kernel or not player:
|
3476
|
+
return kernel
|
3245
3477
|
|
3246
3478
|
longest_path_card = None
|
3247
3479
|
|
3248
|
-
if len(
|
3249
|
-
longest_path_deck =
|
3480
|
+
if len(kernel.decks) > 2:
|
3481
|
+
longest_path_deck = kernel.decks[2]
|
3250
3482
|
if longest_path_deck.facedown_stack:
|
3251
3483
|
# Move the longest path card to the player's hand
|
3252
3484
|
longest_path_card = longest_path_deck.facedown_stack.pop()
|
3253
3485
|
else:
|
3254
3486
|
# Search for the longest path card in each player's cards
|
3255
|
-
player_with_card = find_player_with_longest_path_card(
|
3487
|
+
player_with_card = find_player_with_longest_path_card(kernel)
|
3256
3488
|
if player_with_card:
|
3257
3489
|
# Find the index of the card from the player's cards, then pop it
|
3258
|
-
card_idx = next((i for i, card_uuid in enumerate(player_with_card.cards) if
|
3490
|
+
card_idx = next((i for i, card_uuid in enumerate(player_with_card.cards) if kernel.carduuid2card[card_uuid].deck_idx == 2), None)
|
3259
3491
|
if card_idx is not None:
|
3260
3492
|
longest_path_card = player_with_card.cards.pop(card_idx)
|
3261
3493
|
|
@@ -3263,9 +3495,9 @@ def handle_move_longest_path_card(game, action):
|
|
3263
3495
|
if longest_path_card:
|
3264
3496
|
player.cards.append(longest_path_card)
|
3265
3497
|
|
3266
|
-
return
|
3267
|
-
players=
|
3268
|
-
decks=
|
3498
|
+
return kernel.set(
|
3499
|
+
players=kernel.players,
|
3500
|
+
decks=kernel.decks,
|
3269
3501
|
)
|
3270
3502
|
|
3271
3503
|
|
@@ -3276,9 +3508,10 @@ def find_player_with_longest_path_card(game):
|
|
3276
3508
|
return None
|
3277
3509
|
|
3278
3510
|
|
3279
|
-
|
3280
|
-
|
3281
|
-
|
3511
|
+
@dispatch(StateKernel, Action2)
|
3512
|
+
def handle_move_pieces_to_path_action(kernel, action):
|
3513
|
+
if not kernel or not action or not action.legal_action or not action.legal_action.move_pieces_to_path:
|
3514
|
+
return kernel
|
3282
3515
|
|
3283
3516
|
legal_action = action.legal_action
|
3284
3517
|
move_pieces_to_path = legal_action.move_pieces_to_path
|
@@ -3288,15 +3521,15 @@ def handle_move_pieces_to_path_action(game, action):
|
|
3288
3521
|
default = default.set(piece_uuids=override.piece_uuids)
|
3289
3522
|
default = default.set(card_uuids=override.card_uuids)
|
3290
3523
|
|
3291
|
-
player =
|
3524
|
+
player = kernel.players[legal_action.player_idx]
|
3292
3525
|
if not player or not player.pieces:
|
3293
|
-
return
|
3526
|
+
return kernel
|
3294
3527
|
|
3295
3528
|
path_idx = move_pieces_to_path.path_idx
|
3296
|
-
path =
|
3529
|
+
path = kernel.idx2path[path_idx]
|
3297
3530
|
|
3298
3531
|
if path is None or not path.segments:
|
3299
|
-
return
|
3532
|
+
return kernel
|
3300
3533
|
|
3301
3534
|
for piece_uuid, segment in zip(default.piece_uuids, path.segments):
|
3302
3535
|
# Find the piece in the player's pieces
|
@@ -3313,15 +3546,14 @@ def handle_move_pieces_to_path_action(game, action):
|
|
3313
3546
|
# Remove the card from player's cards
|
3314
3547
|
card_uuid = player.cards.pop(card_idx)
|
3315
3548
|
# add to discard
|
3316
|
-
|
3549
|
+
kernel.decks[kernel.carduuid2card[card_uuid].deck_idx].discard.append(card_uuid)
|
3317
3550
|
|
3318
|
-
|
3319
|
-
|
3320
|
-
|
3321
|
-
|
3322
|
-
game = game.set(player_graphs=calc_player_graphs(game)) #
|
3551
|
+
kernel = recycle_decks_if_needed(kernel)
|
3552
|
+
kernel = replenish_decks_if_needed(kernel)
|
3553
|
+
kernel = kernel.set(players=kernel.players)
|
3554
|
+
kernel = kernel.set(idx2path=kernel.idx2path)
|
3323
3555
|
|
3324
|
-
return
|
3556
|
+
return kernel
|
3325
3557
|
|
3326
3558
|
|
3327
3559
|
@dispatch(State, int)
|
@@ -3346,6 +3578,27 @@ def calc_player_graph(state, player_idx):
|
|
3346
3578
|
)
|
3347
3579
|
|
3348
3580
|
|
3581
|
+
@dispatch(list, list, dict, int)
|
3582
|
+
def calc_player_graph3(nodes, edges, pieceuuid2piece, player_idx):
|
3583
|
+
node2neighbors = {node.idx: set() for node in nodes}
|
3584
|
+
|
3585
|
+
for edge in edges:
|
3586
|
+
for path in edge.paths:
|
3587
|
+
for segment in path.segments:
|
3588
|
+
if segment.pieces and segment.pieces[0] and pieceuuid2piece[segment.pieces[0]].player_idx == player_idx:
|
3589
|
+
# print(f"[path_idx {path.idx}] edge.start_point_uuid {edge.start_point_uuid} ({edge.node_1_idx}) connected to edge.end_point_uuid {edge.end_point_uuid} ({edge.node_2_idx}) player_idx: {player_idx}")
|
3590
|
+
node2neighbors[edge.node_1_idx].add(edge.node_2_idx)
|
3591
|
+
node2neighbors[edge.node_2_idx].add(edge.node_1_idx)
|
3592
|
+
|
3593
|
+
return PlayerGraph(
|
3594
|
+
player_idx=player_idx,
|
3595
|
+
neighbors=[
|
3596
|
+
list(node2neighbors.get(node_idx, set()))
|
3597
|
+
for node_idx in range(len(nodes))
|
3598
|
+
]
|
3599
|
+
)
|
3600
|
+
|
3601
|
+
|
3349
3602
|
def calc_player_graphs(state):
|
3350
3603
|
game_config = state.kernel.game_config
|
3351
3604
|
return [
|
@@ -3354,22 +3607,31 @@ def calc_player_graphs(state):
|
|
3354
3607
|
]
|
3355
3608
|
|
3356
3609
|
|
3610
|
+
@dispatch(list, list, dict, GameConfig)
|
3611
|
+
def calc_player_graphs3(nodes, edges, pieceuuid2piece, game_config):
|
3612
|
+
return [
|
3613
|
+
calc_player_graph3(nodes, edges, pieceuuid2piece, player_idx)
|
3614
|
+
for player_idx in range(game_config.num_players)
|
3615
|
+
]
|
3616
|
+
|
3617
|
+
|
3357
3618
|
# Does the opposite of handle_discard_action, i.e., it keeps the cards in the discard tray (the other cards are discarded)
|
3358
|
-
|
3359
|
-
|
3360
|
-
|
3619
|
+
@dispatch(StateKernel, Action2)
|
3620
|
+
def handle_keep_action(kernel, action):
|
3621
|
+
if not kernel or not action or not action.legal_action.keep:
|
3622
|
+
return kernel
|
3361
3623
|
|
3362
3624
|
deck_idx = action.legal_action.keep.deck_idx
|
3363
|
-
if deck_idx < 0 or deck_idx >= len(
|
3364
|
-
return
|
3625
|
+
if deck_idx < 0 or deck_idx >= len(kernel.decks):
|
3626
|
+
return kernel
|
3365
3627
|
|
3366
|
-
deck =
|
3628
|
+
deck = kernel.decks[deck_idx]
|
3367
3629
|
if not deck:
|
3368
|
-
return
|
3630
|
+
return kernel
|
3369
3631
|
|
3370
|
-
player =
|
3632
|
+
player = kernel.players[action.legal_action.player_idx]
|
3371
3633
|
if not player:
|
3372
|
-
return
|
3634
|
+
return kernel
|
3373
3635
|
|
3374
3636
|
# Keep the specified cards in the discard tray
|
3375
3637
|
kept_cards = [card_uuid for card_uuid in player.discard_tray if card_uuid in action.keep.card_uuids]
|
@@ -3383,32 +3645,33 @@ def handle_keep_action(game, action):
|
|
3383
3645
|
player.cards.extend([c for c in kept_cards if c is not None])
|
3384
3646
|
|
3385
3647
|
player.discard_tray.clear()
|
3386
|
-
return
|
3648
|
+
return kernel.set(decks=kernel.decks, players=kernel.players)
|
3387
3649
|
|
3388
3650
|
|
3389
|
-
|
3651
|
+
@dispatch(StateKernel, Action2)
|
3652
|
+
def handle_discard_action(kernel, action):
|
3390
3653
|
print("****************************** handle_discard_action 1", action)
|
3391
|
-
if not
|
3392
|
-
return
|
3654
|
+
if not kernel or not action or not action.legal_action.discard:
|
3655
|
+
return kernel
|
3393
3656
|
|
3394
3657
|
print("****************************** handle_discard_action 2", action)
|
3395
3658
|
|
3396
3659
|
deck_idx = action.legal_action.discard.deck_idx
|
3397
|
-
if deck_idx < 0 or deck_idx >= len(
|
3398
|
-
return
|
3660
|
+
if deck_idx < 0 or deck_idx >= len(kernel.decks):
|
3661
|
+
return kernel
|
3399
3662
|
|
3400
3663
|
print("****************************** handle_discard_action 3", deck_idx)
|
3401
3664
|
|
3402
|
-
deck =
|
3665
|
+
deck = kernel.decks[deck_idx]
|
3403
3666
|
print("****************************** handle_discard_action 3.5", deck)
|
3404
3667
|
if not deck:
|
3405
|
-
return
|
3668
|
+
return kernel
|
3406
3669
|
|
3407
3670
|
print("****************************** handle_discard_action 4")
|
3408
3671
|
|
3409
|
-
player =
|
3672
|
+
player = kernel.players[action.legal_action.player_idx]
|
3410
3673
|
if not player:
|
3411
|
-
return
|
3674
|
+
return kernel
|
3412
3675
|
|
3413
3676
|
# Keep the specified cards in the discard tray
|
3414
3677
|
non_kept_cards = [card_uuid for card_uuid in player.discard_tray if card_uuid in action.discard.card_uuids]
|
@@ -3426,22 +3689,24 @@ def handle_discard_action(game, action):
|
|
3426
3689
|
player.discard_tray.clear()
|
3427
3690
|
print("****************************** handle_discard_action 10 kept_cards: ", kept_cards)
|
3428
3691
|
|
3429
|
-
return
|
3692
|
+
return kernel.set(decks=kernel.decks, players=kernel.players)
|
3430
3693
|
|
3431
3694
|
|
3432
|
-
|
3433
|
-
|
3434
|
-
|
3695
|
+
@dispatch(StateKernel)
|
3696
|
+
def shuffle_all_decks(kernel):
|
3697
|
+
if not kernel or not kernel.decks:
|
3698
|
+
return kernel
|
3435
3699
|
|
3436
|
-
for i in range(len(
|
3437
|
-
|
3700
|
+
for i in range(len(kernel.decks)):
|
3701
|
+
kernel = shuffle_deck(kernel, i)
|
3438
3702
|
|
3439
|
-
return
|
3703
|
+
return kernel
|
3440
3704
|
|
3441
3705
|
|
3442
|
-
|
3443
|
-
|
3444
|
-
|
3706
|
+
@dispatch(StateKernel, int, Deck)
|
3707
|
+
def set_deck(kernel, deck_idx, deck):
|
3708
|
+
new_decks = kernel.decks[:deck_idx] + [deck] + kernel.decks[deck_idx + 1:]
|
3709
|
+
return kernel.set(decks=new_decks)
|
3445
3710
|
|
3446
3711
|
|
3447
3712
|
def shuffle(rng, deck):
|
@@ -3450,37 +3715,40 @@ def shuffle(rng, deck):
|
|
3450
3715
|
return deck.set(facedown_stack=shuffled_cards)
|
3451
3716
|
|
3452
3717
|
|
3453
|
-
|
3454
|
-
|
3455
|
-
|
3718
|
+
@dispatch(StateKernel, int)
|
3719
|
+
def shuffle_deck(kernel, deck_idx):
|
3720
|
+
if not kernel or not kernel.decks or deck_idx < 0 or deck_idx >= len(kernel.decks):
|
3721
|
+
return kernel
|
3456
3722
|
|
3457
|
-
deck =
|
3723
|
+
deck = kernel.decks[deck_idx]
|
3458
3724
|
if not deck or not deck.facedown_stack:
|
3459
|
-
return
|
3725
|
+
return kernel
|
3460
3726
|
|
3461
|
-
shuffled_deck = shuffle(
|
3462
|
-
|
3463
|
-
return
|
3727
|
+
shuffled_deck = shuffle(kernel.rng, deck)
|
3728
|
+
kernel = set_deck(kernel, deck_idx, shuffled_deck)
|
3729
|
+
return kernel
|
3464
3730
|
|
3465
3731
|
|
3466
|
-
|
3467
|
-
|
3468
|
-
|
3732
|
+
@dispatch(StateKernel)
|
3733
|
+
def shuffle_player_order(kernel):
|
3734
|
+
if not kernel or not kernel.player_idxs:
|
3735
|
+
return kernel
|
3469
3736
|
|
3470
|
-
rng =
|
3471
|
-
shuffled_idxs = list(
|
3737
|
+
rng = kernel.rng
|
3738
|
+
shuffled_idxs = list(kernel.player_idxs)
|
3472
3739
|
rng.shuffle(shuffled_idxs)
|
3473
3740
|
|
3474
|
-
return
|
3741
|
+
return kernel.set(player_idxs=shuffled_idxs)
|
3475
3742
|
|
3476
3743
|
|
3477
|
-
|
3478
|
-
|
3479
|
-
|
3744
|
+
@dispatch(StateKernel, int, int)
|
3745
|
+
def flip_cards(kernel, deck_idx, num_cards):
|
3746
|
+
if not kernel or deck_idx < 0 or deck_idx >= len(kernel.decks):
|
3747
|
+
return kernel
|
3480
3748
|
|
3481
|
-
deck =
|
3749
|
+
deck = kernel.decks[deck_idx]
|
3482
3750
|
if not deck or not deck.facedown_stack:
|
3483
|
-
return
|
3751
|
+
return kernel
|
3484
3752
|
|
3485
3753
|
# Flip the top num_cards from the facedown_stack to the faceup_spots
|
3486
3754
|
for _ in range(num_cards):
|
@@ -3488,52 +3756,55 @@ def flip_cards(game, deck_idx, num_cards):
|
|
3488
3756
|
card = deck.facedown_stack.pop()
|
3489
3757
|
deck.faceup_spots.append(card)
|
3490
3758
|
|
3491
|
-
return
|
3759
|
+
return kernel.set(decks=kernel.decks)
|
3492
3760
|
|
3493
3761
|
|
3494
|
-
|
3495
|
-
|
3496
|
-
|
3762
|
+
@dispatch(StateKernel)
|
3763
|
+
def distribute_all_piles(kernel):
|
3764
|
+
if not kernel or not kernel.piles:
|
3765
|
+
return kernel
|
3497
3766
|
|
3498
|
-
for pile in
|
3767
|
+
for pile in kernel.piles:
|
3499
3768
|
player_idx = pile.player_idx
|
3500
|
-
if player_idx < 0 or player_idx >= len(
|
3769
|
+
if player_idx < 0 or player_idx >= len(kernel.players):
|
3501
3770
|
continue
|
3502
3771
|
|
3503
|
-
player =
|
3772
|
+
player = kernel.players[player_idx]
|
3504
3773
|
player.pieces.extend(pile.pieces)
|
3505
3774
|
pile.pieces.clear() # Clear the pile after distribution
|
3506
3775
|
|
3507
|
-
return
|
3776
|
+
return kernel.set(players=kernel.players, piles=kernel.piles)
|
3508
3777
|
|
3509
3778
|
|
3510
|
-
|
3511
|
-
|
3512
|
-
|
3779
|
+
@dispatch(StateKernel, int, int)
|
3780
|
+
def deal_cards_to_each_player_discard_tray(kernel, deck_idx, num_cards):
|
3781
|
+
if not kernel or deck_idx < 0 or deck_idx >= len(kernel.decks):
|
3782
|
+
return kernel
|
3513
3783
|
|
3514
|
-
deck =
|
3784
|
+
deck = kernel.decks[deck_idx]
|
3515
3785
|
if not deck or not deck.facedown_stack:
|
3516
|
-
return
|
3786
|
+
return kernel
|
3517
3787
|
|
3518
|
-
for player in
|
3788
|
+
for player in kernel.players:
|
3519
3789
|
# Deal cards to the player's discard tray
|
3520
3790
|
for _ in range(num_cards):
|
3521
3791
|
if deck.facedown_stack:
|
3522
3792
|
card = deck.facedown_stack.pop()
|
3523
3793
|
player.discard_tray.append(card)
|
3524
3794
|
|
3525
|
-
return
|
3795
|
+
return kernel.set(players=kernel.players, decks=kernel.decks)
|
3526
3796
|
|
3527
3797
|
|
3528
|
-
|
3529
|
-
|
3530
|
-
|
3798
|
+
@dispatch(StateKernel, int, int)
|
3799
|
+
def deal_cards_to_each_player(kernel, deck_idx, num_cards):
|
3800
|
+
if not kernel or deck_idx < 0 or deck_idx >= len(kernel.decks):
|
3801
|
+
return kernel
|
3531
3802
|
|
3532
|
-
deck =
|
3803
|
+
deck = kernel.decks[deck_idx]
|
3533
3804
|
if not deck or not deck.facedown_stack:
|
3534
|
-
return
|
3805
|
+
return kernel
|
3535
3806
|
|
3536
|
-
for player in
|
3807
|
+
for player in kernel.players:
|
3537
3808
|
player_hand = player.cards
|
3538
3809
|
# Deal cards to the player's hand
|
3539
3810
|
for _ in range(num_cards):
|
@@ -3541,7 +3812,7 @@ def deal_cards_to_each_player(game, deck_idx, num_cards):
|
|
3541
3812
|
card = deck.facedown_stack.pop()
|
3542
3813
|
player_hand.append(card)
|
3543
3814
|
|
3544
|
-
return
|
3815
|
+
return kernel.set(players=kernel.players, decks=kernel.decks)
|
3545
3816
|
|
3546
3817
|
|
3547
3818
|
def default_accept_action(game, action):
|
@@ -3972,6 +4243,37 @@ def run_state_hook(state, hook, log=False):
|
|
3972
4243
|
raise Exception(msg) from e
|
3973
4244
|
|
3974
4245
|
|
4246
|
+
def run_kernel_hook(kernel, hook, log=False):
|
4247
|
+
|
4248
|
+
namespace = HOOK_NAMESPACE.copy()
|
4249
|
+
|
4250
|
+
try:
|
4251
|
+
# Execute the code string
|
4252
|
+
exec(hook.code, namespace)
|
4253
|
+
|
4254
|
+
# Retrieve the handler function
|
4255
|
+
handler_func = namespace.get('handler')
|
4256
|
+
|
4257
|
+
if handler_func is None or not callable(handler_func):
|
4258
|
+
raise ValueError("No callable function named 'handler' found in the code")
|
4259
|
+
|
4260
|
+
# Call the handler function
|
4261
|
+
result = handler_func(kernel)
|
4262
|
+
# if log:
|
4263
|
+
# print(f"****************************** Running initialization hook 3: {hook.uuid} {result.player_idxs}")
|
4264
|
+
# print(f"****************************** Result of handler(5, 3): {result}")
|
4265
|
+
return result
|
4266
|
+
|
4267
|
+
except SyntaxError as e:
|
4268
|
+
msg = f"Syntax error in initialization hook {hook.uuid}: {str(e)}"
|
4269
|
+
logging.error(msg, exc_info=True)
|
4270
|
+
raise SyntaxError(msg) from e
|
4271
|
+
except Exception as e:
|
4272
|
+
msg = f"Error in initialization hook {hook.uuid}: {str(e)}"
|
4273
|
+
logging.error(msg, exc_info=True)
|
4274
|
+
raise Exception(msg) from e
|
4275
|
+
|
4276
|
+
|
3975
4277
|
# Implementing the following Julia function:
|
3976
4278
|
# function getfaceupspots(f, unit_deck, unit_deck_idx)
|
3977
4279
|
# num_faceup_spots = getsettingvalue(f, :num_faceup_spots)
|
@@ -4073,7 +4375,11 @@ def get_next_player_shuffled_idx(state_kernel, last_action):
|
|
4073
4375
|
|
4074
4376
|
|
4075
4377
|
def init_state(kernel):
|
4076
|
-
|
4378
|
+
|
4379
|
+
legal_actions_3 = calc_legal_actions3(kernel)
|
4380
|
+
|
4381
|
+
return State(
|
4382
|
+
kernel=kernel,
|
4077
4383
|
rng=kernel.rng,
|
4078
4384
|
game_config=kernel.game_config,
|
4079
4385
|
edges=kernel.edges,
|
@@ -4083,41 +4389,31 @@ def init_state(kernel):
|
|
4083
4389
|
player_idxs=kernel.player_idxs,
|
4084
4390
|
history=kernel.history,
|
4085
4391
|
player_scores=kernel.player_scores,
|
4086
|
-
legal_actions_3=[],
|
4087
4392
|
is_terminal=False,
|
4088
4393
|
idx2path=kernel.idx2path,
|
4089
4394
|
carduuid2card=kernel.carduuid2card,
|
4395
|
+
legal_actions_3=legal_actions_3,
|
4090
4396
|
)
|
4091
|
-
state = state.set(legal_actions_3=calc_legal_actions3(kernel))
|
4092
|
-
return state
|
4093
4397
|
|
4094
4398
|
|
4095
4399
|
def getnextstate2(s, a, log=False):
|
4096
4400
|
is_legal, reason = isactionlegal2(s, a)
|
4097
4401
|
if not is_legal:
|
4098
4402
|
raise ValueError(f"Action is not legal: {a}. Reason: {reason}")
|
4099
|
-
|
4100
|
-
|
4101
|
-
|
4102
|
-
|
4103
|
-
|
4104
|
-
|
4105
|
-
|
4106
|
-
|
4107
|
-
|
4108
|
-
decks=s.decks,
|
4109
|
-
piles=s.piles,
|
4110
|
-
players=s.players,
|
4111
|
-
player_idxs=s.player_idxs,
|
4112
|
-
history=s.history,
|
4113
|
-
player_scores=s.player_scores,
|
4114
|
-
starting_decks=s.kernel.starting_decks,
|
4115
|
-
starting_piles=s.kernel.starting_piles,
|
4116
|
-
goals=s.kernel.goals,
|
4403
|
+
|
4404
|
+
kernel = s.kernel
|
4405
|
+
kernel = run_state_action_hooks(kernel, a, AFTER_ACCEPT_ACTION_HOOKS, log)
|
4406
|
+
kernel = run_state_action_hooks(kernel, a, HANDLE_ACTION_HOOKS, log)
|
4407
|
+
|
4408
|
+
state = State(
|
4409
|
+
kernel=kernel,
|
4410
|
+
legal_actions_3=calc_legal_actions3(kernel),
|
4411
|
+
bonus_statuses_3=calc_bonus_statuses3(kernel),
|
4117
4412
|
)
|
4118
|
-
|
4413
|
+
state = state.set(player_scores_3=calc_player_scores3(state))
|
4414
|
+
state = state.set(winners=calc_winners_3(state))
|
4119
4415
|
|
4120
|
-
return
|
4416
|
+
return state
|
4121
4417
|
|
4122
4418
|
|
4123
4419
|
def getpublicplayerscore(s, player_score):
|
@@ -4257,19 +4553,19 @@ def imagine_decks(public_state, private_state):
|
|
4257
4553
|
|
4258
4554
|
|
4259
4555
|
def imagine_state(public_state, private_state):
|
4260
|
-
|
4556
|
+
imagined_kernel = init_state_kernel(
|
4261
4557
|
rng=random.Random(),
|
4262
4558
|
game_config=public_state.game_config,
|
4263
4559
|
player_idxs=public_state.player_idxs,
|
4264
4560
|
piles=public_state.piles,
|
4265
4561
|
edges=public_state.edges,
|
4562
|
+
nodes=public_state.nodes,
|
4266
4563
|
decks=imagine_decks(public_state, private_state),
|
4267
4564
|
players=imagine_players(public_state, private_state),
|
4268
4565
|
history=imagine_history(public_state, private_state),
|
4269
4566
|
player_scores=imagine_player_scores(public_state, private_state),
|
4270
4567
|
)
|
4271
|
-
|
4272
|
-
return imagined_state
|
4568
|
+
return init_state(imagined_kernel)
|
4273
4569
|
|
4274
4570
|
|
4275
4571
|
def isterminal(s):
|
@@ -4284,22 +4580,22 @@ def getpublicstate(s):
|
|
4284
4580
|
allotted_times=get_max_allotted_times(s),
|
4285
4581
|
all_pieces=list(s.kernel.pieceuuid2piece.values()),
|
4286
4582
|
to_play_2=getpublictoplay(s),
|
4287
|
-
bonus_statuses=s.
|
4583
|
+
bonus_statuses=s.bonus_statuses_3,
|
4288
4584
|
starting_decks=s.kernel.starting_decks,
|
4289
4585
|
starting_piles=s.kernel.starting_piles,
|
4290
4586
|
history=get_public_history(s),
|
4291
4587
|
player_scores=get_public_player_scores(s),
|
4292
|
-
player_graphs=s.
|
4588
|
+
player_graphs=s.kernel.player_graphs_3,
|
4293
4589
|
goals=s.kernel.goals,
|
4294
|
-
nodes=s.nodes,
|
4295
|
-
edges=s.edges,
|
4296
|
-
regions=s.regions,
|
4590
|
+
nodes=s.kernel.nodes,
|
4591
|
+
edges=s.kernel.edges,
|
4592
|
+
regions=s.kernel.regions,
|
4297
4593
|
decks=[getpublicdeck(s, deck) for deck in s.decks],
|
4298
|
-
piles=s.piles,
|
4299
|
-
player_idxs=s.player_idxs,
|
4594
|
+
piles=s.kernel.piles,
|
4595
|
+
player_idxs=s.kernel.player_idxs,
|
4300
4596
|
players=[getpublicplayer(s, p) for p in s.players],
|
4301
|
-
last_to_play=s.last_to_play,
|
4302
|
-
winners=s.winners,
|
4597
|
+
last_to_play=s.kernel.last_to_play,
|
4598
|
+
winners=s.kernel.winners,
|
4303
4599
|
terminal=isterminal(s),
|
4304
4600
|
)
|
4305
4601
|
|
@@ -4467,6 +4763,7 @@ def get_legal_actions(s, player_idx):
|
|
4467
4763
|
return [a for a in s.legal_actions_3 if a.player_idx == player_idx]
|
4468
4764
|
|
4469
4765
|
|
4766
|
+
@dispatch(State, int)
|
4470
4767
|
def get_goal_completions(s, player_idx):
|
4471
4768
|
# print("len(get_goals(s, player_idx)): ", len(get_goals(s, player_idx)))
|
4472
4769
|
# print("len(s.history): ", len(s.history))
|
@@ -4480,10 +4777,24 @@ def get_goal_completions(s, player_idx):
|
|
4480
4777
|
]
|
4481
4778
|
|
4482
4779
|
|
4483
|
-
@dispatch(
|
4484
|
-
def
|
4485
|
-
|
4486
|
-
|
4780
|
+
@dispatch(StateKernel, int)
|
4781
|
+
def get_goal_completions3(kernel, player_idx):
|
4782
|
+
# print("len(get_goals(s, player_idx)): ", len(get_goals(s, player_idx)))
|
4783
|
+
# print("len(s.history): ", len(s.history))
|
4784
|
+
# print("player.cards: ", len(s.players[player_idx].cards))
|
4785
|
+
return [
|
4786
|
+
GoalCompletion(
|
4787
|
+
goal_uuid=goal.uuid,
|
4788
|
+
complete=is_goal_complete(kernel, goal, player_idx),
|
4789
|
+
)
|
4790
|
+
for goal in get_goals3(kernel, player_idx)
|
4791
|
+
]
|
4792
|
+
|
4793
|
+
|
4794
|
+
@dispatch(StateKernel, FrozenGoal, int)
|
4795
|
+
def is_goal_complete(kernel, goal, player_idx):
|
4796
|
+
graph = kernel.player_graphs_3[player_idx].neighbors
|
4797
|
+
node_idxs = [kernel.nodeuuid2idx[node_uuid] for node_uuid in goal.node_uuids]
|
4487
4798
|
return are_nodes_connected(
|
4488
4799
|
graph,
|
4489
4800
|
node_idxs,
|
@@ -4519,11 +4830,21 @@ def get_player_graph(s, player_idx):
|
|
4519
4830
|
return nodes
|
4520
4831
|
|
4521
4832
|
|
4522
|
-
@dispatch(
|
4523
|
-
def get_goals(
|
4524
|
-
player =
|
4525
|
-
goaluuid2goal = {goal.uuid: goal for goal in
|
4526
|
-
goal_uuids = [
|
4833
|
+
@dispatch(StateKernel, int)
|
4834
|
+
def get_goals(kernel, player_idx):
|
4835
|
+
player = kernel.players[player_idx]
|
4836
|
+
goaluuid2goal = {goal.uuid: goal for goal in kernel.goals}
|
4837
|
+
goal_uuids = [kernel.carduuid2card[card_uuid].goal_uuid for card_uuid in player.cards if kernel.carduuid2card[card_uuid].goal_uuid]
|
4838
|
+
return [
|
4839
|
+
goaluuid2goal[goal_uuid] for goal_uuid in goal_uuids if goal_uuid in goaluuid2goal
|
4840
|
+
]
|
4841
|
+
|
4842
|
+
|
4843
|
+
@dispatch(StateKernel, int)
|
4844
|
+
def get_goals3(kernel, player_idx):
|
4845
|
+
player = kernel.players[player_idx]
|
4846
|
+
goaluuid2goal = {goal.uuid: goal for goal in kernel.goals}
|
4847
|
+
goal_uuids = [kernel.carduuid2card[card_uuid].goal_uuid for card_uuid in player.cards if kernel.carduuid2card[card_uuid].goal_uuid]
|
4527
4848
|
return [
|
4528
4849
|
goaluuid2goal[goal_uuid] for goal_uuid in goal_uuids if goal_uuid in goaluuid2goal
|
4529
4850
|
]
|
@@ -5182,23 +5503,23 @@ def getqproxy0(ps, a, td):
|
|
5182
5503
|
return qproxyrecurse() if td > 0 else qproxybase()
|
5183
5504
|
|
5184
5505
|
|
5185
|
-
INIT_HOOK_1 = """def handler(
|
5186
|
-
return shuffle_all_decks(
|
5506
|
+
INIT_HOOK_1 = """def handler(kernel):
|
5507
|
+
return shuffle_all_decks(kernel)
|
5187
5508
|
"""
|
5188
|
-
INIT_HOOK_2 = """def handler(
|
5189
|
-
return deal_cards_to_each_player(
|
5509
|
+
INIT_HOOK_2 = """def handler(kernel):
|
5510
|
+
return deal_cards_to_each_player(kernel, 0, 4)
|
5190
5511
|
"""
|
5191
|
-
INIT_HOOK_3 = """def handler(
|
5192
|
-
return deal_cards_to_each_player_discard_tray(
|
5512
|
+
INIT_HOOK_3 = """def handler(kernel):
|
5513
|
+
return deal_cards_to_each_player_discard_tray(kernel, 1, 3)
|
5193
5514
|
"""
|
5194
|
-
INIT_HOOK_4 = """def handler(
|
5195
|
-
return distribute_all_piles(
|
5515
|
+
INIT_HOOK_4 = """def handler(kernel):
|
5516
|
+
return distribute_all_piles(kernel)
|
5196
5517
|
"""
|
5197
|
-
INIT_HOOK_5 = """def handler(
|
5198
|
-
return flip_cards(
|
5518
|
+
INIT_HOOK_5 = """def handler(kernel):
|
5519
|
+
return flip_cards(kernel, 0, 5)
|
5199
5520
|
"""
|
5200
|
-
INIT_HOOK_7 = """def handler(
|
5201
|
-
return shuffle_player_order(
|
5521
|
+
INIT_HOOK_7 = """def handler(kernel):
|
5522
|
+
return shuffle_player_order(kernel)
|
5202
5523
|
"""
|
5203
5524
|
|
5204
5525
|
INITIALIZATION_HOOKS = [
|
@@ -5240,9 +5561,10 @@ INITIALIZATION_HOOKS = [
|
|
5240
5561
|
),
|
5241
5562
|
]
|
5242
5563
|
|
5564
|
+
|
5243
5565
|
HANDLE_ACTION_HOOK_1 = """
|
5244
|
-
def handler(
|
5245
|
-
return default_handle_action(
|
5566
|
+
def handler(kernel, action):
|
5567
|
+
return default_handle_action(kernel, action)
|
5246
5568
|
"""
|
5247
5569
|
|
5248
5570
|
HANDLE_ACTION_HOOKS = [
|
@@ -5255,8 +5577,8 @@ HANDLE_ACTION_HOOKS = [
|
|
5255
5577
|
]
|
5256
5578
|
|
5257
5579
|
AFTER_ACCEPT_ACTION_HOOK_1 = """
|
5258
|
-
def handler(
|
5259
|
-
return default_after_accept_action(
|
5580
|
+
def handler(kernel, action):
|
5581
|
+
return default_after_accept_action(kernel, action)
|
5260
5582
|
"""
|
5261
5583
|
|
5262
5584
|
AFTER_ACCEPT_ACTION_HOOKS = [
|