суббота, 11 октября 2025 г.

Игра в дурня на python. Deep seek

 Отличная идея! Минимакс с альфа-бета отсечением значительно улучшит силу игры компьютера. Вот улучшенная версия с минимаксом:


```python

import random

from typing import List, Tuple, Optional, Dict

from collections import defaultdict

import math


class Card:

    def __init__(self, rank: str, suit: str):

        self.rank = rank

        self.suit = suit

        self.value = self._get_value()

    

    def _get_value(self) -> int:

        values = {'6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 

                 'J': 11, 'Q': 12, 'K': 13, 'A': 14}

        return values[self.rank]

    

    def __str__(self):

        return f"{self.rank}{self.suit}"

    

    def __repr__(self):

        return str(self)

    

    def __eq__(self, other):

        return self.rank == other.rank and self.suit == other.suit

    

    def __hash__(self):

        return hash((self.rank, self.suit))


class Deck:

    def __init__(self):

        ranks = ['6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']

        suits = ['♥', '♦', '♣', '♠']

        self.cards = [Card(rank, suit) for suit in suits for rank in ranks]

        random.shuffle(self.cards)

    

    def deal(self, num_cards: int) -> List[Card]:

        return [self.cards.pop() for _ in range(num_cards)]


class GameState:

    """Класс для представления состояния игры"""

    def __init__(self, players_hands: List[List[Card]], table: List[Card], 

                 trump: str, attacker: int, deck_size: int):

        self.players_hands = [hand.copy() for hand in players_hands]

        self.table = table.copy()

        self.trump = trump

        self.attacker = attacker

        self.defender = 1 - attacker

        self.deck_size = deck_size

    

    def copy(self):

        return GameState(self.players_hands, self.table, self.trump, 

                        self.attacker, self.deck_size)


class FoolGameWithMinimax:

    def __init__(self):

        self.deck = Deck()

        self.players = [[], []]  # [0] - компьютер, [1] - игрок

        self.trump = self.deck.cards[-1].suit

        self.table = []

        self.attacker = 1  # кто ходит первым

        self.defender = 0  # кто отбивается

        self.max_depth = 3  # глубина поиска минимакса

        

    def deal_initial_cards(self):

        for i in range(2):

            self.players[i] = self.deck.deal(6)

            self.sort_hand(self.players[i])

    

    def sort_hand(self, hand: List[Card]):

        hand.sort(key=lambda card: (card.suit != self.trump, card.value))

    

    def get_game_state(self) -> GameState:

        return GameState(self.players, self.table, self.trump, 

                        self.attacker, len(self.deck.cards))

    

    def evaluate_state(self, state: GameState, player: int) -> float:

        """Оценка состояния игры для указанного игрока"""

        if not state.players_hands[player]:

            return 1000  # выигрыш

        

        if not state.players_hands[1 - player]:

            return -1000  # проигрыш

        

        score = 0

        

        # Оценка по картам в руке

        for i, hand in enumerate(state.players_hands):

            player_factor = 1 if i == player else -1

            hand_value = sum(card.value for card in hand)

            

            # Бонус за козыри

            trump_count = sum(1 for card in hand if card.suit == state.trump)

            trump_bonus = trump_count * 5

            

            # Штраф за много высоких карт (их сложнее сбросить)

            high_cards_penalty = sum(max(0, card.value - 10) for card in hand) * 0.5

            

            score += player_factor * (hand_value + trump_bonus - high_cards_penalty)

        

        # Оценка стола

        if state.table:

            if state.attacker == player:

                # Мы атакуем - хорошо иметь много карт на столе

                score += len(state.table) * 2

            else:

                # Мы защищаемся - плохо иметь много карт на столе

                score -= len(state.table) * 3

        

        # Бонус за карты в колоде

        score += state.deck_size * 0.1 * (1 if player == state.attacker else -1)

        

        return score

    

    def minimax(self, state: GameState, depth: int, alpha: float, beta: float, 

                maximizing_player: bool) -> Tuple[float, Optional[Tuple[Card, Optional[Card]]]]:

        """Минимакс с альфа-бета отсечением"""

        if depth == 0 or self.is_terminal_state(state):

            return self.evaluate_state(state, 0), None  # 0 - компьютер

        

        current_player = state.attacker if not state.table else state.defender

        is_computer_turn = (current_player == 0)

        

        if (maximizing_player and is_computer_turn) or (not maximizing_player and not is_computer_turn):

            return self._max_value(state, depth, alpha, beta, maximizing_player)

        else:

            return self._min_value(state, depth, alpha, beta, maximizing_player)

    

    def _max_value(self, state: GameState, depth: int, alpha: float, beta: float, 

                  maximizing_player: bool) -> Tuple[float, Optional[Tuple]]:

        best_value = -math.inf

        best_move = None

        

        for move in self.get_possible_moves(state):

            new_state = self.apply_move(state, move)

            value, _ = self.minimax(new_state, depth - 1, alpha, beta, not maximizing_player)

            

            if value > best_value:

                best_value = value

                best_move = move

            

            alpha = max(alpha, best_value)

            if beta <= alpha:

                break

        

        return best_value, best_move

    

    def _min_value(self, state: GameState, depth: int, alpha: float, beta: float, 

                  maximizing_player: bool) -> Tuple[float, Optional[Tuple]]:

        best_value = math.inf

        best_move = None

        

        for move in self.get_possible_moves(state):

            new_state = self.apply_move(state, move)

            value, _ = self.minimax(new_state, depth - 1, alpha, beta, not maximizing_player)

            

            if value < best_value:

                best_value = value

                best_move = move

            

            beta = min(beta, best_value)

            if beta <= alpha:

                break

        

        return best_value, best_move

    

    def get_possible_moves(self, state: GameState) -> List[Tuple[Card, Optional[Card]]]:

        """Возвращает все возможные ходы для текущего состояния"""

        moves = []

        current_player = state.attacker if not state.table else state.defender

        

        if not state.table:  # Атака

            # Можно пойти любой картой

            for card in state.players_hands[current_player]:

                moves.append((card, None))

        else:  # Защита или дополнительная атака

            if current_player == state.defender:  # Защита

                attacking_card = state.table[-1]

                for card in state.players_hands[current_player]:

                    if self._can_beat(attacking_card, card, state.trump):

                        moves.append((card, None))

                

                # Также можно взять карты

                moves.append((None, None))

            else:  # Дополнительная атака

                table_ranks = {card.rank for card in state.table}

                for card in state.players_hands[current_player]:

                    if card.rank in table_ranks:

                        moves.append((card, None))

        

        return moves

    

    def apply_move(self, state: GameState, move: Tuple[Card, Optional[Card]]) -> GameState:

        """Применяет ход к состоянию и возвращает новое состояние"""

        new_state = state.copy()

        card, _ = move

        

        if card is None:  # Взять карты

            new_state.players_hands[new_state.defender].extend(new_state.table)

            new_state.table.clear()

            new_state.attacker, new_state.defender = new_state.defender, new_state.attacker

        else:  # Сходить картой

            # Убираем карту из руки игрока

            new_state.players_hands[new_state.attacker if not new_state.table else new_state.defender].remove(card)

            new_state.table.append(card)

            

            # Если защита успешна и карты закончились - очищаем стол

            if new_state.table and len(new_state.table) % 2 == 0:

                if not self.can_continue_attack(new_state):

                    new_state.table.clear()

                    new_state.attacker, new_state.defender = new_state.defender, new_state.attacker

        

        return new_state

    

    def can_continue_attack(self, state: GameState) -> bool:

        """Можно ли продолжать атаку"""

        if not state.table or len(state.players_hands[state.attacker]) == 0:

            return False

        

        table_ranks = {card.rank for card in state.table}

        return any(card.rank in table_ranks for card in state.players_hands[state.attacker])

    

    def is_terminal_state(self, state: GameState) -> bool:

        """Проверка конечного состояния"""

        return (not state.players_hands[0] or not state.players_hands[1] or 

                (not state.deck_size and not state.table and 

                 (not state.players_hands[state.attacker] or not state.players_hands[state.defender])))

    

    def _can_beat(self, attacking: Card, defending: Card, trump: str) -> bool:

        """Может ли защищающая карта побить атакующую"""

        if defending.suit == trump and attacking.suit != trump:

            return True

        elif defending.suit == attacking.suit and defending.value > attacking.value:

            return True

        return False

    

    def find_best_move_minimax(self) -> Tuple[Optional[Card], Optional[Card]]:

        """Находит лучший ход с помощью минимакса"""

        state = self.get_game_state()

        _, best_move = self.minimax(state, self.max_depth, -math.inf, math.inf, True)

        return best_move or (None, None)

    

    def make_computer_move(self):

        """Компьютер делает ход на основе минимакса"""

        print("Компьютер думает...")

        card_to_play, _ = self.find_best_move_minimax()

        

        if card_to_play:

            self.players[0].remove(card_to_play)

            self.table.append(card_to_play)

            print(f"Компьютер ходит: {card_to_play}")

            return True

        else:

            print("Компьютер берет карты")

            self.players[0].extend(self.table)

            self.table.clear()

            self.sort_hand(self.players[0])

            return False

    

    def make_player_move(self, card_index: int) -> bool:

        """Игрок делает ход"""

        if 0 <= card_index < len(self.players[1]):

            card = self.players[1][card_index]

            self.players[1].remove(card)

            self.table.append(card)

            print(f"Вы ходите: {card}")

            return True

        return False

    

    def player_defend(self, card_index: int) -> bool:

        """Игрок защищается"""

        if not self.table:

            return False

            

        attacking_card = self.table[-1]

        if 0 <= card_index < len(self.players[1]):

            defending_card = self.players[1][card_index]

            

            if self._can_beat(attacking_card, defending_card, self.trump):

                self.players[1].remove(defending_card)

                self.table.append(defending_card)

                print(f"Вы отбиваете: {defending_card}")

                return True

        

        print("Нельзя побить этой картой!")

        return False

    

    def replenish_hands(self):

        """Добираем карты из колоды"""

        for i in range(2):

            while len(self.players[i]) < 6 and self.deck.cards:

                new_card = self.deck.deal(1)[0]

                self.players[i].append(new_card)

            self.sort_hand(self.players[i])

    

    def display_game_state(self):

        """Показывает текущее состояние игры"""

        print(f"\n{'='*50}")

        print(f"Козырь: {self.trump}")

        print(f"Карт в колоде: {len(self.deck.cards)}")

        print(f"\nСтол: {self.table}")

        print(f"\nВаши карты:")

        for i, card in enumerate(self.players[1]):

            print(f"{i}: {card}")

        print(f"\nКарты компьютера: {len(self.players[0])} карт")


def main():

    game = FoolGameWithMinimax()

    game.deal_initial_cards()

    

    print("Добро пожаловать в игру 'Дурак' с минимакс-ИИ!")

    print("Компьютер использует алгоритм минимакс для поиска лучшего хода.")

    

    while True:

        game.display_game_state()

        

        if game.attacker == 1:  # ход игрока

            if not game.table:  # атака

                print("\nВаш ход (атака). Выберите карту:")

                try:

                    choice = int(input("Введите номер карты: "))

                    if game.make_player_move(choice):

                        game.attacker, game.defender = game.defender, game.attacker

                except (ValueError, IndexError):

                    print("Неверный ввод!")

                    continue

            else:  # дополнительная атака

                print("\nХотите подкинуть карту? (y/n/t - взять)")

                action = input().lower()

                if action == 'y':

                    print("Выберите карту для подкидывания:")

                    try:

                        choice = int(input("Введите номер карты: "))

                        if game.make_player_move(choice):

                            pass

                        else:

                            continue

                    except (ValueError, IndexError):

                        print("Неверный ввод!")

                        continue

                elif action == 't':

                    print("Вы берете карты")

                    game.players[1].extend(game.table)

                    game.table.clear()

                    game.sort_hand(game.players[1])

                    game.replenish_hands()

                    game.attacker = 0

                    game.defender = 1

                else:

                    # Завершаем раунд

                    game.table.clear()

                    game.replenish_hands()

                    game.attacker = 0

                    game.defender = 1

        

        else:  # ход компьютера

            if not game.table:  # атака

                print("\nХод компьютера (атака):")

                if game.make_computer_move():

                    game.attacker, game.defender = game.defender, game.attacker

                else:

                    game.table.clear()

                    game.replenish_hands()

            else:  # защита компьютера

                print("\nХод компьютера (защита):")

                if game.make_computer_move():

                    pass

                else:

                    print("Компьютер берет карты")

                    game.players[0].extend(game.table)

                    game.table.clear()

                    game.sort_hand(game.players[0])

                    game.replenish_hands()

                    game.attacker = 1

                    game.defender = 0

        

        # Проверка конца игры

        if not game.players[0] and not game.players[1]:

            print("Ничья!")

            break

        elif not game.players[0]:

            print("Компьютер проиграл! Вы выиграли!")

            break

        elif not game.players[1]:

            print("Вы проиграли! Компьютер выиграл!")

            break


if __name__ == "__main__":

    main()

```


**Основные улучшения с минимаксом:**


1. **Класс GameState** - изолированное состояние игры для поиска

2. **Минимакс с альфа-бета отсечением** - просчет ходов на 3 шага вперед

3. **Функция оценки** учитывает:

   - Количество и ценность карт

   - Козыри

   - Позицию на столе

   - Карты в колоде


4. **Генерация всех возможных ходов** для каждого состояния

5. **Применение ходов** к копиям состояния для симуляции


**Преимущества минимакса:**

- Компьютер думает на несколько ходов вперед

- Учитывает ответные ходы игрока

- Выбирает стратегически выгодные позиции

- Избегает ловушек и использует слабости игрока


**Настройки:**

- `max_depth = 3` - можно увеличить для более сильной игры (но дольше расчет)

- Функцию оценки можно дополнить более сложными эвристиками


Теперь компьютер играет на качественно новом уровне!

Комментариев нет:

Отправить комментарий