"""
This module contains classes for managing update instructions in a batch.
Classes:
- UpdateInstructions: Represents update instructions for a single node.
- UpdateInstructionsBatch: Represents a batch of update instructions for multiple nodes.
"""
from dataclasses import dataclass, field
from typing import Any, Self
from chipiron.environments.chess_env.move.imove import moveKey
from chipiron.players.move_selector.treevalue.nodes import ITreeNode
from chipiron.utils.dict_of_numbered_dict_with_pointer_on_max import (
DictOfNumberedDictWithPointerOnMax,
)
from .index_block import (
IndexUpdateInstructionsFromOneNode,
IndexUpdateInstructionsTowardsOneParentNode,
)
from .value_block import (
ValueUpdateInstructionsFromOneNode,
ValueUpdateInstructionsTowardsOneParentNode,
)
[docs]@dataclass(slots=True)
class UpdateInstructionsFromOneNode:
"""
Represents update instructions generated from a single node.
Attributes:
- value_block: The value update instructions generated from a single node.
- index_block: The index update instructions generated from a single node.
"""
value_block: ValueUpdateInstructionsFromOneNode | None = None
index_block: IndexUpdateInstructionsFromOneNode | None = None
[docs]@dataclass(slots=True)
class UpdateInstructionsTowardsOneParentNode:
"""
Represents update instructions for a single node.
Attributes:
- value_block: The value update instructions block.
- index_block: The index update instructions block.
"""
value_updates_toward_one_parent_node: (
ValueUpdateInstructionsTowardsOneParentNode | None
) = None
index_updates_toward_one_parent_node: (
IndexUpdateInstructionsTowardsOneParentNode | None
) = None
[docs] def add_update_from_a_child_node(
self,
update_from_a_child_node: UpdateInstructionsFromOneNode,
move_from_parent_to_child: moveKey,
) -> None:
assert self.value_updates_toward_one_parent_node is not None
assert update_from_a_child_node.value_block is not None
self.value_updates_toward_one_parent_node.add_update_from_one_child_node(
move_from_parent_to_child=move_from_parent_to_child,
update_from_one_child_node=update_from_a_child_node.value_block,
)
if self.index_updates_toward_one_parent_node is None:
assert update_from_a_child_node.index_block is None
else:
if update_from_a_child_node.index_block is not None:
self.index_updates_toward_one_parent_node.add_update_from_one_child_node(
move_from_parent_to_child=move_from_parent_to_child,
update_from_one_child_node=update_from_a_child_node.index_block,
)
[docs] def add_updates_towards_one_parent_node(self, another_update: Self) -> None:
assert self.value_updates_toward_one_parent_node is not None
assert another_update.value_updates_toward_one_parent_node is not None
self.value_updates_toward_one_parent_node.add_update_toward_one_parent_node(
another_update.value_updates_toward_one_parent_node
)
if self.index_updates_toward_one_parent_node is None:
assert another_update.index_updates_toward_one_parent_node is None
else:
if another_update.index_updates_toward_one_parent_node is not None:
self.index_updates_toward_one_parent_node.add_update_toward_one_parent_node(
another_update.index_updates_toward_one_parent_node
)
[docs] def print_info(self) -> None:
"""
Prints information about the update instructions.
"""
print("printing info of update instructions")
assert (
self.index_updates_toward_one_parent_node is not None
and self.value_updates_toward_one_parent_node is not None
)
self.value_updates_toward_one_parent_node.print_info()
self.index_updates_toward_one_parent_node.print_info()
[docs] def empty(self) -> bool:
"""
Checks if the update instructions are empty.
Returns:
- True if the update instructions are empty, False otherwise.
"""
assert self.value_updates_toward_one_parent_node is not None
return self.value_updates_toward_one_parent_node.empty() and (
self.index_updates_toward_one_parent_node is None
or self.index_updates_toward_one_parent_node.empty()
)
[docs]@dataclass
class UpdateInstructionsTowardsMultipleNodes:
# mapping from nodes to the update instructions that are intended to them for consideration (performing the updates)
one_node_instructions: DictOfNumberedDictWithPointerOnMax[
ITreeNode[Any], UpdateInstructionsTowardsOneParentNode
] = field(default_factory=DictOfNumberedDictWithPointerOnMax)
[docs] def add_update_from_one_child_node(
self,
update_from_child_node: UpdateInstructionsFromOneNode,
parent_node: ITreeNode[Any],
move_from_parent: moveKey,
) -> None:
if parent_node not in self.one_node_instructions:
# build the UpdateInstructionsTowardsOneParentNode
assert update_from_child_node.value_block is not None
value_updates_toward_one_parent_node: (
ValueUpdateInstructionsTowardsOneParentNode
)
value_updates_toward_one_parent_node = (
ValueUpdateInstructionsTowardsOneParentNode(
moves_with_updated_value=(
{move_from_parent}
if update_from_child_node.value_block.new_value_for_node
else set()
),
moves_with_updated_over=(
{move_from_parent}
if update_from_child_node.value_block.is_node_newly_over
else set()
),
moves_with_updated_best_move=(
{move_from_parent}
if update_from_child_node.value_block.new_best_move_for_node
else set()
),
)
)
index_updates_toward_one_parent_node: (
IndexUpdateInstructionsTowardsOneParentNode | None
)
if update_from_child_node.index_block is not None:
index_updates_toward_one_parent_node = (
IndexUpdateInstructionsTowardsOneParentNode(
moves_with_updated_index=(
{move_from_parent}
if update_from_child_node.index_block.updated_index
else set()
),
)
)
else:
index_updates_toward_one_parent_node = None
update_instructions_towards_parent: UpdateInstructionsTowardsOneParentNode
update_instructions_towards_parent = UpdateInstructionsTowardsOneParentNode(
value_updates_toward_one_parent_node=value_updates_toward_one_parent_node,
index_updates_toward_one_parent_node=index_updates_toward_one_parent_node,
)
self.one_node_instructions[parent_node] = update_instructions_towards_parent
else:
# update the UpdateInstructionsTowardsOneParentNode
self.one_node_instructions[parent_node].add_update_from_a_child_node(
update_from_a_child_node=update_from_child_node,
move_from_parent_to_child=move_from_parent,
)
[docs] def add_updates_towards_one_parent_node(
self,
update_from_child_node: UpdateInstructionsTowardsOneParentNode,
parent_node: ITreeNode[Any],
) -> None:
if parent_node in self.one_node_instructions:
self.one_node_instructions[parent_node].add_updates_towards_one_parent_node(
another_update=update_from_child_node
)
else:
self.one_node_instructions[parent_node] = update_from_child_node
[docs] def pop_item(self) -> tuple[ITreeNode[Any], UpdateInstructionsTowardsOneParentNode]:
return self.one_node_instructions.popitem()
[docs] def __bool__(self) -> bool:
"""
Checks if the data structure is non-empty.
Returns:
bool: True if the data structure is non-empty, False otherwise.
"""
return bool(self.one_node_instructions)