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,88 @@
1
+ """
2
+ This module contains functions for selecting elements based on Zipf distribution.
3
+
4
+ Zipf distribution is a discrete probability distribution that models the occurrence of elements in a dataset.
5
+ The functions in this module provide methods for selecting elements based on their rank and value.
6
+
7
+ """
8
+
9
+ from math import e, log
10
+ from random import Random
11
+
12
+
13
+ def zipf_picks(
14
+ ranks_values: dict[int, int | float],
15
+ random_generator: Random,
16
+ shift: bool = False,
17
+ random_pick: bool = False,
18
+ ) -> int:
19
+ """
20
+ Selects an element based on its rank and value.
21
+
22
+ Args:
23
+ ranks_values (dict[int, int | float]): A dictionary containing the ranks and values of the elements.
24
+ random_generator (random.Random): A random number generator.
25
+ shift (bool, optional): Whether to shift the ranks. Defaults to False.
26
+ random_pick (bool, optional): Whether to perform a random pick. Defaults to False.
27
+
28
+ Returns:
29
+ int: The rank of the selected element.
30
+
31
+ Raises:
32
+ Exception: If random_pick is True (not implemented yet).
33
+
34
+ """
35
+ shift_rank: int
36
+
37
+ if shift:
38
+ shift_rank = min(ranks_values.keys())
39
+ else:
40
+ shift_rank = 0
41
+
42
+ best_weight: float | None = None
43
+ best_rank: int = shift_rank
44
+
45
+ weights: list[float] = []
46
+ for rank, value in ranks_values.items():
47
+ shifted_rank = rank - shift_rank + 1
48
+ log_term: float = (log(e * shifted_rank)) ** 2
49
+ weight: float = value * shifted_rank * log_term + 0.0001
50
+ weights.append(weight)
51
+
52
+ if best_weight is None or weight < best_weight:
53
+ best_weight = weight
54
+ best_rank = rank
55
+
56
+ if random_pick:
57
+ choices_ = random_generator.choices(
58
+ list(ranks_values.keys()), weights=weights, k=1
59
+ )
60
+ raise NotImplementedError("not coded yet", choices_) # not codeed properly yet
61
+
62
+ return best_rank
63
+
64
+
65
+ def zipf_picks_random[T](ordered_list_elements: list[T], random_generator: Random) -> T:
66
+ """
67
+ Selects a random element from an ordered list based on Zipf distribution.
68
+
69
+ Args:
70
+ ordered_list_elements (list[T]): A list of elements.
71
+ random_generator (random.Random): A random number generator.
72
+
73
+ Returns:
74
+ T: The selected element.
75
+
76
+ Raises:
77
+ AssertionError: If the length of the list is 0.
78
+
79
+ """
80
+ length_list = len(ordered_list_elements)
81
+ assert length_list > 0
82
+ weights = [
83
+ 1 / (index + 1) / (log(e * (index + 1))) ** 0 for index in range(length_list)
84
+ ]
85
+ picked_element = random_generator.choices(
86
+ ordered_list_elements, weights=weights, k=1
87
+ )
88
+ return picked_element[0]
@@ -0,0 +1,249 @@
1
+ """
2
+ This module contains classes and functions related to opening instructions in a chess game.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from random import Random
8
+ from typing import Any, ItemsView, Iterator, Self, ValuesView
9
+
10
+ from valanga import BranchKey
11
+
12
+ from anemone import nodes
13
+ from anemone.nodes.utils import (
14
+ a_branch_str_sequence_from_root,
15
+ a_move_key_sequence_from_root,
16
+ )
17
+
18
+ type OpeningInstructionKey = tuple[int, BranchKey]
19
+
20
+
21
+ @dataclass(slots=True)
22
+ class OpeningInstruction[NodeT: nodes.ITreeNode[Any] = nodes.ITreeNode[Any]]:
23
+ """
24
+ Represents an opening instruction for a specific node in the game tree.
25
+ """
26
+
27
+ node_to_open: NodeT
28
+ branch: BranchKey
29
+
30
+ def print_info(self) -> None:
31
+ """
32
+ Prints information about the opening instruction.
33
+ """
34
+ print(
35
+ f"OpeningInstruction: node_to_open {self.node_to_open.id} at hm {self.node_to_open.tree_depth} {self.node_to_open.state}| "
36
+ f"a path from root to node_to_open is {a_move_key_sequence_from_root(self.node_to_open)} {a_branch_str_sequence_from_root(self.node_to_open)}| "
37
+ f"self.branch {self.branch} {self.node_to_open.state.branch_name_from_key(self.branch)}"
38
+ )
39
+
40
+
41
+ class OpeningInstructions[NodeT: nodes.ITreeNode[Any] = nodes.ITreeNode[Any]]:
42
+ # todo do we need a dict? why not a set? verify
43
+
44
+ """
45
+ Represents a collection of opening instructions.
46
+ """
47
+
48
+ batch: dict[OpeningInstructionKey, OpeningInstruction[NodeT]]
49
+
50
+ def __init__(
51
+ self,
52
+ dictionary: dict[OpeningInstructionKey, OpeningInstruction[NodeT]]
53
+ | None = None,
54
+ ) -> None:
55
+ """
56
+ Initializes the OpeningInstructions object.
57
+
58
+ Args:
59
+ dictionary: A dictionary of opening instructions (optional).
60
+ """
61
+ # here i use a dictionary because they are insertion ordered until there is an ordered set in python
62
+ # order is important because some method give a batch where the last element in the batch are prioritary
63
+ self.batch = {}
64
+
65
+ if dictionary is not None:
66
+ for key in dictionary:
67
+ self[key] = dictionary[key]
68
+
69
+ def __setitem__(
70
+ self, key: OpeningInstructionKey, value: OpeningInstruction[NodeT]
71
+ ) -> None:
72
+ """
73
+ Sets an opening instruction in the collection.
74
+
75
+ Args:
76
+ key: The key for the opening instruction.
77
+ value: The opening instruction.
78
+ """
79
+ # key is supposed to be a tuple with (node_to_open, move_to_play)
80
+ self.batch[key] = value
81
+
82
+ def __getitem__(self, key: OpeningInstructionKey) -> OpeningInstruction[NodeT]:
83
+ """
84
+ Retrieves an opening instruction from the collection.
85
+
86
+ Args:
87
+ key: The key for the opening instruction.
88
+
89
+ Returns:
90
+ The opening instruction.
91
+ """
92
+ # assert(0==1)
93
+ return self.batch[key]
94
+
95
+ def __iter__(self) -> Iterator[OpeningInstructionKey]:
96
+ """
97
+ Returns an iterator over the keys of the opening instructions.
98
+
99
+ Returns:
100
+ An iterator over the keys.
101
+ """
102
+ return iter(self.batch)
103
+
104
+ def __bool__(self) -> bool:
105
+ """
106
+ Checks if the collection is non-empty.
107
+
108
+ Returns:
109
+ True if the collection is non-empty, False otherwise.
110
+ """
111
+ return bool(self.batch)
112
+
113
+ def merge(self, another_opening_instructions_batch: Self) -> None:
114
+ """
115
+ Merges another batch of opening instructions into the current collection.
116
+
117
+ Args:
118
+ another_opening_instructions_batch: Another OpeningInstructions object.
119
+ """
120
+ for (
121
+ opening_instruction_key,
122
+ opening_instruction,
123
+ ) in another_opening_instructions_batch.items():
124
+ if opening_instruction_key not in self.batch:
125
+ self.batch[opening_instruction_key] = opening_instruction
126
+
127
+ def pop_items(self, how_many: int, popped: Self) -> None:
128
+ """
129
+ Pops a specified number of opening instructions from the collection.
130
+
131
+ Args:
132
+ how_many: The number of opening instructions to pop.
133
+ popped: An OpeningInstructions object to store the popped instructions.
134
+ """
135
+ how_many = min(how_many, len(self.batch))
136
+ for _ in range(how_many):
137
+ key, value = self.batch.popitem()
138
+ popped[key] = value
139
+
140
+ def values(self) -> ValuesView[OpeningInstruction[NodeT]]:
141
+ """
142
+ Returns a view of the values in the collection.
143
+
144
+ Returns:
145
+ A view of the values.
146
+ """
147
+ return self.batch.values()
148
+
149
+ def items(self) -> ItemsView[OpeningInstructionKey, OpeningInstruction[NodeT]]:
150
+ """
151
+ Returns a view of the items (key-value pairs) in the collection.
152
+
153
+ Returns:
154
+ A view of the items.
155
+ """
156
+ return self.batch.items()
157
+
158
+ def print_info(self) -> None:
159
+ """
160
+ Prints information about the opening instructions in the collection.
161
+ """
162
+ print("OpeningInstructionsBatch: batch contains", len(self.batch), "elements:")
163
+ for _key, opening_instructions in self.batch.items():
164
+ opening_instructions.print_info()
165
+
166
+ def __len__(self) -> int:
167
+ """
168
+ Returns the number of opening instructions in the collection.
169
+
170
+ Returns:
171
+ The number of opening instructions.
172
+ """
173
+ return len(self.batch)
174
+
175
+
176
+ def create_instructions_to_open_all_branches[NodeT: nodes.ITreeNode[Any]](
177
+ branches_to_play: list[BranchKey], node_to_open: NodeT
178
+ ) -> OpeningInstructions[NodeT]:
179
+ """
180
+ Creates opening instructions for all possible moves to play from a given node.
181
+
182
+ Args:
183
+ moves_to_play: A list of chess moves.
184
+ node_to_open: The node to open.
185
+
186
+ Returns:
187
+ An OpeningInstructions object containing the opening instructions.
188
+ """
189
+ opening_instructions_batch: OpeningInstructions[NodeT] = OpeningInstructions()
190
+
191
+ for branch_to_play in branches_to_play:
192
+ # at the moment it looks redundant keys are almost the same as values but its clean
193
+ # the keys are here for fast and redundant proof insertion
194
+ # and the values are here for clean data processing
195
+ opening_instructions_batch[(node_to_open.id, branch_to_play)] = (
196
+ OpeningInstruction(node_to_open=node_to_open, branch=branch_to_play)
197
+ )
198
+ # node_to_open.non_opened_legal_moves.add(move_to_play)
199
+ return opening_instructions_batch
200
+
201
+
202
+ class OpeningType(Enum):
203
+ """
204
+ Represents the type of opening to use.
205
+ """
206
+
207
+ ALL_CHILDREN = "all_children"
208
+
209
+
210
+ class OpeningInstructor:
211
+ """
212
+ Represents an opening instructor that provides opening instructions based on a specific opening type.
213
+ """
214
+
215
+ def __init__(self, opening_type: OpeningType, random_generator: Random) -> None:
216
+ """
217
+ Initializes the OpeningInstructor object.
218
+
219
+ Args:
220
+ opening_type: The type of opening to use.
221
+ random_generator: A random number generator.
222
+ """
223
+ self.opening_type = opening_type
224
+ self.random_generator = random_generator
225
+
226
+ def all_branches_to_open(
227
+ self, node_to_open: nodes.ITreeNode[Any]
228
+ ) -> list[BranchKey]:
229
+ """
230
+ Returns a list of all possible branches to open from a given node.
231
+
232
+ Args:
233
+ node_to_open: The node to open.
234
+
235
+ Returns:
236
+ A list of chess moves.
237
+ """
238
+ if self.opening_type == OpeningType.ALL_CHILDREN:
239
+ node_to_open.all_branches_generated = True
240
+ branches_to_play: list[BranchKey] = node_to_open.state.branch_keys.get_all()
241
+
242
+ # this shuffling add randomness to the playing style
243
+ # (it stills depends on the random seed, but if random seed varies then the behavior will be more random)
244
+ # DEACTIVATED ATM BECAUSE I DO NOT UNDERSTAND or FORGOT THE USE CASE: MAYBE DEAD SINCE SEED SYSTEM CHANGED
245
+ # self.random_generator.shuffle(moves_to_play)
246
+
247
+ else:
248
+ raise NotImplementedError("Hello-la")
249
+ return branches_to_play
File without changes
@@ -0,0 +1,141 @@
1
+ """
2
+ This module contains the implementation of the RecurZipfBase class, which is a node selector for a move selector tree.
3
+
4
+ The RecurZipfBase class is responsible for selecting the next node to explore in a move selector tree based on the RecurZipf algorithm.
5
+
6
+ Classes:
7
+ - RecurZipfBase: The RecurZipfBase Node selector.
8
+
9
+ """
10
+
11
+ from dataclasses import dataclass
12
+ from random import Random
13
+ from typing import TYPE_CHECKING, Any, Literal
14
+
15
+ from anemone import trees
16
+ from anemone.node_selector.branch_explorer import SamplingPriorities, ZipfBranchExplorer
17
+ from anemone.node_selector.node_selector_types import NodeSelectorType
18
+ from anemone.node_selector.opening_instructions import (
19
+ OpeningInstructions,
20
+ OpeningInstructor,
21
+ create_instructions_to_open_all_branches,
22
+ )
23
+ from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
24
+
25
+ if TYPE_CHECKING:
26
+ from anemone import tree_manager as tree_man
27
+
28
+
29
+ @dataclass
30
+ class RecurZipfBaseArgs:
31
+ """
32
+ Arguments for the RecurZipfBase node selector.
33
+
34
+ Attributes:
35
+ move_explorer_priority (SamplingPriorities): The priority for move exploration.
36
+ """
37
+
38
+ type: Literal[NodeSelectorType.RECUR_ZIPF_BASE]
39
+ branch_explorer_priority: SamplingPriorities
40
+
41
+
42
+ class RecurZipfBase[NodeT: AlgorithmNode[Any] = AlgorithmNode[Any]]:
43
+ """The RecurZipfBase Node selector"""
44
+
45
+ opening_instructor: OpeningInstructor
46
+
47
+ def __init__(
48
+ self,
49
+ args: RecurZipfBaseArgs,
50
+ random_generator: Random,
51
+ opening_instructor: OpeningInstructor,
52
+ ) -> None:
53
+ """
54
+ Initializes a new instance of the RecurZipfBase class.
55
+
56
+ Args:
57
+ - args (RecurZipfBaseArgs): The arguments for the RecurZipfBase node selector.
58
+ - random_generator (random.Random): The random number generator.
59
+ - opening_instructor (OpeningInstructor): The opening instructor.
60
+
61
+ """
62
+ self.opening_instructor = opening_instructor
63
+ self.branch_explorer = ZipfBranchExplorer(
64
+ args.branch_explorer_priority, random_generator
65
+ )
66
+ self.random_generator = random_generator
67
+
68
+ def choose_node_and_branch_to_open(
69
+ self,
70
+ tree: trees.Tree[NodeT],
71
+ latest_tree_expansions: "tree_man.TreeExpansions[NodeT]",
72
+ ) -> OpeningInstructions[NodeT]:
73
+ """
74
+ Chooses the next node to explore and the move to open.
75
+
76
+ Args:
77
+ - tree (trees.Tree[AlgorithmNode]): The move selector tree.
78
+ - latest_tree_expansions (tree_man.TreeExpansions): The latest tree expansions.
79
+
80
+ Returns:
81
+ - OpeningInstructions: The instructions for opening the selected move.
82
+
83
+ """
84
+ # todo maybe proportions and proportions can be valuesorted dict with smart updates
85
+
86
+ _ = latest_tree_expansions # not used here
87
+ opening_instructions: OpeningInstructions[NodeT]
88
+ # TODO make sure this block is put in chipiron now with a wrapper
89
+ # best_node_sequence = best_node_sequence_from_node(tree.root_node)
90
+ # if best_node_sequence:
91
+ # last_node_in_best_line = best_node_sequence[-1]
92
+ # assert isinstance(last_node_in_best_line, AlgorithmNode)
93
+ # if (
94
+ # last_node_in_best_line.state.is_attacked(
95
+ # not last_node_in_best_line.tree_node.player_to_move
96
+ # )
97
+ # and not last_node_in_best_line.minmax_evaluation.is_over()
98
+ # ):
99
+ # # print('best line is underattacked')
100
+ # if self.random_generator.random() > 0.5:
101
+ # # print('best line is underattacked and i do')
102
+ # all_moves_to_open: list[BranchKey] = (
103
+ # self.opening_instructor.all_branches_to_open(
104
+ # node_to_open=last_node_in_best_line.tree_node
105
+ # )
106
+ # )
107
+ # opening_instructions = create_instructions_to_open_all_branches(
108
+ # branches_to_play=all_moves_to_open,
109
+ # node_to_open=last_node_in_best_line,
110
+ # )
111
+ # return opening_instructions
112
+
113
+ wandering_node: NodeT = tree.root_node
114
+
115
+ while wandering_node.tree_evaluation.branches_not_over:
116
+ assert not wandering_node.is_over()
117
+ branch = self.branch_explorer.sample_branch_to_explore(
118
+ tree_node_to_sample_from=wandering_node
119
+ )
120
+ next_node = wandering_node.branches_children[branch]
121
+ assert next_node is not None
122
+ wandering_node = next_node
123
+
124
+ all_branches_to_open = self.opening_instructor.all_branches_to_open(
125
+ node_to_open=wandering_node
126
+ )
127
+ opening_instructions = create_instructions_to_open_all_branches(
128
+ branches_to_play=all_branches_to_open, node_to_open=wandering_node
129
+ )
130
+
131
+ return opening_instructions
132
+
133
+ def __str__(self) -> str:
134
+ """
135
+ Returns a string representation of the RecurZipfBase node selector.
136
+
137
+ Returns:
138
+ - str: The string representation of the RecurZipfBase node selector.
139
+
140
+ """
141
+ return "RecurZipfBase"
@@ -0,0 +1,19 @@
1
+ """
2
+ This module provides functionality for creating and managing Sequool objects.
3
+
4
+ A Sequool object represents a sequence of operations that can be applied to a dataset.
5
+
6
+ Example usage:
7
+ from .factory import create_sequool, SequoolArgs
8
+
9
+ # Create a Sequool object
10
+ sequool = create_sequool()
11
+
12
+ # Define the arguments for the Sequool object
13
+ sequool_args = SequoolArgs()
14
+
15
+ """
16
+
17
+ from .factory import SequoolArgs, create_sequool
18
+
19
+ __all__ = ["create_sequool", "SequoolArgs"]
@@ -0,0 +1,102 @@
1
+ """
2
+ factory for sequool node selector
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from functools import partial
7
+ from random import Random
8
+ from typing import Literal
9
+
10
+ from anemone import trees
11
+ from anemone.node_selector.node_selector_types import (
12
+ NodeSelectorType,
13
+ )
14
+ from anemone.node_selector.opening_instructions import (
15
+ OpeningInstructor,
16
+ )
17
+ from anemone.nodes.algorithm_node.algorithm_node import AlgorithmNode
18
+
19
+ from .sequool import (
20
+ ConsiderNodesFromTreeDepths,
21
+ RandomAllSelector,
22
+ Sequool,
23
+ StaticNotOpenedSelector,
24
+ TreeDepthSelector,
25
+ consider_nodes_from_all_lesser_tree_depths_in_descendants,
26
+ consider_nodes_from_all_lesser_tree_depths_in_sub_stree,
27
+ consider_nodes_only_from_tree_depths_in_descendants,
28
+ )
29
+
30
+
31
+ @dataclass
32
+ class SequoolArgs:
33
+ """
34
+ Arguments for the Sequool node selector.
35
+
36
+ Attributes:
37
+ recursive_selection_on_all_nodes (bool): Flag indicating whether to perform recursive selection on all nodes.
38
+ random_depth_pick (bool): Flag indicating whether to randomly pick a depth for selection.
39
+ consider_all_lesser_half_move (bool): Flag indicating whether to consider all lesser half moves.
40
+ """
41
+
42
+ type: Literal[NodeSelectorType.SEQUOOL]
43
+ recursive_selection_on_all_nodes: bool
44
+ random_depth_pick: bool
45
+ consider_all_lesser_half_move: bool
46
+
47
+
48
+ def create_sequool(
49
+ opening_instructor: OpeningInstructor,
50
+ args: SequoolArgs,
51
+ random_generator: Random,
52
+ ) -> Sequool[AlgorithmNode]:
53
+ """
54
+ Create a sequool node selector object.
55
+
56
+ Args:
57
+ opening_instructor: An opening instructor object.
58
+ args: Dictionary of arguments.
59
+ random_generator: Random generator object.
60
+
61
+ Returns:
62
+ A sequool node selector object.
63
+
64
+ """
65
+ all_nodes_not_opened = trees.Descendants[AlgorithmNode]()
66
+ tree_depth_selector: TreeDepthSelector
67
+ if args.recursive_selection_on_all_nodes:
68
+ tree_depth_selector = RandomAllSelector()
69
+ else:
70
+ tree_depth_selector = StaticNotOpenedSelector(
71
+ all_nodes_not_opened=all_nodes_not_opened
72
+ )
73
+
74
+ consider_nodes_from_tree_depths: ConsiderNodesFromTreeDepths[AlgorithmNode]
75
+ if args.recursive_selection_on_all_nodes:
76
+ consider_nodes_from_tree_depths = (
77
+ consider_nodes_from_all_lesser_tree_depths_in_sub_stree
78
+ )
79
+ else:
80
+ if args.consider_all_lesser_half_move:
81
+ consider_nodes_from_all_lesser_tree_depths = partial(
82
+ consider_nodes_from_all_lesser_tree_depths_in_descendants,
83
+ descendants=all_nodes_not_opened,
84
+ )
85
+ consider_nodes_from_tree_depths = consider_nodes_from_all_lesser_tree_depths
86
+ else:
87
+ consider_nodes_only_from_tree_depths = partial(
88
+ consider_nodes_only_from_tree_depths_in_descendants,
89
+ descendants=all_nodes_not_opened,
90
+ )
91
+ consider_nodes_from_tree_depths = consider_nodes_only_from_tree_depths
92
+ sequool: Sequool[AlgorithmNode] = Sequool(
93
+ opening_instructor=opening_instructor,
94
+ all_nodes_not_opened=all_nodes_not_opened,
95
+ recursif=args.recursive_selection_on_all_nodes,
96
+ tree_depth_selector=tree_depth_selector,
97
+ random_depth_pick=args.random_depth_pick,
98
+ random_generator=random_generator,
99
+ consider_nodes_from_tree_depths=consider_nodes_from_tree_depths,
100
+ )
101
+
102
+ return sequool