Advertisement
pakuula

Untitled

Jan 14th, 2025
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.85 KB | None | 0 0
  1. """Поиск лучшей последовательности ходов для белых в игре с нулевой суммой."""
  2. from collections import defaultdict
  3. from typing import List, Self, Tuple
  4.  
  5. WHITE = "W"
  6. BLACK = "B"
  7.  
  8.  
  9. class Move:
  10.     """Ход в игре: позиция и сторона"""
  11.  
  12.     def __init__(self, position: int, side: str):
  13.         self.position = position
  14.         self.side = side
  15.  
  16.     def __hash__(self) -> int:
  17.         return hash((self.position, self.side))
  18.  
  19.     def __eq__(self, other) -> bool:
  20.         if isinstance(other, Move):
  21.             return self.position == other.position and self.side == other.side
  22.         return False
  23.  
  24.     def __repr__(self) -> str:
  25.         return f"Move({self.position}, {self.side})"
  26.  
  27.     def __str__(self) -> str:
  28.         return f"{self.side}:{self.position}"
  29.  
  30.     @property
  31.     def opponent(self) -> str:
  32.         """Цвет противника"""
  33.         return WHITE if self.side == BLACK else BLACK
  34.  
  35.  
  36. class Game:
  37.     """Игра: последовательность ходов, сторона, делающая первый ход, и итоговый счёт"""
  38.  
  39.     def __init__(self, moves: Tuple[int], side: str, score: Tuple[int]):
  40.         self.moves = moves
  41.         self.side = side
  42.         self.score = score
  43.  
  44.     def __len__(self) -> int:
  45.         return len(self.moves)
  46.  
  47.     @property
  48.     def head(self) -> Move:
  49.         """Первый ход"""
  50.         return Move(self.moves[0], self.side)
  51.  
  52.     @property
  53.     def tail(self) -> Self:
  54.         """Оставшиеся ходы"""
  55.         return Game(self.moves[1:], self.opponent, self.score)
  56.  
  57.     @property
  58.     def opponent(self) -> str:
  59.         """Цвет противника"""
  60.         return self.head.opponent
  61.  
  62.  
  63. class Tree:
  64.     """Дерево игры: ход и оценка хода. Дочерние узлы - возможные продолжения игры.
  65.    Оценка хода - разница в счёте в наихудшем продолжении после данного хода."""
  66.  
  67.     def __init__(self, move: Move, score: Tuple[int] = None):
  68.         self.move = move
  69.         self.score = score
  70.         self._minimax: int = None
  71.         if score:
  72.             # Если последний ход в игре, то оценка хода - итоговый счёт
  73.             self._minimax = score[0] - score[1]
  74.         self.children: List[Self] = []
  75.  
  76.     @property
  77.     def minimax(self) -> int:
  78.         """Оценка хода: разница в счёте в наихудшем продолжении после данного хода."""
  79.         if self._minimax is not None:
  80.             return self._minimax
  81.         if not self.children:
  82.             raise ValueError("Листовой узел, но итоговый счёт не задан.")
  83.         if self.move.side == WHITE:
  84.             # Чёрные выберут продолжение с минимальным счётом.
  85.             return min(c.minimax for c in self.children)
  86.         # Белые выберут продолжение с максимальным счётом
  87.         return max(c.minimax for c in self.children)
  88.  
  89.     def add_child(self, child) -> Self:
  90.         """Добавить дочернее дерево продолжения игры"""
  91.         self.children.append(child)
  92.         return self
  93.  
  94.     def get_by_minimax(self) -> Self:
  95.         """Возвращает дочернее дерево с наилучшим продолжением игры."""
  96.         minimax = self.minimax
  97.         for child in self.children:
  98.             if child.minimax == minimax:
  99.                 return child
  100.  
  101.     def best_game(self) -> Game:
  102.         """Возвращает лучшую игру, начинающуюся с этого хода."""
  103.         if not self.children:
  104.             return Game([self.move.position], self.move.side, self.score)
  105.         child = self.get_by_minimax()
  106.         best_game = child.best_game()
  107.         return Game(
  108.             [self.move.position] + best_game.moves, self.move.side, best_game.score
  109.         )
  110.  
  111.     def print_tree(self, level=0):
  112.         """Печатает дерево игры"""
  113.         print("| " * level, self.move, self.minimax)
  114.         for child in self.children:
  115.             child.print_tree(level + 1)
  116.  
  117.  
  118. class TreeBuilder:
  119.     """Построитель дерева игры из списка игр."""
  120.  
  121.     def __init__(self, move, games):
  122.         self.games = games
  123.         self.tree = Tree(move)
  124.         self.games_by_move = defaultdict(list)
  125.         self._build_tree()
  126.  
  127.     def _build_tree(self):
  128.         for game in self.games:
  129.             if len(game) == 1:
  130.                 self.tree.add_child(Tree(game.head, game.score))
  131.             else:
  132.                 self.games_by_move[game.head].append(game.tail)
  133.         for move, tails in self.games_by_move.items():
  134.             self.tree.add_child(TreeBuilder(move, tails).tree)
  135.         self.tree.children.sort(key=lambda c: c.move.position)
  136.  
  137.  
  138. # список игр из задачи
  139. GAMES = [
  140.     Game((0, 1, 2, 3, 6), WHITE, (30, 34)),
  141.     Game((0, 1, 3, 2, 6), WHITE, (32, 32)),
  142.     Game((0, 1, 6, None, 2, 3), WHITE, (27, 37)),
  143.     Game((0, 1, 6, None, 3, 2), WHITE, (29, 35)),
  144.     Game((1, 2, 0, None, 3, None, 6), WHITE, (36, 28)),
  145.     Game((1, 2, 0, None, 6, None, 3), WHITE, (36, 28)),
  146.     Game((1, 2, 3, 0, 6), WHITE, (27, 37)),
  147.     Game((1, 2, 6, 0, 3), WHITE, (30, 34)),
  148.     Game((1, 3, 0, None, 2, None, 6), WHITE, (35, 29)),
  149.     Game((1, 3, 0, None, 6, None, 2), WHITE, (35, 29)),
  150.     Game((1, 3, 2, 0, 6), WHITE, (25, 39)),
  151.     Game((1, 3, 6, None, 0, None, 2), WHITE, (35, 29)),
  152.     Game((1, 3, 6, None, 2, 0), WHITE, (30, 34)),
  153.     Game((2, 0, 1, 3, 6), WHITE, (22, 42)),
  154.     Game((2, 0, 3, 1, 6), WHITE, (24, 40)),
  155.     Game((2, 0, 6, 3, 1), WHITE, (27, 37)),
  156.     Game((2, 1, 0, 3, 6), WHITE, (30, 34)),
  157.     Game((2, 1, 3, None, 0, None, 6), WHITE, (33, 31)),
  158.     Game((2, 1, 3, None, 6, None, 0), WHITE, (33, 31)),
  159.     Game((2, 1, 6, 3, 0), WHITE, (30, 34)),
  160.     Game((2, 3, 0, 1, 6), WHITE, (28, 36)),
  161.     Game((2, 3, 1, 0, 6), WHITE, (25, 39)),
  162.     Game((2, 3, 6, 0, 1), WHITE, (30, 34)),
  163.     Game((2, 3, 6, 1, 0), WHITE, (30, 34)),
  164.     Game((3, 2, 0, 1, 6), WHITE, (32, 32)),
  165.     Game((3, 2, 1, 0, 6), WHITE, (25, 39)),
  166.     Game((3, 2, 6, None, 0, 1), WHITE, (27, 37)),
  167.     Game((3, 2, 6, None, 1, None, 0), WHITE, (34, 30)),
  168.     Game((6, None, 0, 1, 2, 3), WHITE, (27, 37)),
  169.     Game((6, None, 0, 1, 3, 2), WHITE, (29, 35)),
  170.     Game((6, None, 1, 2, 0, None, 3), WHITE, (36, 28)),
  171.     Game((6, None, 1, 2, 3, None, 0), WHITE, (36, 28)),
  172.     Game((6, None, 1, 3, 0, None, 2), WHITE, (35, 29)),
  173.     Game((6, None, 1, 3, 2, 0), WHITE, (30, 34)),
  174.     Game((6, None, 2, 0, 1, 3), WHITE, (22, 42)),
  175.     Game((6, None, 2, 0, 3, None, 1), WHITE, (33, 31)),
  176.     Game((6, None, 2, 1, 0, 3), WHITE, (27, 37)),
  177.     Game((6, None, 2, 1, 3, None, 0), WHITE, (33, 31)),
  178.     Game((6, None, 2, 3, 0, 1), WHITE, (23, 41)),
  179.     Game((6, None, 2, 3, 1, 0), WHITE, (25, 39)),
  180.     Game((6, None, 3, 2, 0, 1), WHITE, (29, 35)),
  181.     Game((6, None, 3, 2, 1, None, 0), WHITE, (33, 31)),
  182. ]
  183.  
  184. # Дерево игры
  185. tree = TreeBuilder(Move(None, BLACK), GAMES).tree
  186. for kid in tree.children:
  187.     print(
  188.         f"Если белые сделают ход {kid.move.position}, то разница в итоговом счёте будет {kid.minimax}"
  189.     )
  190.  
  191. white_best = max(tree.children, key=lambda c: c.minimax)
  192. game = white_best.best_game()
  193. print("Лучшая игра для белых: ", game.moves, game.score)
  194.  
  195. for kid in tree.children:
  196.     game = kid.best_game()
  197.     print(
  198.         f"Если белые сделают ход {kid.move.position}, то лучшая игра будет ",
  199.         game.moves,
  200.         game.score,
  201.     )
  202.  
  203.  
  204. tree.print_tree()
  205.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement