graph-games-proto 0.3.1991__py3-none-any.whl → 0.3.2031__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,79 +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
2082
  kernel = field(type=StateKernel)
2070
2083
 
2071
- # Identity: these fields should be in the kernel (set once at the start of the game and never changes)
2072
- rng = field(type=random.Random)
2073
-
2074
- # Identity: these fields should be in the kernel (can only be changed in "get_initial_state")
2075
- player_idxs = field(type=list) # List[int]
2076
-
2077
2084
  # Identity - these fields should be in the kernel (can only be changed in "get_initial_state" and "get_next_state")
2078
2085
  # The struture should be this: uuid2segment, uuid2edge, uuid2path, uuid2region, uuid2node
2079
- history = field(type=list) # List[Action2]
2080
- players = field(type=list) # List[Player]
2081
- nodes = field(type=list) # List[Node]
2082
- edges = field(type=list) # List[BiEdge]
2083
- regions = field(type=list) # List[Region]
2084
- piles = field(type=list) # List[Pile]
2085
- last_to_play = field(type=(int, type(None)), initial=None)
2086
- decks = field(type=list) # List[Deck]
2087
-
2088
- # Not sure yet
2089
- idx2path = field(type=list) # List[Path2]
2090
2086
 
2091
2087
  # Memoized
2092
- player_graphs = field(type=list) # List[PlayerGraph]
2093
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]
2094
2094
  player_scores = field(type=list) # List[PlayerScore2]
2095
2095
  bonus_statuses = field(type=list) # List[BonusStatus]
2096
- winners = field(type=list) # List[int]
2097
2096
 
2098
2097
  def __todict__(self):
2099
2098
  return {
2100
2099
  "kernel": self.kernel.__todict__(),
2101
- "idx2path": [v.__todict__() for v in self.idx2path],
2102
- "bonus_statuses": [status.__todict__() for status in self.bonus_statuses],
2103
- "history": [x.__todict__() for x in self.history],
2104
- "player_scores": [x.__todict__() for x in self.player_scores],
2105
- "player_graphs": [x.__todict__() for x in self.player_graphs],
2106
- "nodes": [node.__todict__() for node in self.nodes],
2107
- "edges": [edge.__todict__() for edge in self.edges],
2108
- "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],
2109
2102
  "legal_actions_3": [x.__todict__() for x in self.legal_actions_3],
2110
- "piles": [pile.__todict__() for pile in self.piles],
2111
- "players": [player.__todict__() for player in self.players],
2112
- "player_idxs": self.player_idxs,
2113
- "decks": [deck.__todict__() for deck in self.decks],
2114
- "rng": rng2json(self.rng),
2115
- "last_to_play": self.last_to_play,
2116
- "winners": self.winners,
2117
2103
  }
2118
2104
  @staticmethod
2119
2105
  def __fromdict__(d):
2120
2106
  return State(
2121
2107
  kernel=StateKernel.__fromdict__(d["kernel"]),
2122
- idx2path=[Path2.__fromdict__(v) for v in d["idx2path"]],
2123
- bonus_statuses=[BonusStatus.__fromdict__(x) for x in d["bonus_statuses"]],
2124
- history=[Action2.__fromdict__(x) for x in d["history"]],
2125
- player_scores=[PlayerScore2.__fromdict__(x) for x in d["player_scores"]],
2126
- player_graphs=[PlayerGraph.__fromdict__(x) for x in d["player_graphs"]],
2127
- nodes=[Node.__fromdict__(n) for n in d["nodes"]],
2128
- edges=[BiEdge.__fromdict__(e) for e in d["edges"]],
2129
- 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"]],
2130
2110
  legal_actions_3=[LegalAction.__fromdict__(x) for x in d["legal_actions_3"]],
2131
- piles=[Pile.__fromdict__(p) for p in d["piles"]],
2132
- players=[Player.__fromdict__(p) for p in d["players"]],
2133
- player_idxs=d["player_idxs"],
2134
- decks=[Deck.__fromdict__(deck) for deck in d["decks"]],
2135
- rng=json2rng(d["rng"]),
2136
- last_to_play=d.get("last_to_play"),
2137
- winners=d["winners"],
2138
2111
  )
2139
2112
 
2140
2113
 
@@ -2630,12 +2603,6 @@ def getinitialstate(game_config):
2630
2603
  nodeuuid2idx = {node.uuid: idx for idx, node in enumerate(board_config.points)}
2631
2604
  edges = get_edges(rng, board_config, nodeuuid2idx)
2632
2605
 
2633
- idx2path = []
2634
-
2635
- for edge in edges:
2636
- for path in edge.paths:
2637
- idx2path.append(path)
2638
-
2639
2606
  bonuses = game_config.fig.board_config.bonuses
2640
2607
  bonus_statuses = [
2641
2608
  BonusStatus(
@@ -2652,57 +2619,81 @@ def getinitialstate(game_config):
2652
2619
  )
2653
2620
  for bonus in bonuses
2654
2621
  ]
2655
-
2656
- starting_decks=[Deck.__fromdict__(x.__todict__()) for x in decks]
2657
- starting_piles=[Pile.__fromdict__(x.__todict__()) for x in piles]
2658
-
2659
- state = State(
2660
- idx2path=idx2path,
2661
- bonus_statuses=bonus_statuses,
2662
- history=[],
2663
- player_scores=[PlayerScore2(public_items=[], private_items=[]) for _ in range(game_config.num_players)],
2664
- player_graphs=[
2665
- PlayerGraph(
2666
- player_idx=player_idx,
2667
- neighbors=[[] for _ in range(len(nodes))]
2668
- ) for player_idx in range(game_config.num_players)
2669
- ],
2670
- nodes = nodes,
2671
- edges = edges,
2672
- regions = get_regions(board_config),
2673
- legal_actions_3=[],
2674
- piles=piles,
2675
- players=[Player(idx=idx, pieces=[], cards=[], discard_tray=[]) for idx in range(game_config.num_players)],
2676
- player_idxs=list(range(game_config.num_players)),
2677
- decks=decks,
2678
- rng=rng,
2679
- last_to_play=None,
2680
- winners=[],
2681
- )
2682
2622
 
2683
- 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
+ # )
2684
2645
 
2685
2646
  kernel = init_state_kernel(
2686
2647
  rng=rng,
2687
2648
  game_config=game_config,
2688
- edges=state.edges,
2689
- decks=state.decks,
2690
- piles=state.piles,
2691
- players=state.players,
2692
- player_idxs=state.player_idxs,
2693
- history=state.history,
2694
- player_scores=state.player_scores,
2695
- starting_decks=starting_decks,
2696
- 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],
2697
2659
  goals=board_config.goals,
2698
2660
  )
2699
2661
 
2700
- state = state.set(kernel=kernel)
2701
- 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))
2702
2672
 
2703
2673
  return state
2704
2674
 
2705
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
+
2706
2697
  def run_state_hooks(state, hooks, log=False):
2707
2698
  return reduce(
2708
2699
  lambda s, h: run_state_hook(s, h, log),
@@ -2711,6 +2702,15 @@ def run_state_hooks(state, hooks, log=False):
2711
2702
  )
2712
2703
 
2713
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
+
2714
2714
  def run_accept_action_hooks(state, action, hooks, log=False):
2715
2715
  # Just like "run_state_action_hooks", but returns immediately returns True if any hook returns True, otherwise returns False
2716
2716
  for hook in hooks:
@@ -2730,6 +2730,42 @@ def run_state_action_hooks(state, action, hooks, log=False):
2730
2730
  )
2731
2731
 
2732
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
+
2733
2769
  @dispatch(State, int)
2734
2770
  def score_public_items(state, player_idx):
2735
2771
  items = []
@@ -2794,6 +2830,34 @@ def score_private_items(state, player_idx):
2794
2830
  return items
2795
2831
 
2796
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
+
2797
2861
  def handle_last_to_play(game):
2798
2862
  if game.last_to_play is None:
2799
2863
  # If any player can less than 3 pieces, the game is terminal
@@ -2813,6 +2877,14 @@ def getfinalscores(game):
2813
2877
  ]
2814
2878
 
2815
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
+
2816
2888
  def handle_calc_winners(game):
2817
2889
  if isterminal(game):
2818
2890
  players_with_highest_score = []
@@ -2832,6 +2904,24 @@ def handle_calc_winners(game):
2832
2904
  return game
2833
2905
 
2834
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
+
2835
2925
  def default_handle_terminal(game):
2836
2926
  game = handle_last_to_play(game)
2837
2927
  game = handle_calc_winners(game)
@@ -2846,6 +2936,48 @@ def calc_bonus_status(game, bonus):
2846
2936
  return None
2847
2937
 
2848
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
+
2849
2981
  def get_bonus_status_longest_trail(state, bonus):
2850
2982
  longest_trail = 0
2851
2983
  winners = []
@@ -2899,137 +3031,142 @@ def default_handle_scoring(game):
2899
3031
  return game.set(player_scores=player_scores)
2900
3032
 
2901
3033
 
2902
- def default_handle_action(game, action):
3034
+ @dispatch(StateKernel, Action2)
3035
+ def default_handle_action(kernel, action):
2903
3036
  if action.legal_action.discard:
2904
- return handle_discard_action(game, action)
3037
+ return handle_discard_action(kernel, action)
2905
3038
  if action.legal_action.keep:
2906
- return handle_keep_action(game, action)
3039
+ return handle_keep_action(kernel, action)
2907
3040
  if action.legal_action.move_pieces_to_path:
2908
- game = handle_move_pieces_to_path_action(game, action)
2909
- 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)
2910
3043
  if action.legal_action.faceup_draw:
2911
- return handle_faceup_draw_action(game, action)
3044
+ return handle_faceup_draw_action(kernel, action)
2912
3045
  if action.legal_action.draw:
2913
- return handle_draw_action(game, action)
3046
+ return handle_draw_action(kernel, action)
2914
3047
  if action.legal_action.draw_discard:
2915
- return handle_draw_discard_action(game, action)
3048
+ return handle_draw_discard_action(kernel, action)
2916
3049
 
2917
- return game
3050
+ return kernel
2918
3051
 
2919
3052
 
2920
- def default_after_accept_action(game, action):
3053
+ @dispatch(StateKernel, Action2)
3054
+ def default_after_accept_action(kernel, action):
2921
3055
  # Remove all actions for matching player of action
2922
- if not game or not action or not action.legal_action:
2923
- return game
3056
+ if not kernel or not action or not action.legal_action:
3057
+ return kernel
2924
3058
  player_idx = action.legal_action.player_idx
2925
- if player_idx < 0 or player_idx >= len(game.players):
2926
- return game
2927
- new_legal_actions = [
2928
- la for la in game.legal_actions_3 if la.player_idx != player_idx
2929
- ]
2930
- history = game.history + [action]
2931
- return game.set(
2932
- 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(
2933
3063
  history=history,
2934
3064
  )
2935
3065
 
2936
3066
 
2937
- def recycle_decks_if_needed(game):
2938
- 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)):
2939
3070
  print("recycle_decks_if_needed for deck_idx =", deck_idx)
2940
- game = recycle_if_needed(game, deck_idx)
2941
- return game
3071
+ kernel = recycle_if_needed(kernel, deck_idx)
3072
+ return kernel
2942
3073
 
2943
3074
 
2944
- def replenish_decks_if_needed(game):
2945
- for deck_idx in range(len(game.decks)):
2946
- game = replenish_faceup_if_needed(game, deck_idx)
2947
- 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
2948
3080
 
2949
3081
 
2950
- def replenish_faceup_spot_if_needed(game, deck_idx, spot_idx):
2951
- 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]
2952
3085
 
2953
3086
  if deck.faceup_spots[spot_idx] is not None:
2954
- return game
3087
+ return kernel
2955
3088
 
2956
3089
  if len(deck.facedown_stack) == 0:
2957
- return game
3090
+ return kernel
2958
3091
 
2959
3092
  deck.faceup_spots[spot_idx] = deck.facedown_stack.pop()
2960
3093
 
2961
- game = ensure_faceup_spots_valid(game)
3094
+ kernel = ensure_faceup_spots_valid(kernel)
2962
3095
 
2963
- return game.set(
2964
- decks=game.decks,
3096
+ return kernel.set(
3097
+ decks=kernel.decks,
2965
3098
  )
2966
3099
 
2967
3100
 
2968
- def replenish_faceup_if_needed(game, deck_idx):
2969
- deck = game.decks[deck_idx]
3101
+ @dispatch(StateKernel, int)
3102
+ def replenish_faceup_if_needed(kernel, deck_idx):
3103
+ deck = kernel.decks[deck_idx]
2970
3104
  for spot_idx in range(len(deck.faceup_spots)):
2971
- game = replenish_faceup_spot_if_needed(game, deck_idx, spot_idx)
2972
- return game
3105
+ kernel = replenish_faceup_spot_if_needed(kernel, deck_idx, spot_idx)
3106
+ return kernel
2973
3107
 
2974
3108
 
2975
- def recycle_if_needed(game, deck_idx):
2976
- deck = game.decks[deck_idx]
3109
+ @dispatch(StateKernel, int)
3110
+ def recycle_if_needed(kernel, deck_idx):
3111
+ deck = kernel.decks[deck_idx]
2977
3112
  if len(deck.facedown_stack) == 0:
2978
3113
  shuffled_discards = list(deck.discard)
2979
- game.rng.shuffle(shuffled_discards)
3114
+ kernel.rng.shuffle(shuffled_discards)
2980
3115
  deck = deck.set(
2981
3116
  facedown_stack = shuffled_discards,
2982
3117
  discard = []
2983
3118
  )
2984
- game = set_deck(game, deck.idx, deck)
2985
- return game
3119
+ kernel = set_deck(kernel, deck.idx, deck)
3120
+ return kernel
2986
3121
 
2987
3122
 
2988
- def handle_draw_action(game, action):
2989
- if not game or not action or not action.legal_action or not action.legal_action.draw:
2990
- 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
2991
3127
  legal_action = action.legal_action
2992
3128
  draw = legal_action.draw
2993
- player = game.players[legal_action.player_idx]
2994
- deck = game.decks[draw.deck_idx]
3129
+ player = kernel.players[legal_action.player_idx]
3130
+ deck = kernel.decks[draw.deck_idx]
2995
3131
 
2996
3132
  if len(deck.facedown_stack) == 0:
2997
- return game # No cards to draw
3133
+ return kernel # No cards to draw
2998
3134
 
2999
3135
  for _ in range(draw.quantity):
3000
3136
  drawn_card = deck.facedown_stack.pop()
3001
3137
  player.cards.append(drawn_card)
3002
- game = recycle_if_needed(game, draw.deck_idx)
3138
+ kernel = recycle_if_needed(kernel, draw.deck_idx)
3003
3139
 
3004
- game = game.set(
3005
- players=game.players,
3140
+ kernel = kernel.set(
3141
+ players=kernel.players,
3006
3142
  )
3007
3143
 
3008
3144
  # TODO: extract this out to user-defined function/hook
3009
3145
  # if draw.quantity == 1:
3010
3146
  # game = append_follow_up_draw_legal_actions(game, action)
3011
3147
 
3012
- return game
3148
+ return kernel
3013
3149
 
3014
3150
 
3015
- def handle_draw_discard_action(game, action):
3016
- if not game or not action or not action.legal_action or not action.legal_action.draw_discard:
3017
- 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
3018
3155
  legal_action = action.legal_action
3019
3156
  draw_discard = legal_action.draw_discard
3020
- player = game.players[legal_action.player_idx]
3021
- deck = game.decks[draw_discard.deck_idx]
3157
+ player = kernel.players[legal_action.player_idx]
3158
+ deck = kernel.decks[draw_discard.deck_idx]
3022
3159
 
3023
3160
  if len(deck.facedown_stack) == 0:
3024
- return game # No cards to draw
3161
+ return kernel # No cards to draw
3025
3162
 
3026
3163
  for _ in range(draw_discard.quantity):
3027
3164
  drawn_card = deck.facedown_stack.pop()
3028
3165
  player.discard_tray.append(drawn_card)
3029
3166
 
3030
- return game.set(
3031
- players=game.players,
3032
- decks=game.decks,
3167
+ return kernel.set(
3168
+ players=kernel.players,
3169
+ decks=kernel.decks,
3033
3170
  )
3034
3171
 
3035
3172
 
@@ -3076,26 +3213,29 @@ def get_follow_up_draw_legal_actions(game, action):
3076
3213
  return to_return
3077
3214
 
3078
3215
 
3079
- def get_num_faceup_wilds(game, deck_idx):
3080
- deck = game.decks[deck_idx]
3216
+ @dispatch(StateKernel, int)
3217
+ def get_num_faceup_wilds(kernel, deck_idx):
3218
+ deck = kernel.decks[deck_idx]
3081
3219
  non_empty_spots = [spot for spot in deck.faceup_spots if spot]
3082
3220
  if not non_empty_spots:
3083
3221
  return 0
3084
- 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)
3085
3223
 
3086
3224
 
3087
- def ensure_faceup_spots_valid(game):
3088
- for deck in game.decks:
3225
+ @dispatch(StateKernel)
3226
+ def ensure_faceup_spots_valid(kernel):
3227
+ for deck in kernel.decks:
3089
3228
  max_iters = 5
3090
3229
  i = 0
3091
- while i < max_iters and get_num_faceup_wilds(game, deck.idx) >= 3:
3092
- 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)
3093
3232
  i += 1
3094
- return game
3233
+ return kernel
3095
3234
 
3096
3235
 
3097
- def discardfaceup_shuffle_flip(game, deck_idx):
3098
- deck = game.decks[deck_idx]
3236
+ @dispatch(StateKernel, int)
3237
+ def discardfaceup_shuffle_flip(kernel, deck_idx):
3238
+ deck = kernel.decks[deck_idx]
3099
3239
  num_faceup_spots = len(deck.faceup_spots)
3100
3240
  for faceup_spot in deck.faceup_spots:
3101
3241
  if faceup_spot:
@@ -3104,45 +3244,48 @@ def discardfaceup_shuffle_flip(game, deck_idx):
3104
3244
  while len(deck.discard) > 0:
3105
3245
  card = deck.discard.pop()
3106
3246
  deck.facedown_stack.append(card)
3107
- deck = shuffle(game.rng, deck)
3108
- game = set_deck(game, deck.idx, deck)
3109
- game = flip_cards(game, deck.idx, num_faceup_spots)
3110
- 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
3111
3251
 
3112
3252
 
3113
- def handle_faceup_draw_action(game, action):
3114
- if not game or not action or not action.legal_action or not action.legal_action.faceup_draw:
3115
- 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
3116
3257
  legal_action = action.legal_action
3117
3258
  faceup_draw = legal_action.faceup_draw
3118
- player = game.players[legal_action.player_idx]
3119
- deck = game.decks[faceup_draw.deck_idx]
3259
+ player = kernel.players[legal_action.player_idx]
3260
+ deck = kernel.decks[faceup_draw.deck_idx]
3120
3261
  # find the faceup spot idx with uuid "faceup_draw.card_uuid"
3121
3262
  spot_idx = next((i for i, card_uuid in enumerate(deck.faceup_spots) if card_uuid == faceup_draw.card_uuid), None)
3122
3263
  drawn_card = deck.faceup_spots[spot_idx] if spot_idx is not None else None
3123
3264
  player.cards.append(drawn_card)
3124
3265
 
3125
3266
  deck.faceup_spots[spot_idx] = None
3126
- game = game.set(decks=game.decks)
3127
- 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)
3128
3269
 
3129
3270
  # Prevent an extra draw if last draw was a face-wild-draw
3130
3271
  # if not game.kernel.carduuid2card[drawn_card].is_wild:
3131
3272
  # # TODO: extract this out to user-defined function/hook
3132
3273
  # game = append_follow_up_draw_legal_actions(game, action)
3133
3274
 
3134
- return game.set(
3135
- players=game.players,
3275
+ return kernel.set(
3276
+ players=kernel.players,
3136
3277
  )
3137
3278
 
3138
3279
 
3139
- def handle_after_move_pieces_to_path_action(game, action):
3140
- game = handle_move_bonus_cards(game, action)
3141
- 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
3142
3284
 
3143
3285
 
3144
- def handle_move_bonus_cards(game, action):
3145
- 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)
3146
3289
 
3147
3290
 
3148
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.
@@ -3186,12 +3329,53 @@ def random_player_graph_walk(game, player_idx):
3186
3329
  return walk([random_start_node_idx], set([]))
3187
3330
 
3188
3331
 
3189
- 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):
3190
3374
  longest_path_player_idx = None
3191
3375
  longest_path_length = 0
3192
3376
 
3193
- for player_idx in range(state.kernel.game_config.num_players):
3194
- 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)
3195
3379
  if path_length > longest_path_length:
3196
3380
  longest_path_length = path_length
3197
3381
  longest_path_player_idx = player_idx
@@ -3199,7 +3383,7 @@ def find_player_with_longest_path(state):
3199
3383
  if longest_path_player_idx is None:
3200
3384
  return None
3201
3385
 
3202
- return state.players[longest_path_player_idx]
3386
+ return kernel.players[longest_path_player_idx]
3203
3387
 
3204
3388
 
3205
3389
  @dispatch(State, set)
@@ -3220,15 +3404,34 @@ def calc_path_len_from_edges(state, edge_tuples):
3220
3404
  return sum(edge_lens)
3221
3405
 
3222
3406
 
3223
- 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):
3224
3427
  longest_path_length = 0
3225
3428
  num_iters_since_change = 0
3226
3429
 
3227
3430
  for _ in range(1000):
3228
- walk = random_player_graph_walk(game, player_idx)
3431
+ walk = random_player_graph_walk3(kernel, player_idx)
3229
3432
  if not walk:
3230
3433
  return 0
3231
- path_len = calc_path_len_from_edges(game, walk[1])
3434
+ path_len = calc_path_len_from_edges3(kernel, walk[1])
3232
3435
  if path_len > longest_path_length:
3233
3436
  longest_path_length = path_len
3234
3437
  num_iters_since_change = 0
@@ -3242,25 +3445,49 @@ def get_longest_path_length(game, player_idx):
3242
3445
  return longest_path_length
3243
3446
 
3244
3447
 
3245
- def handle_move_longest_path_card(game, action):
3246
- 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
3247
3469
 
3248
- if not game or not player:
3249
- 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
3250
3477
 
3251
3478
  longest_path_card = None
3252
3479
 
3253
- if len(game.decks) > 2:
3254
- longest_path_deck = game.decks[2]
3480
+ if len(kernel.decks) > 2:
3481
+ longest_path_deck = kernel.decks[2]
3255
3482
  if longest_path_deck.facedown_stack:
3256
3483
  # Move the longest path card to the player's hand
3257
3484
  longest_path_card = longest_path_deck.facedown_stack.pop()
3258
3485
  else:
3259
3486
  # Search for the longest path card in each player's cards
3260
- player_with_card = find_player_with_longest_path_card(game)
3487
+ player_with_card = find_player_with_longest_path_card(kernel)
3261
3488
  if player_with_card:
3262
3489
  # Find the index of the card from the player's cards, then pop it
3263
- 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)
3264
3491
  if card_idx is not None:
3265
3492
  longest_path_card = player_with_card.cards.pop(card_idx)
3266
3493
 
@@ -3268,9 +3495,9 @@ def handle_move_longest_path_card(game, action):
3268
3495
  if longest_path_card:
3269
3496
  player.cards.append(longest_path_card)
3270
3497
 
3271
- return game.set(
3272
- players=game.players,
3273
- decks=game.decks,
3498
+ return kernel.set(
3499
+ players=kernel.players,
3500
+ decks=kernel.decks,
3274
3501
  )
3275
3502
 
3276
3503
 
@@ -3281,9 +3508,10 @@ def find_player_with_longest_path_card(game):
3281
3508
  return None
3282
3509
 
3283
3510
 
3284
- def handle_move_pieces_to_path_action(game, action):
3285
- if not game or not action or not action.legal_action or not action.legal_action.move_pieces_to_path:
3286
- 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
3287
3515
 
3288
3516
  legal_action = action.legal_action
3289
3517
  move_pieces_to_path = legal_action.move_pieces_to_path
@@ -3293,15 +3521,15 @@ def handle_move_pieces_to_path_action(game, action):
3293
3521
  default = default.set(piece_uuids=override.piece_uuids)
3294
3522
  default = default.set(card_uuids=override.card_uuids)
3295
3523
 
3296
- player = game.players[legal_action.player_idx]
3524
+ player = kernel.players[legal_action.player_idx]
3297
3525
  if not player or not player.pieces:
3298
- return game
3526
+ return kernel
3299
3527
 
3300
3528
  path_idx = move_pieces_to_path.path_idx
3301
- path = game.idx2path[path_idx]
3529
+ path = kernel.idx2path[path_idx]
3302
3530
 
3303
3531
  if path is None or not path.segments:
3304
- return game
3532
+ return kernel
3305
3533
 
3306
3534
  for piece_uuid, segment in zip(default.piece_uuids, path.segments):
3307
3535
  # Find the piece in the player's pieces
@@ -3318,15 +3546,14 @@ def handle_move_pieces_to_path_action(game, action):
3318
3546
  # Remove the card from player's cards
3319
3547
  card_uuid = player.cards.pop(card_idx)
3320
3548
  # add to discard
3321
- 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)
3322
3550
 
3323
- game = recycle_decks_if_needed(game)
3324
- game = replenish_decks_if_needed(game)
3325
- game = game.set(players=game.players)
3326
- game = game.set(idx2path=game.idx2path)
3327
- 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)
3328
3555
 
3329
- return game
3556
+ return kernel
3330
3557
 
3331
3558
 
3332
3559
  @dispatch(State, int)
@@ -3351,6 +3578,27 @@ def calc_player_graph(state, player_idx):
3351
3578
  )
3352
3579
 
3353
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
+
3354
3602
  def calc_player_graphs(state):
3355
3603
  game_config = state.kernel.game_config
3356
3604
  return [
@@ -3359,22 +3607,31 @@ def calc_player_graphs(state):
3359
3607
  ]
3360
3608
 
3361
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
+
3362
3618
  # Does the opposite of handle_discard_action, i.e., it keeps the cards in the discard tray (the other cards are discarded)
3363
- def handle_keep_action(game, action):
3364
- if not game or not action or not action.legal_action.keep:
3365
- 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
3366
3623
 
3367
3624
  deck_idx = action.legal_action.keep.deck_idx
3368
- if deck_idx < 0 or deck_idx >= len(game.decks):
3369
- return game
3625
+ if deck_idx < 0 or deck_idx >= len(kernel.decks):
3626
+ return kernel
3370
3627
 
3371
- deck = game.decks[deck_idx]
3628
+ deck = kernel.decks[deck_idx]
3372
3629
  if not deck:
3373
- return game
3630
+ return kernel
3374
3631
 
3375
- player = game.players[action.legal_action.player_idx]
3632
+ player = kernel.players[action.legal_action.player_idx]
3376
3633
  if not player:
3377
- return game
3634
+ return kernel
3378
3635
 
3379
3636
  # Keep the specified cards in the discard tray
3380
3637
  kept_cards = [card_uuid for card_uuid in player.discard_tray if card_uuid in action.keep.card_uuids]
@@ -3388,32 +3645,33 @@ def handle_keep_action(game, action):
3388
3645
  player.cards.extend([c for c in kept_cards if c is not None])
3389
3646
 
3390
3647
  player.discard_tray.clear()
3391
- return game.set(decks=game.decks, players=game.players)
3648
+ return kernel.set(decks=kernel.decks, players=kernel.players)
3392
3649
 
3393
3650
 
3394
- def handle_discard_action(game, action):
3651
+ @dispatch(StateKernel, Action2)
3652
+ def handle_discard_action(kernel, action):
3395
3653
  print("****************************** handle_discard_action 1", action)
3396
- if not game or not action or not action.legal_action.discard:
3397
- return game
3654
+ if not kernel or not action or not action.legal_action.discard:
3655
+ return kernel
3398
3656
 
3399
3657
  print("****************************** handle_discard_action 2", action)
3400
3658
 
3401
3659
  deck_idx = action.legal_action.discard.deck_idx
3402
- if deck_idx < 0 or deck_idx >= len(game.decks):
3403
- return game
3660
+ if deck_idx < 0 or deck_idx >= len(kernel.decks):
3661
+ return kernel
3404
3662
 
3405
3663
  print("****************************** handle_discard_action 3", deck_idx)
3406
3664
 
3407
- deck = game.decks[deck_idx]
3665
+ deck = kernel.decks[deck_idx]
3408
3666
  print("****************************** handle_discard_action 3.5", deck)
3409
3667
  if not deck:
3410
- return game
3668
+ return kernel
3411
3669
 
3412
3670
  print("****************************** handle_discard_action 4")
3413
3671
 
3414
- player = game.players[action.legal_action.player_idx]
3672
+ player = kernel.players[action.legal_action.player_idx]
3415
3673
  if not player:
3416
- return game
3674
+ return kernel
3417
3675
 
3418
3676
  # Keep the specified cards in the discard tray
3419
3677
  non_kept_cards = [card_uuid for card_uuid in player.discard_tray if card_uuid in action.discard.card_uuids]
@@ -3431,22 +3689,24 @@ def handle_discard_action(game, action):
3431
3689
  player.discard_tray.clear()
3432
3690
  print("****************************** handle_discard_action 10 kept_cards: ", kept_cards)
3433
3691
 
3434
- return game.set(decks=game.decks, players=game.players)
3692
+ return kernel.set(decks=kernel.decks, players=kernel.players)
3435
3693
 
3436
3694
 
3437
- def shuffle_all_decks(game):
3438
- if not game or not game.decks:
3439
- return game
3695
+ @dispatch(StateKernel)
3696
+ def shuffle_all_decks(kernel):
3697
+ if not kernel or not kernel.decks:
3698
+ return kernel
3440
3699
 
3441
- for i in range(len(game.decks)):
3442
- game = shuffle_deck(game, i)
3700
+ for i in range(len(kernel.decks)):
3701
+ kernel = shuffle_deck(kernel, i)
3443
3702
 
3444
- return game
3703
+ return kernel
3445
3704
 
3446
3705
 
3447
- def set_deck(game, deck_idx, deck):
3448
- new_decks = game.decks[:deck_idx] + [deck] + game.decks[deck_idx + 1:]
3449
- 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)
3450
3710
 
3451
3711
 
3452
3712
  def shuffle(rng, deck):
@@ -3455,37 +3715,40 @@ def shuffle(rng, deck):
3455
3715
  return deck.set(facedown_stack=shuffled_cards)
3456
3716
 
3457
3717
 
3458
- def shuffle_deck(game, deck_idx):
3459
- if not game or not game.decks or deck_idx < 0 or deck_idx >= len(game.decks):
3460
- 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
3461
3722
 
3462
- deck = game.decks[deck_idx]
3723
+ deck = kernel.decks[deck_idx]
3463
3724
  if not deck or not deck.facedown_stack:
3464
- return game
3725
+ return kernel
3465
3726
 
3466
- shuffled_deck = shuffle(game.rng, deck)
3467
- game = set_deck(game, deck_idx, shuffled_deck)
3468
- return game
3727
+ shuffled_deck = shuffle(kernel.rng, deck)
3728
+ kernel = set_deck(kernel, deck_idx, shuffled_deck)
3729
+ return kernel
3469
3730
 
3470
3731
 
3471
- def shuffle_player_order(game):
3472
- if not game or not game.player_idxs:
3473
- return game
3732
+ @dispatch(StateKernel)
3733
+ def shuffle_player_order(kernel):
3734
+ if not kernel or not kernel.player_idxs:
3735
+ return kernel
3474
3736
 
3475
- rng = game.rng
3476
- shuffled_idxs = list(game.player_idxs)
3737
+ rng = kernel.rng
3738
+ shuffled_idxs = list(kernel.player_idxs)
3477
3739
  rng.shuffle(shuffled_idxs)
3478
3740
 
3479
- return game.set(player_idxs=shuffled_idxs)
3741
+ return kernel.set(player_idxs=shuffled_idxs)
3480
3742
 
3481
3743
 
3482
- def flip_cards(game, deck_idx, num_cards):
3483
- if not game or deck_idx < 0 or deck_idx >= len(game.decks):
3484
- 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
3485
3748
 
3486
- deck = game.decks[deck_idx]
3749
+ deck = kernel.decks[deck_idx]
3487
3750
  if not deck or not deck.facedown_stack:
3488
- return game
3751
+ return kernel
3489
3752
 
3490
3753
  # Flip the top num_cards from the facedown_stack to the faceup_spots
3491
3754
  for _ in range(num_cards):
@@ -3493,52 +3756,55 @@ def flip_cards(game, deck_idx, num_cards):
3493
3756
  card = deck.facedown_stack.pop()
3494
3757
  deck.faceup_spots.append(card)
3495
3758
 
3496
- return game.set(decks=game.decks)
3759
+ return kernel.set(decks=kernel.decks)
3497
3760
 
3498
3761
 
3499
- def distribute_all_piles(game):
3500
- if not game or not game.piles:
3501
- return game
3762
+ @dispatch(StateKernel)
3763
+ def distribute_all_piles(kernel):
3764
+ if not kernel or not kernel.piles:
3765
+ return kernel
3502
3766
 
3503
- for pile in game.piles:
3767
+ for pile in kernel.piles:
3504
3768
  player_idx = pile.player_idx
3505
- if player_idx < 0 or player_idx >= len(game.players):
3769
+ if player_idx < 0 or player_idx >= len(kernel.players):
3506
3770
  continue
3507
3771
 
3508
- player = game.players[player_idx]
3772
+ player = kernel.players[player_idx]
3509
3773
  player.pieces.extend(pile.pieces)
3510
3774
  pile.pieces.clear() # Clear the pile after distribution
3511
3775
 
3512
- return game.set(players=game.players, piles=game.piles)
3776
+ return kernel.set(players=kernel.players, piles=kernel.piles)
3513
3777
 
3514
3778
 
3515
- def deal_cards_to_each_player_discard_tray(game, deck_idx, num_cards):
3516
- if not game or deck_idx < 0 or deck_idx >= len(game.decks):
3517
- 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
3518
3783
 
3519
- deck = game.decks[deck_idx]
3784
+ deck = kernel.decks[deck_idx]
3520
3785
  if not deck or not deck.facedown_stack:
3521
- return game
3786
+ return kernel
3522
3787
 
3523
- for player in game.players:
3788
+ for player in kernel.players:
3524
3789
  # Deal cards to the player's discard tray
3525
3790
  for _ in range(num_cards):
3526
3791
  if deck.facedown_stack:
3527
3792
  card = deck.facedown_stack.pop()
3528
3793
  player.discard_tray.append(card)
3529
3794
 
3530
- return game.set(players=game.players, decks=game.decks)
3795
+ return kernel.set(players=kernel.players, decks=kernel.decks)
3531
3796
 
3532
3797
 
3533
- def deal_cards_to_each_player(game, deck_idx, num_cards):
3534
- if not game or deck_idx < 0 or deck_idx >= len(game.decks):
3535
- 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
3536
3802
 
3537
- deck = game.decks[deck_idx]
3803
+ deck = kernel.decks[deck_idx]
3538
3804
  if not deck or not deck.facedown_stack:
3539
- return game
3805
+ return kernel
3540
3806
 
3541
- for player in game.players:
3807
+ for player in kernel.players:
3542
3808
  player_hand = player.cards
3543
3809
  # Deal cards to the player's hand
3544
3810
  for _ in range(num_cards):
@@ -3546,7 +3812,7 @@ def deal_cards_to_each_player(game, deck_idx, num_cards):
3546
3812
  card = deck.facedown_stack.pop()
3547
3813
  player_hand.append(card)
3548
3814
 
3549
- return game.set(players=game.players, decks=game.decks)
3815
+ return kernel.set(players=kernel.players, decks=kernel.decks)
3550
3816
 
3551
3817
 
3552
3818
  def default_accept_action(game, action):
@@ -3977,6 +4243,37 @@ def run_state_hook(state, hook, log=False):
3977
4243
  raise Exception(msg) from e
3978
4244
 
3979
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
+
3980
4277
  # Implementing the following Julia function:
3981
4278
  # function getfaceupspots(f, unit_deck, unit_deck_idx)
3982
4279
  # num_faceup_spots = getsettingvalue(f, :num_faceup_spots)
@@ -4103,28 +4400,20 @@ def getnextstate2(s, a, log=False):
4103
4400
  is_legal, reason = isactionlegal2(s, a)
4104
4401
  if not is_legal:
4105
4402
  raise ValueError(f"Action is not legal: {a}. Reason: {reason}")
4106
- s = run_state_action_hooks(s, a, AFTER_ACCEPT_ACTION_HOOKS, log)
4107
- s = run_state_action_hooks(s, a, HANDLE_ACTION_HOOKS, log)
4108
- s = run_state_hooks(s, HANDLE_SCORING_HOOKS, log)
4109
- s = run_state_hooks(s, HANDLE_TERMINAL_HOOKS, log)
4110
-
4111
- state_kernel = init_state_kernel(
4112
- rng=s.rng,
4113
- game_config=s.kernel.game_config,
4114
- edges=s.edges,
4115
- decks=s.decks,
4116
- piles=s.piles,
4117
- players=s.players,
4118
- player_idxs=s.player_idxs,
4119
- history=s.history,
4120
- player_scores=s.player_scores,
4121
- starting_decks=s.kernel.starting_decks,
4122
- starting_piles=s.kernel.starting_piles,
4123
- 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),
4124
4412
  )
4125
- 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))
4126
4415
 
4127
- return s
4416
+ return state
4128
4417
 
4129
4418
 
4130
4419
  def getpublicplayerscore(s, player_score):
@@ -4270,6 +4559,7 @@ def imagine_state(public_state, private_state):
4270
4559
  player_idxs=public_state.player_idxs,
4271
4560
  piles=public_state.piles,
4272
4561
  edges=public_state.edges,
4562
+ nodes=public_state.nodes,
4273
4563
  decks=imagine_decks(public_state, private_state),
4274
4564
  players=imagine_players(public_state, private_state),
4275
4565
  history=imagine_history(public_state, private_state),
@@ -4290,22 +4580,22 @@ def getpublicstate(s):
4290
4580
  allotted_times=get_max_allotted_times(s),
4291
4581
  all_pieces=list(s.kernel.pieceuuid2piece.values()),
4292
4582
  to_play_2=getpublictoplay(s),
4293
- bonus_statuses=s.bonus_statuses,
4583
+ bonus_statuses=s.bonus_statuses_3,
4294
4584
  starting_decks=s.kernel.starting_decks,
4295
4585
  starting_piles=s.kernel.starting_piles,
4296
4586
  history=get_public_history(s),
4297
4587
  player_scores=get_public_player_scores(s),
4298
- player_graphs=s.player_graphs,
4588
+ player_graphs=s.kernel.player_graphs_3,
4299
4589
  goals=s.kernel.goals,
4300
- nodes=s.nodes,
4301
- edges=s.edges,
4302
- regions=s.regions,
4590
+ nodes=s.kernel.nodes,
4591
+ edges=s.kernel.edges,
4592
+ regions=s.kernel.regions,
4303
4593
  decks=[getpublicdeck(s, deck) for deck in s.decks],
4304
- piles=s.piles,
4305
- player_idxs=s.player_idxs,
4594
+ piles=s.kernel.piles,
4595
+ player_idxs=s.kernel.player_idxs,
4306
4596
  players=[getpublicplayer(s, p) for p in s.players],
4307
- last_to_play=s.last_to_play,
4308
- winners=s.winners,
4597
+ last_to_play=s.kernel.last_to_play,
4598
+ winners=s.kernel.winners,
4309
4599
  terminal=isterminal(s),
4310
4600
  )
4311
4601
 
@@ -4473,6 +4763,7 @@ def get_legal_actions(s, player_idx):
4473
4763
  return [a for a in s.legal_actions_3 if a.player_idx == player_idx]
4474
4764
 
4475
4765
 
4766
+ @dispatch(State, int)
4476
4767
  def get_goal_completions(s, player_idx):
4477
4768
  # print("len(get_goals(s, player_idx)): ", len(get_goals(s, player_idx)))
4478
4769
  # print("len(s.history): ", len(s.history))
@@ -4486,10 +4777,24 @@ def get_goal_completions(s, player_idx):
4486
4777
  ]
4487
4778
 
4488
4779
 
4489
- @dispatch(State, FrozenGoal, int)
4490
- def is_goal_complete(s, goal, player_idx):
4491
- graph = s.player_graphs[player_idx].neighbors
4492
- 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]
4493
4798
  return are_nodes_connected(
4494
4799
  graph,
4495
4800
  node_idxs,
@@ -4525,11 +4830,21 @@ def get_player_graph(s, player_idx):
4525
4830
  return nodes
4526
4831
 
4527
4832
 
4528
- @dispatch(State, int)
4529
- def get_goals(s, player_idx):
4530
- player = s.players[player_idx]
4531
- goaluuid2goal = {goal.uuid: goal for goal in s.kernel.goals}
4532
- 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]
4533
4848
  return [
4534
4849
  goaluuid2goal[goal_uuid] for goal_uuid in goal_uuids if goal_uuid in goaluuid2goal
4535
4850
  ]
@@ -5188,23 +5503,23 @@ def getqproxy0(ps, a, td):
5188
5503
  return qproxyrecurse() if td > 0 else qproxybase()
5189
5504
 
5190
5505
 
5191
- INIT_HOOK_1 = """def handler(game):
5192
- return shuffle_all_decks(game)
5506
+ INIT_HOOK_1 = """def handler(kernel):
5507
+ return shuffle_all_decks(kernel)
5193
5508
  """
5194
- INIT_HOOK_2 = """def handler(game):
5195
- 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)
5196
5511
  """
5197
- INIT_HOOK_3 = """def handler(game):
5198
- 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)
5199
5514
  """
5200
- INIT_HOOK_4 = """def handler(game):
5201
- return distribute_all_piles(game)
5515
+ INIT_HOOK_4 = """def handler(kernel):
5516
+ return distribute_all_piles(kernel)
5202
5517
  """
5203
- INIT_HOOK_5 = """def handler(game):
5204
- return flip_cards(game, 0, 5)
5518
+ INIT_HOOK_5 = """def handler(kernel):
5519
+ return flip_cards(kernel, 0, 5)
5205
5520
  """
5206
- INIT_HOOK_7 = """def handler(game):
5207
- return shuffle_player_order(game)
5521
+ INIT_HOOK_7 = """def handler(kernel):
5522
+ return shuffle_player_order(kernel)
5208
5523
  """
5209
5524
 
5210
5525
  INITIALIZATION_HOOKS = [
@@ -5246,9 +5561,10 @@ INITIALIZATION_HOOKS = [
5246
5561
  ),
5247
5562
  ]
5248
5563
 
5564
+
5249
5565
  HANDLE_ACTION_HOOK_1 = """
5250
- def handler(game, action):
5251
- return default_handle_action(game, action)
5566
+ def handler(kernel, action):
5567
+ return default_handle_action(kernel, action)
5252
5568
  """
5253
5569
 
5254
5570
  HANDLE_ACTION_HOOKS = [
@@ -5261,8 +5577,8 @@ HANDLE_ACTION_HOOKS = [
5261
5577
  ]
5262
5578
 
5263
5579
  AFTER_ACCEPT_ACTION_HOOK_1 = """
5264
- def handler(game, action):
5265
- return default_after_accept_action(game, action)
5580
+ def handler(kernel, action):
5581
+ return default_after_accept_action(kernel, action)
5266
5582
  """
5267
5583
 
5268
5584
  AFTER_ACCEPT_ACTION_HOOKS = [