algorhino-anemone 0.1.1__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.
Files changed (82) hide show
  1. algorhino_anemone-0.1.1.dist-info/METADATA +151 -0
  2. algorhino_anemone-0.1.1.dist-info/RECORD +82 -0
  3. algorhino_anemone-0.1.1.dist-info/WHEEL +5 -0
  4. algorhino_anemone-0.1.1.dist-info/licenses/LICENSE +674 -0
  5. algorhino_anemone-0.1.1.dist-info/top_level.txt +1 -0
  6. anemone/__init__.py +27 -0
  7. anemone/basics.py +36 -0
  8. anemone/factory.py +161 -0
  9. anemone/indices/__init__.py +0 -0
  10. anemone/indices/index_manager/__init__.py +12 -0
  11. anemone/indices/index_manager/factory.py +50 -0
  12. anemone/indices/index_manager/node_exploration_manager.py +549 -0
  13. anemone/indices/node_indices/__init__.py +22 -0
  14. anemone/indices/node_indices/factory.py +121 -0
  15. anemone/indices/node_indices/index_data.py +166 -0
  16. anemone/indices/node_indices/index_types.py +20 -0
  17. anemone/nn/torch_evaluator.py +108 -0
  18. anemone/node_evaluation/__init__.py +0 -0
  19. anemone/node_evaluation/node_direct_evaluation/__init__.py +22 -0
  20. anemone/node_evaluation/node_direct_evaluation/factory.py +12 -0
  21. anemone/node_evaluation/node_direct_evaluation/node_direct_evaluator.py +192 -0
  22. anemone/node_evaluation/node_tree_evaluation/node_minmax_evaluation.py +885 -0
  23. anemone/node_evaluation/node_tree_evaluation/node_tree_evaluation.py +137 -0
  24. anemone/node_evaluation/node_tree_evaluation/node_tree_evaluation_factory.py +43 -0
  25. anemone/node_factory/__init__.py +14 -0
  26. anemone/node_factory/algorithm_node_factory.py +123 -0
  27. anemone/node_factory/base.py +76 -0
  28. anemone/node_selector/__init__.py +32 -0
  29. anemone/node_selector/branch_explorer.py +89 -0
  30. anemone/node_selector/factory.py +65 -0
  31. anemone/node_selector/node_selector.py +44 -0
  32. anemone/node_selector/node_selector_args.py +22 -0
  33. anemone/node_selector/node_selector_types.py +15 -0
  34. anemone/node_selector/notations_and_statics.py +88 -0
  35. anemone/node_selector/opening_instructions.py +249 -0
  36. anemone/node_selector/recurzipf/__init__.py +0 -0
  37. anemone/node_selector/recurzipf/recur_zipf_base.py +141 -0
  38. anemone/node_selector/sequool/__init__.py +19 -0
  39. anemone/node_selector/sequool/factory.py +102 -0
  40. anemone/node_selector/sequool/sequool.py +395 -0
  41. anemone/node_selector/uniform/__init__.py +16 -0
  42. anemone/node_selector/uniform/uniform.py +113 -0
  43. anemone/nodes/__init__.py +15 -0
  44. anemone/nodes/algorithm_node/__init__.py +7 -0
  45. anemone/nodes/algorithm_node/algorithm_node.py +204 -0
  46. anemone/nodes/itree_node.py +136 -0
  47. anemone/nodes/tree_node.py +240 -0
  48. anemone/nodes/tree_traversal.py +108 -0
  49. anemone/nodes/utils.py +146 -0
  50. anemone/progress_monitor/__init__.py +0 -0
  51. anemone/progress_monitor/progress_monitor.py +375 -0
  52. anemone/recommender_rule/__init__.py +12 -0
  53. anemone/recommender_rule/recommender_rule.py +140 -0
  54. anemone/search_factory/__init__.py +14 -0
  55. anemone/search_factory/search_factory.py +192 -0
  56. anemone/state_transition.py +47 -0
  57. anemone/tree_and_value_branch_selector.py +99 -0
  58. anemone/tree_exploration.py +274 -0
  59. anemone/tree_manager/__init__.py +29 -0
  60. anemone/tree_manager/algorithm_node_tree_manager.py +246 -0
  61. anemone/tree_manager/factory.py +77 -0
  62. anemone/tree_manager/tree_expander.py +122 -0
  63. anemone/tree_manager/tree_manager.py +254 -0
  64. anemone/trees/__init__.py +14 -0
  65. anemone/trees/descendants.py +765 -0
  66. anemone/trees/factory.py +80 -0
  67. anemone/trees/tree.py +70 -0
  68. anemone/trees/tree_visualization.py +143 -0
  69. anemone/updates/__init__.py +33 -0
  70. anemone/updates/algorithm_node_updater.py +157 -0
  71. anemone/updates/factory.py +36 -0
  72. anemone/updates/index_block.py +91 -0
  73. anemone/updates/index_updater.py +100 -0
  74. anemone/updates/minmax_evaluation_updater.py +108 -0
  75. anemone/updates/updates_file.py +248 -0
  76. anemone/updates/value_block.py +133 -0
  77. anemone/utils/comparable.py +32 -0
  78. anemone/utils/dataclass.py +64 -0
  79. anemone/utils/dict_of_numbered_dict_with_pointer_on_max.py +128 -0
  80. anemone/utils/logger.py +94 -0
  81. anemone/utils/my_value_sorted_dict.py +27 -0
  82. anemone/utils/small_tools.py +103 -0
@@ -0,0 +1,192 @@
1
+ """
2
+ This module contains the implementation of the SearchFactory class, which is an abstract factory that creates
3
+ dependent factories for selecting nodes to open, creating indices, and updating indices. These factories need to
4
+ operate on the same data, so they are created in a coherent way.
5
+
6
+ The SearchFactory class provides methods to create the node selector factory, the index updater, and to create
7
+ node indices for a given tree node.
8
+
9
+ Classes:
10
+ - SearchFactory: An abstract factory for creating dependent factories for selecting nodes to open, creating indices,
11
+ and updating indices.
12
+
13
+ Protocols:
14
+ - SearchFactoryP: A protocol that defines the abstract methods for the SearchFactory class.
15
+
16
+ Functions:
17
+ - create_node_selector_factory: A function that creates the node selector factory.
18
+ - create_node_index_updater: A function that creates the index updater.
19
+ - node_index_create: A function that creates node indices for a given tree node.
20
+ """
21
+
22
+ from dataclasses import dataclass
23
+ from functools import partial
24
+ from random import Random
25
+ from typing import Callable, Protocol
26
+
27
+ from valanga import State
28
+
29
+ from anemone import node_selector as node_selectors
30
+ from anemone import nodes
31
+ from anemone.indices import node_indices
32
+ from anemone.indices.node_indices.factory import (
33
+ create_exploration_index_data,
34
+ )
35
+ from anemone.node_selector.opening_instructions import (
36
+ OpeningInstructor,
37
+ OpeningType,
38
+ )
39
+ from anemone.node_selector.sequool.factory import (
40
+ SequoolArgs,
41
+ )
42
+ from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
43
+ from anemone.updates.index_updater import IndexUpdater
44
+
45
+ NodeSelectorFactory = Callable[[], node_selectors.NodeSelector]
46
+
47
+
48
+ class SearchFactoryP(Protocol):
49
+ """
50
+ The abstract Factory that creates the following dependent factories in charge of selecting nodes to open
51
+ - the node selector
52
+ - the index creator
53
+ - the index updater
54
+ These three classes needs to operate on the same data, so they need to be created in a coherent way
55
+ """
56
+
57
+ def create_node_selector_factory(self) -> NodeSelectorFactory:
58
+ """
59
+ Creates a NodeSelectorFactory object.
60
+
61
+ Returns:
62
+ NodeSelectorFactory: The created NodeSelectorFactory object.
63
+ """
64
+ ...
65
+
66
+ def create_node_index_updater(self) -> IndexUpdater | None:
67
+ """
68
+ Creates and returns an instance of the IndexUpdater class, which is responsible for updating the node index.
69
+
70
+ Returns:
71
+ IndexUpdater | None: An instance of the IndexUpdater class if successful, None otherwise.
72
+ """
73
+ ...
74
+
75
+ def node_index_create[StateT: State](
76
+ self,
77
+ tree_node: nodes.TreeNode[AlgorithmNode[StateT], StateT],
78
+ ) -> node_indices.NodeExplorationData[AlgorithmNode[StateT], StateT] | None:
79
+ """
80
+ Creates a node index for the given tree node.
81
+
82
+ Args:
83
+ tree_node (nodes.TreeNode): The tree node for which to create the index.
84
+
85
+ Returns:
86
+ node_indices.NodeExplorationData | None: The created node index, or None if the index could not be created.
87
+ """
88
+ ...
89
+
90
+
91
+ @dataclass
92
+ class SearchFactory:
93
+ """
94
+ The abstract Factory that creates the following dependent factories in charge of selecting nodes to open
95
+ - the node selector
96
+ - the index creator
97
+ - the index updater
98
+ These three classes needs to operate on the same data, so they need to be created in a coherent way
99
+ """
100
+
101
+ node_selector_args: node_selectors.AllNodeSelectorArgs | None
102
+ opening_type: OpeningType | None
103
+ random_generator: Random | None
104
+ index_computation: node_indices.IndexComputationType | None
105
+ depth_index: bool = False
106
+
107
+ def __post_init__(self) -> None:
108
+ """
109
+ Initializes the object after it has been created.
110
+
111
+ This method is automatically called after the object has been initialized.
112
+ It sets the value of `depth_index` based on the type of `node_selector_args`.
113
+
114
+ If `node_selector_args` is an instance of `SequoolArgs`, then `depth_index`
115
+ is set to the value of `recursive_selection_on_all_nodes` attribute of `node_selector_args`.
116
+ Otherwise, `depth_index` is set to False.
117
+ """
118
+ if isinstance(self.node_selector_args, SequoolArgs):
119
+ a: SequoolArgs = self.node_selector_args
120
+ self.depth_index: bool = a.recursive_selection_on_all_nodes
121
+ else:
122
+ self.depth_index: bool = False
123
+
124
+ def create_node_selector_factory(self) -> NodeSelectorFactory:
125
+ """
126
+ Creates the node selector factory .
127
+
128
+ Returns:
129
+ A callable object that creates the node selector.
130
+
131
+ Raises:
132
+ AssertionError: If the random generator is not provided.
133
+ """
134
+ # creates the opening instructor
135
+
136
+ assert self.random_generator is not None
137
+ opening_instructor: OpeningInstructor | None = (
138
+ OpeningInstructor(
139
+ opening_type=self.opening_type, random_generator=self.random_generator
140
+ )
141
+ if self.opening_type is not None
142
+ else None
143
+ )
144
+
145
+ assert self.node_selector_args is not None
146
+ assert opening_instructor is not None
147
+
148
+ node_selector_create: NodeSelectorFactory = partial(
149
+ node_selectors.create,
150
+ args=self.node_selector_args,
151
+ opening_instructor=opening_instructor,
152
+ random_generator=self.random_generator,
153
+ )
154
+ return node_selector_create
155
+
156
+ def create_node_index_updater(self) -> IndexUpdater | None:
157
+ """
158
+ Creates the index updater.
159
+
160
+ Returns:
161
+ An instance of the IndexUpdater class if depth indexing is enabled, otherwise None.
162
+ """
163
+ index_updater: IndexUpdater | None
164
+ if self.depth_index:
165
+ index_updater = IndexUpdater()
166
+ else:
167
+ index_updater = None
168
+ return index_updater
169
+
170
+ def node_index_create[StateT: State](
171
+ self,
172
+ tree_node: nodes.TreeNode[AlgorithmNode[StateT], StateT],
173
+ ) -> node_indices.NodeExplorationData[AlgorithmNode[StateT], StateT] | None:
174
+ """
175
+ Creates node indices for a given tree node.
176
+
177
+ Args:
178
+ tree_node: The tree node for which to create the node indices.
179
+
180
+ Returns:
181
+ An instance of the NodeExplorationData class if depth indexing is enabled, otherwise None.
182
+ """
183
+
184
+ exploration_index_data: (
185
+ node_indices.NodeExplorationData[AlgorithmNode[StateT], StateT] | None
186
+ ) = create_exploration_index_data(
187
+ tree_node=tree_node,
188
+ index_computation=self.index_computation,
189
+ depth_index=self.depth_index,
190
+ )
191
+
192
+ return exploration_index_data
@@ -0,0 +1,47 @@
1
+ """Defines state transition protocols and implementations for Anemone."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Protocol
5
+
6
+ from valanga import BranchKey, State, StateModifications
7
+
8
+
9
+ class StateTransition[StateT](Protocol):
10
+ """Protocol for state transitions in Anemone."""
11
+
12
+ def copy_for_expansion(self, state: StateT, *, copy_stack: bool) -> StateT:
13
+ """Return a copy of state suitable for expansion."""
14
+ ...
15
+
16
+ def step(
17
+ self,
18
+ state: StateT,
19
+ *,
20
+ branch_key: BranchKey,
21
+ ) -> tuple[StateT, StateModifications | None]:
22
+ """Apply a branch key to advance the state."""
23
+ ...
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class ValangaStateTransition(StateTransition[State]):
28
+ """State transition implementation using Valanga's State."""
29
+
30
+ deep_copy_legal_moves: bool = False
31
+
32
+ def copy_for_expansion(self, state: State, *, copy_stack: bool) -> State:
33
+ """Copy the state, optionally copying its stack."""
34
+ return state.copy(
35
+ stack=copy_stack,
36
+ deep_copy_legal_moves=self.deep_copy_legal_moves,
37
+ )
38
+
39
+ def step(
40
+ self,
41
+ state: State,
42
+ *,
43
+ branch_key: BranchKey,
44
+ ) -> tuple[State, StateModifications | None]:
45
+ """Advance state by a branch key and return modifications."""
46
+ modifications = state.step(branch_key=branch_key)
47
+ return state, modifications
@@ -0,0 +1,99 @@
1
+ """
2
+ This module contains the implementation of the TreeAndValueMoveSelector class, which is responsible for selecting moves
3
+ based on a tree and value strategy.
4
+
5
+ The TreeAndValueMoveSelector class uses a tree-based approach to explore possible moves and select the best move based on
6
+ a value function. It utilizes a tree manager, a tree factory, stopping criterion arguments, a node selector factory, a
7
+ random generator, and recommendation functions to guide the move selection process.
8
+
9
+ The TreeAndValueMoveSelector class provides the following methods:
10
+ - select_move: Selects the best move based on the tree and value strategy.
11
+ - print_info: Prints information about the move selector type.
12
+ """
13
+
14
+ from dataclasses import dataclass
15
+ from queue import Queue
16
+ from random import Random
17
+
18
+ from valanga import TurnState
19
+
20
+ from anemone.basics import BranchRecommendation, Seed
21
+ from anemone.progress_monitor.progress_monitor import (
22
+ AllStoppingCriterionArgs,
23
+ )
24
+ from anemone.search_factory import NodeSelectorFactory
25
+ from anemone.utils.dataclass import IsDataclass
26
+
27
+ from . import recommender_rule
28
+ from . import tree_manager as tree_man
29
+ from .tree_exploration import TreeExploration, create_tree_exploration
30
+ from .trees.factory import ValueTreeFactory
31
+
32
+
33
+ @dataclass
34
+ class TreeAndValueBranchSelector[StateT: TurnState = TurnState]:
35
+ """
36
+ The TreeAndValueBranchSelector class is responsible for selecting moves based on a tree and value strategy.
37
+
38
+ Attributes:
39
+ - tree_manager: The tree manager responsible for managing the algorithm nodes.
40
+ - tree_factory: The tree factory responsible for creating move and value trees.
41
+ - stopping_criterion_args: The stopping criterion arguments used to determine when to stop the tree exploration.
42
+ - node_selector_create: The node selector factory used to create node selectors for tree exploration.
43
+ - random_generator: The random generator used for randomization during tree exploration.
44
+ - recommend_move_after_exploration: The recommendation functions used to recommend a move after tree exploration.
45
+ """
46
+
47
+ # pretty empty class but might be useful when dealing with multi round and time , no?
48
+
49
+ tree_manager: tree_man.AlgorithmNodeTreeManager
50
+ tree_factory: ValueTreeFactory[StateT]
51
+ stopping_criterion_args: AllStoppingCriterionArgs
52
+ node_selector_create: NodeSelectorFactory
53
+ random_generator: Random
54
+ recommend_move_after_exploration: recommender_rule.AllRecommendFunctionsArgs
55
+ queue_progress_player: Queue[IsDataclass] | None
56
+
57
+ def select_branch(
58
+ self, state: StateT, selection_seed: Seed
59
+ ) -> BranchRecommendation:
60
+ """
61
+ Selects the best move based on the tree and value strategy.
62
+
63
+ Args:
64
+ - board: The current board state.
65
+ - move_seed: The seed used for randomization during move selection.
66
+
67
+ Returns:
68
+ - The recommended move based on the tree and value strategy.
69
+ """
70
+ tree_exploration: TreeExploration = self.create_tree_exploration(state=state)
71
+ self.random_generator.seed(selection_seed)
72
+
73
+ branch_recommendation: BranchRecommendation = tree_exploration.explore(
74
+ random_generator=self.random_generator
75
+ ).branch_recommendation
76
+
77
+ return branch_recommendation
78
+
79
+ def create_tree_exploration(
80
+ self,
81
+ state: StateT,
82
+ ) -> TreeExploration:
83
+ """Create a TreeExploration instance for the given state."""
84
+ tree_exploration: TreeExploration = create_tree_exploration(
85
+ tree_manager=self.tree_manager,
86
+ node_selector_create=self.node_selector_create,
87
+ starting_state=state,
88
+ tree_factory=self.tree_factory,
89
+ stopping_criterion_args=self.stopping_criterion_args,
90
+ recommend_move_after_exploration=self.recommend_move_after_exploration,
91
+ queue_progress_player=self.queue_progress_player,
92
+ )
93
+ return tree_exploration
94
+
95
+ def print_info(self) -> None:
96
+ """
97
+ Prints information about the move selector type.
98
+ """
99
+ print("type: Tree and Value")
@@ -0,0 +1,274 @@
1
+ """
2
+ This module contains the implementation of the TreeExploration class, which is responsible for managing a search
3
+ for the best move in a given chess position using a tree-based approach.
4
+
5
+ The TreeExploration class is used to create and manage a tree structure that represents the possible moves and
6
+ their evaluations in a chess position. It provides methods for exploring the tree, selecting the best move,
7
+ and printing information during the move computation.
8
+
9
+ The module also includes helper functions for creating a TreeExploration object and its dependencies.
10
+
11
+ Classes:
12
+ - TreeExploration: Manages the search for the best move using a tree-based approach.
13
+
14
+ Functions:
15
+ - create_tree_exploration: Creates a TreeExploration object with the specified dependencies.
16
+ """
17
+
18
+ from dataclasses import dataclass
19
+ from queue import Queue
20
+ from random import Random
21
+ from typing import TYPE_CHECKING, Any, Callable
22
+
23
+ from valanga import BoardEvaluation, BranchKey, PlayerProgressMessage, State, TurnState
24
+
25
+ from anemone.basics import BranchRecommendation
26
+ from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
27
+ from anemone.progress_monitor.progress_monitor import (
28
+ AllStoppingCriterionArgs,
29
+ ProgressMonitor,
30
+ create_stopping_criterion,
31
+ )
32
+ from anemone.search_factory import NodeSelectorFactory
33
+ from anemone.utils.dataclass import IsDataclass
34
+ from anemone.utils.logger import anemone_logger
35
+
36
+ from . import node_selector as node_sel
37
+ from . import recommender_rule, trees
38
+ from . import tree_manager as tree_man
39
+ from .trees.factory import ValueTreeFactory
40
+
41
+ if TYPE_CHECKING:
42
+ from anemone.recommender_rule.recommender_rule import BranchPolicy
43
+
44
+
45
+ @dataclass
46
+ class TreeExplorationResult[NodeT: AlgorithmNode[Any] = AlgorithmNode[Any]]:
47
+ """
48
+ Tree Exploration Result holds the result of a tree exploration.
49
+ """
50
+
51
+ branch_recommendation: BranchRecommendation
52
+ tree: trees.Tree[NodeT]
53
+
54
+
55
+ def compute_child_evals[StateT: State](
56
+ root: AlgorithmNode[StateT],
57
+ ) -> dict[BranchKey, BoardEvaluation]:
58
+ """Compute evaluations for each existing child branch."""
59
+ evals: dict[BranchKey, BoardEvaluation] = {}
60
+ for bk, child in root.branches_children.items():
61
+ if child is None:
62
+ continue
63
+
64
+ # Use whatever your canonical per-node evaluation is:
65
+ evals[bk] = child.tree_evaluation.evaluate()
66
+ return evals
67
+
68
+
69
+ @dataclass
70
+ class TreeExploration[NodeT: AlgorithmNode[Any] = AlgorithmNode[Any]]:
71
+ """
72
+ Tree Exploration is an object to manage one best move search.
73
+
74
+ Attributes:
75
+ - tree: The tree structure representing the possible moves and their evaluations.
76
+ - tree_manager: The manager for the tree structure.
77
+ - node_selector: The selector for choosing nodes and moves to open in the tree.
78
+ - recommend_move_after_exploration: The recommender rule for selecting the best move after the tree exploration.
79
+ - stopping_criterion: The stopping criterion for determining when to stop the tree exploration.
80
+
81
+ Methods:
82
+ - print_info_during_move_computation: Prints information during the move computation.
83
+ - explore: Explores the tree to find the best move.
84
+ """
85
+
86
+ # TODO Not sure why this class is not simply the TreeAndValuePlayer Class
87
+ # but might be useful when dealing with multi round and time , no?
88
+
89
+ tree: trees.Tree[NodeT]
90
+ tree_manager: tree_man.AlgorithmNodeTreeManager[NodeT]
91
+ node_selector: node_sel.NodeSelector[NodeT]
92
+ recommend_move_after_exploration: recommender_rule.AllRecommendFunctionsArgs
93
+ stopping_criterion: ProgressMonitor[NodeT]
94
+ notify_percent_function: Callable[[int], None] | None
95
+
96
+ def print_info_during_move_computation(self, random_generator: Random) -> None:
97
+ """
98
+ Prints information during the move computation.
99
+
100
+ Args:
101
+ - random_generator: The random number generator.
102
+
103
+ Returns:
104
+ - None
105
+ """
106
+ current_best_move: str
107
+ if self.tree.root_node.tree_evaluation.best_branch_sequence:
108
+ current_best_move = str(
109
+ self.tree.root_node.tree_evaluation.best_branch_sequence[0]
110
+ )
111
+ else:
112
+ current_best_move = "?"
113
+ if random_generator.random() < 0.11:
114
+ anemone_logger.info(
115
+ "state: %s",
116
+ self.tree.root_node.state,
117
+ )
118
+
119
+ str_progress = self.stopping_criterion.get_string_of_progress(self.tree)
120
+ anemone_logger.info(
121
+ "%s | current best move: %s | current white value: %s",
122
+ str_progress,
123
+ current_best_move,
124
+ self.tree.root_node.tree_evaluation.value_white_minmax,
125
+ )
126
+
127
+ # ,end='\r')
128
+ self.tree.root_node.tree_evaluation.print_branches_sorted_by_value_and_exploration()
129
+ self.tree_manager.print_best_line(tree=self.tree)
130
+
131
+ def explore(self, random_generator: Random) -> TreeExplorationResult[NodeT]:
132
+ """
133
+ Explores the tree to find the best move.
134
+
135
+ Args:
136
+ - random_generator: The random number generator.
137
+
138
+ Returns:
139
+ - MoveRecommendation: The recommended move and its evaluation.
140
+ """
141
+ # by default the first tree expansion is the creation of the tree node
142
+ tree_expansions: tree_man.TreeExpansions[NodeT] = tree_man.TreeExpansions()
143
+
144
+ tree_expansion: tree_man.TreeExpansion[NodeT] = tree_man.TreeExpansion(
145
+ child_node=self.tree.root_node,
146
+ parent_node=None,
147
+ state_modifications=None,
148
+ creation_child_node=True,
149
+ branch_key=None,
150
+ )
151
+ tree_expansions.add_creation(tree_expansion=tree_expansion)
152
+
153
+ loop: int = 0
154
+ while self.stopping_criterion.should_we_continue(tree=self.tree):
155
+ loop = loop + 1
156
+ assert not self.tree.root_node.is_over()
157
+ # print info
158
+ self.print_info_during_move_computation(random_generator=random_generator)
159
+
160
+ # choose the moves and nodes to open
161
+ opening_instructions: node_sel.OpeningInstructions[NodeT]
162
+ opening_instructions = self.node_selector.choose_node_and_branch_to_open(
163
+ tree=self.tree, latest_tree_expansions=tree_expansions
164
+ )
165
+
166
+ # make sure we do not break the stopping criterion
167
+ opening_instructions_subset: node_sel.OpeningInstructions[NodeT]
168
+ opening_instructions_subset = (
169
+ self.stopping_criterion.respectful_opening_instructions(
170
+ opening_instructions=opening_instructions, tree=self.tree
171
+ )
172
+ )
173
+
174
+ # open the nodes
175
+ tree_expansions = self.tree_manager.open_instructions(
176
+ tree=self.tree, opening_instructions=opening_instructions_subset
177
+ )
178
+
179
+ # self.node_selector.communicate_expansions()
180
+ self.tree_manager.update_backward(tree_expansions=tree_expansions)
181
+ self.tree_manager.update_indices(tree=self.tree)
182
+
183
+ if loop % 10 == 0:
184
+ self.stopping_criterion.notify_percent_progress(
185
+ tree=self.tree, notify_percent_function=self.notify_percent_function
186
+ )
187
+
188
+ # trees.save_raw_data_to_file(tree=self.tree)
189
+ # self.tree_manager.print_some_stats(tree=self.tree)
190
+ # for move, child in self.tree.root_node.moves_children.items():
191
+ # chipiron_logger.info(f'{move} {self.tree.root_node.moves_children[move].minmax_evaluation.get_value_white()}'
192
+ # f' {child.minmax_evaluation.over_event.get_over_tag()}')
193
+ # chipiron_logger.info(f'evaluation for white: {self.tree.root_node.minmax_evaluation.get_value_white()}')
194
+
195
+ policy: BranchPolicy = self.recommend_move_after_exploration.policy(
196
+ self.tree.root_node
197
+ )
198
+
199
+ best_branch: BranchKey = self.recommend_move_after_exploration.sample(
200
+ policy, random_generator
201
+ )
202
+
203
+ self.tree_manager.print_best_line(
204
+ tree=self.tree
205
+ ) # todo maybe almost best chosen line no?
206
+
207
+ move_recommendation = BranchRecommendation(
208
+ branch_key=best_branch,
209
+ evaluation=self.tree.root_node.tree_evaluation.evaluate(),
210
+ policy=policy,
211
+ child_evals=compute_child_evals(self.tree.root_node),
212
+ )
213
+
214
+ tree_exploration_result: TreeExplorationResult[NodeT] = TreeExplorationResult(
215
+ branch_recommendation=move_recommendation, tree=self.tree
216
+ )
217
+
218
+ return tree_exploration_result
219
+
220
+
221
+ def create_tree_exploration[StateT: TurnState](
222
+ node_selector_create: NodeSelectorFactory,
223
+ starting_state: StateT,
224
+ tree_manager: tree_man.AlgorithmNodeTreeManager[AlgorithmNode[StateT]],
225
+ tree_factory: ValueTreeFactory[StateT],
226
+ stopping_criterion_args: AllStoppingCriterionArgs,
227
+ recommend_move_after_exploration: recommender_rule.AllRecommendFunctionsArgs,
228
+ queue_progress_player: Queue[IsDataclass] | None,
229
+ ) -> TreeExploration[AlgorithmNode[StateT]]:
230
+ """
231
+ Creates a TreeExploration object with the specified dependencies.
232
+
233
+ Args:
234
+ - node_selector_create: The factory function for creating the node selector.
235
+ - starting_board: The starting chess board position.
236
+ - tree_manager: The manager for the tree structure.
237
+ - tree_factory: The factory for creating the tree structure.
238
+ - stopping_criterion_args: The arguments for creating the stopping criterion.
239
+ - recommend_move_after_exploration: The recommender rule for selecting the best move after the tree exploration.
240
+
241
+ Returns:
242
+ - TreeExploration: The created TreeExploration object.
243
+ """
244
+ # creates the tree
245
+ tree: trees.Tree[AlgorithmNode[StateT]] = tree_factory.create(
246
+ starting_state=starting_state
247
+ )
248
+ # creates the node selector
249
+ node_selector: node_sel.NodeSelector[AlgorithmNode[StateT]] = node_selector_create()
250
+ stopping_criterion: ProgressMonitor[AlgorithmNode[StateT]] = (
251
+ create_stopping_criterion(
252
+ args=stopping_criterion_args, node_selector=node_selector
253
+ )
254
+ )
255
+
256
+ def notify_percent_function(progress_percent: int) -> None:
257
+ """Send progress updates to the queue if configured."""
258
+ if queue_progress_player is not None:
259
+ queue_progress_player.put(
260
+ PlayerProgressMessage(
261
+ progress_percent=progress_percent, player_color=starting_state.turn
262
+ )
263
+ )
264
+
265
+ tree_exploration: TreeExploration[AlgorithmNode[StateT]] = TreeExploration(
266
+ tree=tree,
267
+ tree_manager=tree_manager,
268
+ stopping_criterion=stopping_criterion,
269
+ node_selector=node_selector,
270
+ recommend_move_after_exploration=recommend_move_after_exploration,
271
+ notify_percent_function=notify_percent_function,
272
+ )
273
+
274
+ return tree_exploration
@@ -0,0 +1,29 @@
1
+ """
2
+ This module provides functionality for managing algorithm node trees.
3
+
4
+ The module includes classes for creating and managing algorithm node tree managers,
5
+ expanding tree structures, and managing tree expansions.
6
+
7
+ Classes:
8
+ - AlgorithmNodeTreeManager: A class for managing algorithm node trees.
9
+ - TreeManager: A class for managing tree structures.
10
+ - TreeExpansion: A class for representing a single tree expansion.
11
+ - TreeExpansions: A class for managing multiple tree expansions.
12
+
13
+ Functions:
14
+ - create_algorithm_node_tree_manager: A function for creating an algorithm node tree manager.
15
+
16
+ """
17
+
18
+ from .algorithm_node_tree_manager import AlgorithmNodeTreeManager
19
+ from .factory import create_algorithm_node_tree_manager
20
+ from .tree_expander import TreeExpansion, TreeExpansions
21
+ from .tree_manager import TreeManager
22
+
23
+ __all__ = [
24
+ "create_algorithm_node_tree_manager",
25
+ "TreeManager",
26
+ "AlgorithmNodeTreeManager",
27
+ "TreeExpansion",
28
+ "TreeExpansions",
29
+ ]