Пример #1
0
class Game():
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.history = History(self)
        self.animations: set[Animation] = set()
        self.deck = self.create_deck()
        shuffle(self.deck)
        self.setup_stacks()
        self.deal()
        self.paused = False
        self.time = 0

    # region Commands
    def create_deck(self):
        return deque(
            Card(self.app, suit, symbol) for symbol in Symbol for suit in Suit)

    def setup_stacks(self):
        self.foundations = tuple(
            FoundationStack(self.app, (i * constants.CARD_WIDTH_MARGIN +
                                       constants.BIG_MARGIN,
                                       constants.BIG_MARGIN))
            for i in range(4))
        self.waste = WasteStack(self.app,
                                (4 * constants.CARD_WIDTH_MARGIN +
                                 constants.BIG_MARGIN, constants.BIG_MARGIN))
        self.stock = StockStack(self.app,
                                (6 * constants.CARD_WIDTH_MARGIN +
                                 constants.BIG_MARGIN, constants.BIG_MARGIN))
        self.tableaus = tuple(
            TableauStack(self.app, (i * constants.CARD_WIDTH_MARGIN +
                                    constants.BIG_MARGIN,
                                    constants.CARD_HEIGHT_MARGIN +
                                    constants.BIG_MARGIN)) for i in range(7))
        self.drag = DragStack(self.app, (0, 0))
        self.clickable_stacks: tuple[
            Stack] = self.foundations + self.tableaus + (self.stock,
                                                         self.waste)
        self.stacks: tuple[Stack] = (self.stock, self.waste) + tuple(
            reversed(self.tableaus)) + tuple(reversed(
                self.foundations)) + (self.drag, )

    def deal(self):
        self.stock.cards = self.deck
        self.stock.reset_pos()
        moves = []
        k = 1
        for i, stack in enumerate(self.tableaus):
            for j in range(i + 1):
                m = MoveMove(self.stock, stack, 1)
                if i == j:
                    m = ConcurrentMoves((m, FlipMove(self.stock.cards[-k])))
                moves.append(m)
                k += 1

        self.animations.add(SequentialMoves(moves).redo())

    def undo(self):
        if self.paused:
            return

        self.cancel_animations()
        self.history.undo()

    def redo(self):
        if self.paused:
            return

        self.cancel_animations()
        self.history.redo()

    def deal_card(self):
        if self.paused:
            return

        self.cancel_animations()
        if self.stock.is_empty:
            self.history.add_move(
                ConcurrentMoves(
                    tuple(FlipMove(c) for c in self.waste.cards) +
                    (MoveMove(self.waste, self.stock, self.waste.size, True), )
                ))
        else:
            self.history.add_move(
                ConcurrentMoves((FlipMove(self.stock.card_on_top),
                                 MoveMove(self.stock, self.waste, 1))))

    def _collect_card_move(self, stack: Stack,
                           foundation: FoundationStack) -> Move:
        move = MoveMove(stack, foundation, 1)
        if len(stack.cards) > 1 and stack.cards[-2].flipped:
            move = ConcurrentMoves((FlipMove(stack.cards[-2]), move))
        return move

    def collect_card(self, stack: Stack):
        if stack.is_empty:
            return

        for f in self.foundations:
            if f.can_enter(stack.card_on_top, 1):
                self.cancel_animations()
                self.history.add_move(self._collect_card_move(stack, f))
                return

    def collect_all(self):
        if self.paused:
            return

        self.cancel_animations()
        moves = []
        b = True
        while b:
            b = False
            for stack in self.tableaus + (self.waste, ):
                for f in self.foundations:
                    if not stack.is_empty and f.can_enter(
                            stack.card_on_top, 1):
                        move = self._collect_card_move(stack, f)
                        moves.append(move)
                        move.redo()
                        b = True
                        continue
        if moves:
            m = SequentialMoves(moves)
            list(m.undo().animations)
            self.history.add_move(m)

    def cancel_animations(self):
        if self.paused:
            return

        for animation in self.animations:
            animation.cancel()

    def pause(self):
        self.paused = not self.paused

    # endregion

    def draw(self, screen):
        if not self.paused:
            for animation in set(self.animations):
                animation.tick(self.app.clock.get_time())
                if animation.done:
                    self.animations.remove(animation)

            if not self.animations and all(f.size == 13
                                           for f in self.foundations):
                print("WIN")
                self.app.game_win()
            else:
                self.time += self.app.clock.get_time()

        for stack in self.stacks:
            stack.draw(screen)

    # region Mouse
    def clicked_stack(self, pos):
        for s in self.clickable_stacks:
            if s.rect.collidepoint(pos):
                return s

    def on_mouseclick_l(self, pos):
        if self.paused:
            return

        if self.clicked_stack(pos) == self.stock:
            self.deal_card()

    def on_mouseclick_m(self, pos):
        if self.paused:
            return

        s = self.clicked_stack(pos)
        if s and s.get_cards_to_drag(pos) == 1:
            self.collect_card(s)
        else:
            self.collect_all()

    def on_mousedrag_l(self, pos):
        if self.paused:
            return

        self.drag.mouse_pos = pos

    def on_mousedragbegin_l(self, pos):
        if self.paused:
            return

        for s in self.stacks:
            c = s.get_cards_to_drag(pos)
            if c:
                self.cancel_animations()
                self.drag.cards += list(s.cards)[s.size - c:]
                self.drag.source_stack = s
                self.drag.offset = (pos[0] - self.drag.cards[0].pos[0],
                                    pos[1] - self.drag.cards[0].pos[1])

    def on_mousedragend_l(self, pos):
        if self.paused:
            return

        if self.drag.is_empty:
            return

        for s in self.stacks:
            if s.rect.collidepoint(pos) and s.can_enter(
                    self.drag.card_on_bottom, self.drag.size):
                move = MoveMove(self.drag.source_stack, s, self.drag.size)
                if self.drag.source_stack in self.tableaus and self.drag.source_stack.size > self.drag.size and self.drag.source_stack.cards[
                        -self.drag.size - 1].flipped:
                    move = ConcurrentMoves((FlipMove(
                        self.drag.source_stack.cards[-self.drag.size - 1]),
                                            move))
                self.history.add_move(move)
                break

        self.drag.cards.clear()
        self.animations.add(self.drag.source_stack.animate())