2025-05-04

Ai01 NalMok

#날목01. 사람이 시작하면 백으로 시작하는데 누가 먼저 시작하더라도 흑으로 시작하려면 어디를 바꾸어야 할까?

import sys
import random
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QPushButton, QDialog, QVBoxLayout, QLabel
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt, QTimer

BOARD_SIZE = 19
CELL_SIZE = 30
STONE_RADIUS = 12

EMPTY = 0
BLACK = 1
WHITE = 2

# 나이트 이동 방향
NALMOK_DIRECTIONS = [(2, 1), (1, 2), (-2, 1), (-1, 2), (2, -1), (1, -2), (-2, -1), (-1, -2)]

class FirstTurnDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("누가 먼저 시작할까요?")
        self.choice = None

        layout = QVBoxLayout()
        layout.addWidget(QLabel("누가 먼저 시작할까요?"))

        btn_human = QPushButton("사람")
        btn_computer = QPushButton("컴퓨터")
        layout.addWidget(btn_human)
        layout.addWidget(btn_computer)

        btn_human.clicked.connect(self.choose_human)
        btn_computer.clicked.connect(self.choose_computer)

        self.setLayout(layout)

    def choose_human(self):
        self.choice = "human_first"
        self.accept()

    def choose_computer(self):
        self.choice = "computer_first"
        self.accept()

class NalmokGame(QWidget):
    def __init__(self, first_turn_mode):
        super().__init__()
        self.setWindowTitle("날목")
        self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE)

        self.board = [[EMPTY for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
        self.move_history = []
        self.game_over_flag = False

        self.last_black_move = None
        self.last_white_move = None

        undo_button = QPushButton("Undo", self)
        undo_button.move(10, self.height() - 40)
        undo_button.clicked.connect(self.undo_move)

        if first_turn_mode == "computer_first":
            center = BOARD_SIZE // 2
            self.place_stone(center, center, BLACK)
            self.turn = WHITE
        elif first_turn_mode == "human_first":  # 이 부분을 추가
            self.turn = WHITE
        else:
            self.turn = BLACK

        self.show()

    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        self.draw_board(qp)
        self.draw_stones(qp)
        qp.end()

    def draw_board(self, qp):
        qp.setBrush(QColor(222, 184, 135))
        qp.drawRect(0, 0, self.width(), self.height())

        qp.setPen(QPen(Qt.black, 1))
        for i in range(BOARD_SIZE):
            qp.drawLine(CELL_SIZE // 2, CELL_SIZE // 2 + i * CELL_SIZE,
                        CELL_SIZE // 2 + (BOARD_SIZE - 1) * CELL_SIZE, CELL_SIZE // 2 + i * CELL_SIZE)
            qp.drawLine(CELL_SIZE // 2 + i * CELL_SIZE, CELL_SIZE // 2,
                        CELL_SIZE // 2 + i * CELL_SIZE, CELL_SIZE // 2 + (BOARD_SIZE - 1) * CELL_SIZE)

        star_points = [(3, 3), (3, 9), (3, 15),
                       (9, 3), (9, 9), (9, 15),
                       (15, 3), (15, 9), (15, 15)]
        for x, y in star_points:
            cx = CELL_SIZE // 2 + x * CELL_SIZE
            cy = CELL_SIZE // 2 + y * CELL_SIZE
            qp.setBrush(QColor(0, 0, 0))
            qp.drawEllipse(cx - 3, cy - 3, 6, 6)

    def draw_stones(self, qp):
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] != EMPTY:
                    if self.board[y][x] == BLACK:
                        qp.setBrush(QColor(0, 0, 0))
                    else:
                        qp.setBrush(QColor(255, 255, 255))
                    qp.drawEllipse(
                        CELL_SIZE // 2 + x * CELL_SIZE - STONE_RADIUS,
                        CELL_SIZE // 2 + y * CELL_SIZE - STONE_RADIUS,
                        STONE_RADIUS * 2,
                        STONE_RADIUS * 2
                    )

        last = self.last_black_move if self.turn == WHITE else self.last_white_move
        if last:
            x, y = last
            cx = CELL_SIZE // 2 + x * CELL_SIZE
            cy = CELL_SIZE // 2 + y * CELL_SIZE
            qp.setPen(QPen(Qt.red, 2))
            qp.drawEllipse(cx - 5, cy - 5, 10, 10)

    def mousePressEvent(self, event):
        if self.game_over_flag or self.turn != WHITE:
            return

        x = int((event.x() - CELL_SIZE // 2 + CELL_SIZE / 2) // CELL_SIZE)
        y = int((event.y() - CELL_SIZE // 2 + CELL_SIZE / 2) // CELL_SIZE)

        if 0 <= x < BOARD_SIZE and 0 <= y < BOARD_SIZE and self.board[y][x] == EMPTY:
            self.place_stone(x, y, WHITE)
            if self.check_win(x, y, WHITE):
                self.update()
                self.game_over("사용자(백) 승리!")
                return

            self.turn = BLACK
            self.update()
            QTimer.singleShot(500, self.computer_move)

    def place_stone(self, x, y, stone):
        self.board[y][x] = stone
        self.move_history.append((x, y, stone))
        if stone == BLACK:
            self.last_black_move = (x, y)
        else:
            self.last_white_move = (x, y)

    def undo_move(self):
        if len(self.move_history) >= 2:
            for _ in range(2):
                x, y, stone = self.move_history.pop()
                self.board[y][x] = EMPTY
                if stone == BLACK:
                    self.last_black_move = None
                else:
                    self.last_white_move = None
            self.turn = WHITE
            self.update()

    def check_win(self, x, y, stone):
        for dx, dy in NALMOK_DIRECTIONS:
            count = 1
            for dir in [1, -1]:
                for i in range(1, 5):
                    nx, ny = x + dx * i * dir, y + dy * i * dir
                    if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == stone:
                        count += 1
                    else:
                        break
            if count >= 5:
                return True
        return False

    def should_block_nalmok(self, x, y, stone, length):
        for dx, dy in NALMOK_DIRECTIONS:
            count = 1
            blocked = 0
            for i in range(1, length):
                nx = x + dx * i
                ny = y + dy * i
                if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:
                    cell = self.board[ny][nx]
                    if cell == stone:
                        count += 1
                    elif cell == EMPTY:
                        continue
                    else:
                        blocked += 1
                        break
                else:
                    blocked += 1
                    break
            if count == length and blocked == 0:
                return True
        return False

    def computer_move(self):
        if self.game_over_flag:
            return

        # 우선순위 1: 컴퓨터 승리 수
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY and self.should_block_nalmok(x, y, BLACK, 4):
                    self.place_stone(x, y, BLACK)
                    if self.check_win(x, y, BLACK):
                        self.update()
                        self.game_over("컴퓨터(흑) 승리!")
                        return
                    self.turn = WHITE
                    self.update()
                    return

        # 우선순위 2: 상대 열린 4 차단
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY and self.should_block_nalmok(x, y, WHITE, 4):
                    self.place_stone(x, y, BLACK)
                    self.turn = WHITE
                    self.update()
                    return

        # 우선순위 3: 컴퓨터 공격 - 3 연결
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY and self.should_block_nalmok(x, y, BLACK, 3):
                    self.place_stone(x, y, BLACK)
                    self.turn = WHITE
                    self.update()
                    return

        # 우선순위 4: 상대 열린 3 차단
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY and self.should_block_nalmok(x, y, WHITE, 3):
                    self.place_stone(x, y, BLACK)
                    self.turn = WHITE
                    self.update()
                    return

        # 우선순위 5: 중앙 혹은 임의 착수
        base_x, base_y = self.last_black_move if self.last_black_move else (BOARD_SIZE // 2, BOARD_SIZE // 2)
        for dx, dy in NALMOK_DIRECTIONS:
            nx = base_x + dx
            ny = base_y + dy
            if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:
                self.place_stone(nx, ny, BLACK)
                if self.check_win(nx, ny, BLACK):
                    self.update()
                    self.game_over("컴퓨터(흑) 승리!")
                    return
                self.turn = WHITE
                self.update()
                return

    def game_over(self, message):
        self.game_over_flag = True
        QMessageBox.information(self, "게임 종료", message)
        self.close()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    dialog = FirstTurnDialog()
    if dialog.exec_() == QDialog.Accepted:
        game = NalmokGame(dialog.choice)
        sys.exit(app.exec_())

댓글 없음:

댓글 쓰기