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 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
- # Scoring
2082
- player_idxs = field(type=list) # List[int]
2083
- decks = field(type=list) # List[Deck]
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
- "idx2path": [v.__todict__() for v in self.idx2path],
2097
- "bonus_statuses": [status.__todict__() for status in self.bonus_statuses],
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
- idx2path=[Path2.__fromdict__(v) for v in d["idx2path"]],
2118
- bonus_statuses=[BonusStatus.__fromdict__(x) for x in d["bonus_statuses"]],
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 = run_state_hooks(state, INITIALIZATION_HOOKS, True)
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=state.edges,
2684
- decks=state.decks,
2685
- piles=state.piles,
2686
- players=state.players,
2687
- player_idxs=state.player_idxs,
2688
- history=state.history,
2689
- player_scores=state.player_scores,
2690
- starting_decks=starting_decks,
2691
- starting_piles=starting_piles,
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
- state = state.set(kernel=kernel)
2696
- state = state.set(legal_actions_3=calc_legal_actions3(kernel))
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
- def default_handle_action(game, action):
3034
+ @dispatch(StateKernel, Action2)
3035
+ def default_handle_action(kernel, action):
2898
3036
  if action.legal_action.discard:
2899
- return handle_discard_action(game, action)
3037
+ return handle_discard_action(kernel, action)
2900
3038
  if action.legal_action.keep:
2901
- return handle_keep_action(game, action)
3039
+ return handle_keep_action(kernel, action)
2902
3040
  if action.legal_action.move_pieces_to_path:
2903
- game = handle_move_pieces_to_path_action(game, action)
2904
- return handle_after_move_pieces_to_path_action(game, 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(game, action)
3044
+ return handle_faceup_draw_action(kernel, action)
2907
3045
  if action.legal_action.draw:
2908
- return handle_draw_action(game, action)
3046
+ return handle_draw_action(kernel, action)
2909
3047
  if action.legal_action.draw_discard:
2910
- return handle_draw_discard_action(game, action)
3048
+ return handle_draw_discard_action(kernel, action)
2911
3049
 
2912
- return game
3050
+ return kernel
2913
3051
 
2914
3052
 
2915
- def default_after_accept_action(game, action):
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 game or not action or not action.legal_action:
2918
- return game
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(game.players):
2921
- return game
2922
- new_legal_actions = [
2923
- la for la in game.legal_actions_3 if la.player_idx != player_idx
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
- def recycle_decks_if_needed(game):
2933
- for deck_idx in range(len(game.decks)):
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
- game = recycle_if_needed(game, deck_idx)
2936
- return game
3071
+ kernel = recycle_if_needed(kernel, deck_idx)
3072
+ return kernel
2937
3073
 
2938
3074
 
2939
- def replenish_decks_if_needed(game):
2940
- for deck_idx in range(len(game.decks)):
2941
- game = replenish_faceup_if_needed(game, deck_idx)
2942
- return game
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
- def replenish_faceup_spot_if_needed(game, deck_idx, spot_idx):
2946
- deck = game.decks[deck_idx]
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 game
3087
+ return kernel
2950
3088
 
2951
3089
  if len(deck.facedown_stack) == 0:
2952
- return game
3090
+ return kernel
2953
3091
 
2954
3092
  deck.faceup_spots[spot_idx] = deck.facedown_stack.pop()
2955
3093
 
2956
- game = ensure_faceup_spots_valid(game)
3094
+ kernel = ensure_faceup_spots_valid(kernel)
2957
3095
 
2958
- return game.set(
2959
- decks=game.decks,
3096
+ return kernel.set(
3097
+ decks=kernel.decks,
2960
3098
  )
2961
3099
 
2962
3100
 
2963
- def replenish_faceup_if_needed(game, deck_idx):
2964
- deck = game.decks[deck_idx]
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
- game = replenish_faceup_spot_if_needed(game, deck_idx, spot_idx)
2967
- return game
3105
+ kernel = replenish_faceup_spot_if_needed(kernel, deck_idx, spot_idx)
3106
+ return kernel
2968
3107
 
2969
3108
 
2970
- def recycle_if_needed(game, deck_idx):
2971
- deck = game.decks[deck_idx]
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
- game.rng.shuffle(shuffled_discards)
3114
+ kernel.rng.shuffle(shuffled_discards)
2975
3115
  deck = deck.set(
2976
3116
  facedown_stack = shuffled_discards,
2977
3117
  discard = []
2978
3118
  )
2979
- game = set_deck(game, deck.idx, deck)
2980
- return game
3119
+ kernel = set_deck(kernel, deck.idx, deck)
3120
+ return kernel
2981
3121
 
2982
3122
 
2983
- def handle_draw_action(game, action):
2984
- if not game or not action or not action.legal_action or not action.legal_action.draw:
2985
- return game
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 = game.players[legal_action.player_idx]
2989
- deck = game.decks[draw.deck_idx]
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 game # No cards to draw
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
- game = recycle_if_needed(game, draw.deck_idx)
3138
+ kernel = recycle_if_needed(kernel, draw.deck_idx)
2998
3139
 
2999
- game = game.set(
3000
- players=game.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 game
3148
+ return kernel
3008
3149
 
3009
3150
 
3010
- def handle_draw_discard_action(game, action):
3011
- if not game or not action or not action.legal_action or not action.legal_action.draw_discard:
3012
- return game
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 = game.players[legal_action.player_idx]
3016
- deck = game.decks[draw_discard.deck_idx]
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 game # No cards to draw
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 game.set(
3026
- players=game.players,
3027
- decks=game.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
- def get_num_faceup_wilds(game, deck_idx):
3075
- deck = game.decks[deck_idx]
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 game.kernel.carduuid2card[card_uuid].is_wild)
3222
+ return sum(1 for card_uuid in non_empty_spots if kernel.carduuid2card[card_uuid].is_wild)
3080
3223
 
3081
3224
 
3082
- def ensure_faceup_spots_valid(game):
3083
- for deck in game.decks:
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(game, deck.idx) >= 3:
3087
- game = discardfaceup_shuffle_flip(game, deck.idx)
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 game
3233
+ return kernel
3090
3234
 
3091
3235
 
3092
- def discardfaceup_shuffle_flip(game, deck_idx):
3093
- deck = game.decks[deck_idx]
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(game.rng, deck)
3103
- game = set_deck(game, deck.idx, deck)
3104
- game = flip_cards(game, deck.idx, num_faceup_spots)
3105
- return game
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
- def handle_faceup_draw_action(game, action):
3109
- if not game or not action or not action.legal_action or not action.legal_action.faceup_draw:
3110
- return game
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 = game.players[legal_action.player_idx]
3114
- deck = game.decks[faceup_draw.deck_idx]
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
- game = game.set(decks=game.decks)
3122
- game = replenish_faceup_spot_if_needed(game, faceup_draw.deck_idx, spot_idx)
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 game.set(
3130
- players=game.players,
3275
+ return kernel.set(
3276
+ players=kernel.players,
3131
3277
  )
3132
3278
 
3133
3279
 
3134
- def handle_after_move_pieces_to_path_action(game, action):
3135
- game = handle_move_bonus_cards(game, action)
3136
- return game
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
- def handle_move_bonus_cards(game, action):
3140
- return handle_move_longest_path_card(game, action)
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
- def find_player_with_longest_path(state):
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(state.kernel.game_config.num_players):
3189
- path_length = get_longest_path_length(state, player_idx)
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 state.players[longest_path_player_idx]
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
- def get_longest_path_length(game, player_idx):
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 = random_player_graph_walk(game, player_idx)
3431
+ walk = random_player_graph_walk3(kernel, player_idx)
3224
3432
  if not walk:
3225
3433
  return 0
3226
- path_len = calc_path_len_from_edges(game, walk[1])
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
- def handle_move_longest_path_card(game, action):
3241
- player = find_player_with_longest_path(game)
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
- if not game or not player:
3244
- return game
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(game.decks) > 2:
3249
- longest_path_deck = game.decks[2]
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(game)
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 game.kernel.carduuid2card[card_uuid].deck_idx == 2), None)
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 game.set(
3267
- players=game.players,
3268
- decks=game.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
- def handle_move_pieces_to_path_action(game, action):
3280
- if not game or not action or not action.legal_action or not action.legal_action.move_pieces_to_path:
3281
- return game
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 = game.players[legal_action.player_idx]
3524
+ player = kernel.players[legal_action.player_idx]
3292
3525
  if not player or not player.pieces:
3293
- return game
3526
+ return kernel
3294
3527
 
3295
3528
  path_idx = move_pieces_to_path.path_idx
3296
- path = game.idx2path[path_idx]
3529
+ path = kernel.idx2path[path_idx]
3297
3530
 
3298
3531
  if path is None or not path.segments:
3299
- return game
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
- game.decks[game.kernel.carduuid2card[card_uuid].deck_idx].discard.append(card_uuid)
3549
+ kernel.decks[kernel.carduuid2card[card_uuid].deck_idx].discard.append(card_uuid)
3317
3550
 
3318
- game = recycle_decks_if_needed(game)
3319
- game = replenish_decks_if_needed(game)
3320
- game = game.set(players=game.players)
3321
- game = game.set(idx2path=game.idx2path)
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 game
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
- def handle_keep_action(game, action):
3359
- if not game or not action or not action.legal_action.keep:
3360
- return game
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(game.decks):
3364
- return game
3625
+ if deck_idx < 0 or deck_idx >= len(kernel.decks):
3626
+ return kernel
3365
3627
 
3366
- deck = game.decks[deck_idx]
3628
+ deck = kernel.decks[deck_idx]
3367
3629
  if not deck:
3368
- return game
3630
+ return kernel
3369
3631
 
3370
- player = game.players[action.legal_action.player_idx]
3632
+ player = kernel.players[action.legal_action.player_idx]
3371
3633
  if not player:
3372
- return game
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 game.set(decks=game.decks, players=game.players)
3648
+ return kernel.set(decks=kernel.decks, players=kernel.players)
3387
3649
 
3388
3650
 
3389
- def handle_discard_action(game, action):
3651
+ @dispatch(StateKernel, Action2)
3652
+ def handle_discard_action(kernel, action):
3390
3653
  print("****************************** handle_discard_action 1", action)
3391
- if not game or not action or not action.legal_action.discard:
3392
- return game
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(game.decks):
3398
- return game
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 = game.decks[deck_idx]
3665
+ deck = kernel.decks[deck_idx]
3403
3666
  print("****************************** handle_discard_action 3.5", deck)
3404
3667
  if not deck:
3405
- return game
3668
+ return kernel
3406
3669
 
3407
3670
  print("****************************** handle_discard_action 4")
3408
3671
 
3409
- player = game.players[action.legal_action.player_idx]
3672
+ player = kernel.players[action.legal_action.player_idx]
3410
3673
  if not player:
3411
- return game
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 game.set(decks=game.decks, players=game.players)
3692
+ return kernel.set(decks=kernel.decks, players=kernel.players)
3430
3693
 
3431
3694
 
3432
- def shuffle_all_decks(game):
3433
- if not game or not game.decks:
3434
- return game
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(game.decks)):
3437
- game = shuffle_deck(game, i)
3700
+ for i in range(len(kernel.decks)):
3701
+ kernel = shuffle_deck(kernel, i)
3438
3702
 
3439
- return game
3703
+ return kernel
3440
3704
 
3441
3705
 
3442
- def set_deck(game, deck_idx, deck):
3443
- new_decks = game.decks[:deck_idx] + [deck] + game.decks[deck_idx + 1:]
3444
- return game.set(decks=new_decks)
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
- def shuffle_deck(game, deck_idx):
3454
- if not game or not game.decks or deck_idx < 0 or deck_idx >= len(game.decks):
3455
- return game
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 = game.decks[deck_idx]
3723
+ deck = kernel.decks[deck_idx]
3458
3724
  if not deck or not deck.facedown_stack:
3459
- return game
3725
+ return kernel
3460
3726
 
3461
- shuffled_deck = shuffle(game.rng, deck)
3462
- game = set_deck(game, deck_idx, shuffled_deck)
3463
- return game
3727
+ shuffled_deck = shuffle(kernel.rng, deck)
3728
+ kernel = set_deck(kernel, deck_idx, shuffled_deck)
3729
+ return kernel
3464
3730
 
3465
3731
 
3466
- def shuffle_player_order(game):
3467
- if not game or not game.player_idxs:
3468
- return game
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 = game.rng
3471
- shuffled_idxs = list(game.player_idxs)
3737
+ rng = kernel.rng
3738
+ shuffled_idxs = list(kernel.player_idxs)
3472
3739
  rng.shuffle(shuffled_idxs)
3473
3740
 
3474
- return game.set(player_idxs=shuffled_idxs)
3741
+ return kernel.set(player_idxs=shuffled_idxs)
3475
3742
 
3476
3743
 
3477
- def flip_cards(game, deck_idx, num_cards):
3478
- if not game or deck_idx < 0 or deck_idx >= len(game.decks):
3479
- return game
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 = game.decks[deck_idx]
3749
+ deck = kernel.decks[deck_idx]
3482
3750
  if not deck or not deck.facedown_stack:
3483
- return game
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 game.set(decks=game.decks)
3759
+ return kernel.set(decks=kernel.decks)
3492
3760
 
3493
3761
 
3494
- def distribute_all_piles(game):
3495
- if not game or not game.piles:
3496
- return game
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 game.piles:
3767
+ for pile in kernel.piles:
3499
3768
  player_idx = pile.player_idx
3500
- if player_idx < 0 or player_idx >= len(game.players):
3769
+ if player_idx < 0 or player_idx >= len(kernel.players):
3501
3770
  continue
3502
3771
 
3503
- player = game.players[player_idx]
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 game.set(players=game.players, piles=game.piles)
3776
+ return kernel.set(players=kernel.players, piles=kernel.piles)
3508
3777
 
3509
3778
 
3510
- def deal_cards_to_each_player_discard_tray(game, deck_idx, num_cards):
3511
- if not game or deck_idx < 0 or deck_idx >= len(game.decks):
3512
- return game
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 = game.decks[deck_idx]
3784
+ deck = kernel.decks[deck_idx]
3515
3785
  if not deck or not deck.facedown_stack:
3516
- return game
3786
+ return kernel
3517
3787
 
3518
- for player in game.players:
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 game.set(players=game.players, decks=game.decks)
3795
+ return kernel.set(players=kernel.players, decks=kernel.decks)
3526
3796
 
3527
3797
 
3528
- def deal_cards_to_each_player(game, deck_idx, num_cards):
3529
- if not game or deck_idx < 0 or deck_idx >= len(game.decks):
3530
- return game
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 = game.decks[deck_idx]
3803
+ deck = kernel.decks[deck_idx]
3533
3804
  if not deck or not deck.facedown_stack:
3534
- return game
3805
+ return kernel
3535
3806
 
3536
- for player in game.players:
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 game.set(players=game.players, decks=game.decks)
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
- state = State(
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
- s = run_state_action_hooks(s, a, AFTER_ACCEPT_ACTION_HOOKS, log)
4100
- s = run_state_action_hooks(s, a, HANDLE_ACTION_HOOKS, log)
4101
- s = run_state_hooks(s, HANDLE_SCORING_HOOKS, log)
4102
- s = run_state_hooks(s, HANDLE_TERMINAL_HOOKS, log)
4103
-
4104
- state_kernel = init_state_kernel(
4105
- rng=s.rng,
4106
- game_config=s.kernel.game_config,
4107
- edges=s.edges,
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
- s = s.set(legal_actions_3=calc_legal_actions3(state_kernel))
4413
+ state = state.set(player_scores_3=calc_player_scores3(state))
4414
+ state = state.set(winners=calc_winners_3(state))
4119
4415
 
4120
- return s
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
- kernel = init_state_kernel(
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
- imagined_state = init_state(kernel)
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.bonus_statuses,
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.player_graphs,
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(State, FrozenGoal, int)
4484
- def is_goal_complete(s, goal, player_idx):
4485
- graph = s.player_graphs[player_idx].neighbors
4486
- node_idxs = [s.kernel.nodeuuid2idx[node_uuid] for node_uuid in goal.node_uuids]
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(State, int)
4523
- def get_goals(s, player_idx):
4524
- player = s.players[player_idx]
4525
- goaluuid2goal = {goal.uuid: goal for goal in s.kernel.goals}
4526
- goal_uuids = [s.kernel.carduuid2card[card_uuid].goal_uuid for card_uuid in player.cards if s.kernel.carduuid2card[card_uuid].goal_uuid]
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(game):
5186
- return shuffle_all_decks(game)
5506
+ INIT_HOOK_1 = """def handler(kernel):
5507
+ return shuffle_all_decks(kernel)
5187
5508
  """
5188
- INIT_HOOK_2 = """def handler(game):
5189
- return deal_cards_to_each_player(game, 0, 4)
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(game):
5192
- return deal_cards_to_each_player_discard_tray(game, 1, 3)
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(game):
5195
- return distribute_all_piles(game)
5515
+ INIT_HOOK_4 = """def handler(kernel):
5516
+ return distribute_all_piles(kernel)
5196
5517
  """
5197
- INIT_HOOK_5 = """def handler(game):
5198
- return flip_cards(game, 0, 5)
5518
+ INIT_HOOK_5 = """def handler(kernel):
5519
+ return flip_cards(kernel, 0, 5)
5199
5520
  """
5200
- INIT_HOOK_7 = """def handler(game):
5201
- return shuffle_player_order(game)
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(game, action):
5245
- return default_handle_action(game, 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(game, action):
5259
- return default_after_accept_action(game, action)
5580
+ def handler(kernel, action):
5581
+ return default_after_accept_action(kernel, action)
5260
5582
  """
5261
5583
 
5262
5584
  AFTER_ACCEPT_ACTION_HOOKS = [