2025-08-16

SigolHyanggi

시골향기. 수제비 먹으러 감.
왜 갔냐면? 어제 아내가 TV 봤는데 그 집 방송 나왔다고. 전현무가 맛있게 먹더라고.
우리가 간 곳은 김포 고촌.
경기 김포시 고촌읍 신곡로 114-8
방송에 나온 곳은 운양걸포점.
식당 앞에 차가 줄지어 서 있어서 들어가지 못하고 그 근처 주차하고 걸어감.
아내와 함께 한 시간 가까이 기다려서 먹음.
우거지수제비 23000원. 아내와 함께 잘 먹고.
보리밥 13000원. 집에 있는 우리 아이들에게 주려고 포장.

수제비

메뉴판



2025-08-11

Rolling Rice

롤링 라이스. 강화도 용흥궁 근처 식당

강화군 강화읍 북문길 14

롤링 세트 메뉴 18500원

단품으로 하면 롤링떡볶이 4500 김치치즈볶음밥 8500 라면 5000원

음료 1을 500원에 주는 꼴.

사진은 나중에.



사장님 건강해보이기에 나이 물으니 이 가게 12년 했다고 하시며 여러가지 운동 이야기. 나이는 안 가르쳐주심.


2025-05-10

ai06_5 NalMok

#import pdb

import sys

import random

from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QPushButton

from PyQt5.QtGui import QPainter, QColor, QPen

from PyQt5.QtCore import Qt, QTimer, QPoint


BOARD_SIZE = 19

CELL_SIZE = 40

STONE_RADIUS = CELL_SIZE>>1


EMPTY = 0

BLACK = 1

WHITE = 2

#보드 바탕색

BOARD_COLOR = QColor(222, 184, 135)

# 나이트 이동 방향

NALMOK_DIRECTIONS = [(1, 2),(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2),  (2, -1), (2, 1)]



class NalmokGame(QWidget):

    def __init__(self):

        super().__init__()

        self.setWindowTitle("날목")

        self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE +50)


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

        self.move_history = []

        self.game_over_flag = False

        self.winning_line = [] # 승리한 5개 돌의 좌표를 저장할 리스트


        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)


        # 컴퓨터가 흑으로 시작

        center = BOARD_SIZE // 2

        self.place_stone(center, center, BLACK)

        self.turn = WHITE


        self.show()


    def paintEvent(self, event):

        qp = QPainter()

        qp.begin(self)

        self.draw_board(qp)

        self.draw_stones(qp)

        self.draw_winning_line(qp) # 승리한 돌을 표시하는 함수 호출

        qp.end()


    def draw_winning_line(self, qp):

        if self.winning_line:

            qp.setPen(QPen(QColor(0, 255, 0), 3)) # 녹색으로 강조

            for x, y in self.winning_line:

                cx = STONE_RADIUS + x * CELL_SIZE

                cy = STONE_RADIUS + y * CELL_SIZE

                qp.drawEllipse(cx - STONE_RADIUS // 2, cy - STONE_RADIUS // 2, STONE_RADIUS, STONE_RADIUS)



    def draw_board(self, qp):

        qp.setBrush(BOARD_COLOR)

        qp.drawRect(0, 0, self.width(), self.height())


        qp.setPen(QPen(Qt.black, 1))

        for i in range(BOARD_SIZE):

            qp.drawLine(STONE_RADIUS, STONE_RADIUS + i * CELL_SIZE,

                            STONE_RADIUS + (BOARD_SIZE - 1) * CELL_SIZE, STONE_RADIUS + i * CELL_SIZE)

            qp.drawLine(STONE_RADIUS + i * CELL_SIZE, STONE_RADIUS,

                            STONE_RADIUS + i * CELL_SIZE, STONE_RADIUS + (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 = STONE_RADIUS + x * CELL_SIZE

            cy = STONE_RADIUS + y * CELL_SIZE

            qp.setBrush(BLACK)

            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:

                    qp.setBrush(QColor(0, 0, 0) if self.board[y][x] == BLACK else QColor(255, 255, 255))

                    qp.drawEllipse(

                        STONE_RADIUS + x * CELL_SIZE - STONE_RADIUS,

                        STONE_RADIUS + 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

#       pdb.set_trace()

        if last:

            x, y = last

            cx = STONE_RADIUS + x * CELL_SIZE

            cy = STONE_RADIUS + y * CELL_SIZE

            qp.setPen(QPen(BOARD_COLOR, 2))

            qp.drawRect(cx-1, cy-1, 3, 3)


    def mousePressEvent(self, event):

        if self.game_over_flag or self.turn != WHITE:

            return


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

        y = int((event.y() - STONE_RADIUS + 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, True): # 승리 시 winning_line 업데이트

                self.update()

                self.game_over("사용자(백) 승리!")

                return


            self.turn = BLACK

            self.update()

            QTimer.singleShot(1, 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.winning_line = [] # Undo 시 승리선 초기화

            self.update()


    def check_win(self, x, y, stone, update_winning_line=False):

        winning_stones = []

        for dx, dy in NALMOK_DIRECTIONS:

            line = [(x, y)]

            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:

                        line.append((nx, ny))

                        count += 1

                    else:

                        break

            if count >= 5:

                if update_winning_line:

                    self.winning_line = line

                return True

        return False


    def should_block_nalmok(self, x, y, stone, length):

        for dx, dy in NALMOK_DIRECTIONS:

            count = 0

            blocked = 0

            for dir in [1, -1]:

                temp_count = 0

                temp_blocked = 0

                for i in range(1, length):

                    nx = x + dx * i * dir

                    ny = y + dy * i * dir

                    if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:

                        cell = self.board[ny][nx]

                        if cell == stone:

                            temp_count += 1

                        elif cell == EMPTY:

                            continue

                        else:

                            temp_blocked += 1

                            break

                    else:

                        temp_blocked += 1

                        break

                if dir == 1:

                    count += temp_count

                    blocked += temp_blocked

                else:

                    count += temp_count

                    blocked += temp_blocked

            if count == length -1 and blocked < 2:

                # Check if placing a stone at (x, y) would complete the sequence

                placed = False

                for dir in [1, -1]:

                    nx_check = x + dx * length * dir

                    ny_check = y + dy * length * dir

                    if 0 <= nx_check < BOARD_SIZE and 0 <= ny_check < BOARD_SIZE:

                        if self.board[ny_check][nx_check] == stone:

                            continue # Already blocked on this side

                    else:

                        continue # Already blocked on this side


                    temp_board = [row[:] for row in self.board]

                    temp_board[y][x] = stone

                    win = False

                    check_count = 1

                    for check_dir in [1, -1]:

                        for i in range(1, 5):

                            cx, cy = x + dx * i * check_dir, y + dy * i * check_dir

                            if 0 <= cx < BOARD_SIZE and 0 <= cy < BOARD_SIZE and temp_board[cy][cx] == stone:

                                check_count += 1

                            else:

                                break

                    if check_count >= 5:

                        win = True


                if count == length - 1 and blocked < 2:

                    can_extend = False

                    for dir in [1, -1]:

                        nx_extend = x + dx * length * dir

                        ny_extend = y + dy * length * dir

                        if 0 <= nx_extend < BOARD_SIZE and 0 <= ny_extend < BOARD_SIZE and self.board[ny_extend][nx_extend] == EMPTY:

                            can_extend = True

                            break

                    if can_extend or blocked == 0:

                        return True

        return False


    def evaluate_board(self, stone):

        score = 0

        opponent = WHITE if stone == BLACK else BLACK

        # Evaluate score for the given stone

        for length in range(2, 5):

            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, stone, length):

                        if length == 4:

                            score += 1000

                        elif length == 3:

                            score += 100

                        elif length == 2:

                            score += 10


        # Evaluate score for the opponent to subtract potential threats

        for length in range(2, 5):

            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, opponent, length):

                        if length == 4:

                            score -= 900  # Less priority to block than to create own 4

                        elif length == 3:

                            score -= 90

                        elif length == 2:

                            score -= 9

        return score


    def computer_move(self):

        if self.game_over_flag:

            return


        # 우선순위 1: 컴퓨터 승리 수 (5개)

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    self.place_stone(x, y, BLACK)

                    if self.check_win(x, y, BLACK, True): # 승리 시 winning_line 업데이트

                        self.update()

                        self.game_over("컴퓨터(흑) 승리!")

                        return

                    self.board[y][x] = EMPTY

                    self.move_history.pop()


        # 우선순위 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, BLACK, 4):

                    self.place_stone(x, y, BLACK)

                    self.turn = WHITE

                    self.update()

                    return


        # 우선순위 3: 상대방의 즉각적인 승리 차단 (5개)

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    self.place_stone(x, y, WHITE)

                    if self.check_win(x, y, WHITE, True): # 승리 시 winning_line 업데이트

                        self.board[y][x] = EMPTY

                        self.move_history.pop()

                        self.place_stone(x, y, BLACK)

                        self.turn = WHITE

                        self.update()

                        return

                    self.board[y][x] = EMPTY

                    self.move_history.pop()


        # 우선순위 4: 상대 열린 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


        # 우선순위 5: 컴퓨터 공격 - 열린 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


        # 우선순위 6: 상대 열린 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



        # 우선순위 8: 평가 기반 최적 수

        best_score = -float('inf')

        best_move = None

        center = BOARD_SIZE // 2

        search_range = 5  # 중앙 근처 탐색으로 연산량 감소

        for y in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):

            for x in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):

                if self.board[y][x] == EMPTY:

                    self.board[y][x] = BLACK

                    score = self.evaluate_board(BLACK) - self.evaluate_board(WHITE)

                    self.board[y][x] = EMPTY

                    if score > best_score:

                        best_score = score

                        best_move = (x, y)

        if best_move:

            x, y = best_move

            self.place_stone(x, y, BLACK)

            self.turn = WHITE

            self.update()

            return


        # 대체 수: 최근 돌 근처 나이트 이동 (더 안전한 수)

        possible_moves = []

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == BLACK:

                    for dx, dy in NALMOK_DIRECTIONS:

                        nx, ny = x + dx, y + dy

                        if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:

                            possible_moves.append((nx, ny))


        if possible_moves:

            move = random.choice(possible_moves)

            self.place_stone(move[0], move[1], BLACK)

            self.turn = WHITE

            self.update()

            return


        # 정말 둘 곳이 없다면 랜덤으로

        empty_cells = []

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    empty_cells.append((x, y))

        if empty_cells:

            move = random.choice(empty_cells)

            self.place_stone(move[0], move[1], BLACK)

            self.turn = WHITE

            self.update()

            return



    def game_over(self, message):

        self.game_over_flag = True

        self.update() # 마지막 돌을 포함한 승리선을 표시하기 위해 update 호출

        QMessageBox.information(self, "게임 종료", message)

        self.close()


if __name__ == "__main__":

    app = QApplication(sys.argv)

    game = NalmokGame()

    sys.exit(app.exec_())

ai06NalMok

#import pdb
import sys
import random
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QPushButton
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt, QTimer, QPoint
BOARD_SIZE = 19
CELL_SIZE = 40
RADIUS = CELL_SIZE>>1
EMPTY = 0
BLACK = 1
WHITE = 2
#보드 바탕색
BOARD_COLOR = QColor(222, 184, 135)
# 나이트 이동 방향
NALMOK_DIRECTIONS = [(1, 2), (-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (2, -1), (2, 1)]
class NalmokGame(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("날목")
        self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE + 50)
        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)
        # 컴퓨터가 흑으로 시작
        center = BOARD_SIZE // 2
        self.place_stone(center, center, BLACK)
        self.turn = WHITE
        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(BOARD_COLOR)
        qp.drawRect(0, 0, self.width(), self.height())
        qp.setPen(QPen(Qt.black, 1))
        for i in range(BOARD_SIZE):
            qp.drawLine(RADIUS, RADIUS + i * CELL_SIZE,
                            RADIUS + (BOARD_SIZE - 1) * CELL_SIZE, RADIUS + i * CELL_SIZE)
            qp.drawLine(RADIUS + i * CELL_SIZE, RADIUS,
                            RADIUS + i * CELL_SIZE, RADIUS + (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 = RADIUS + x * CELL_SIZE
            cy = RADIUS + y * CELL_SIZE
            qp.setBrush(BLACK)
            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:
                    qp.setBrush(QColor(0, 0, 0) if self.board[y][x] == BLACK else QColor(255, 255, 255))
                    qp.drawEllipse(
                        RADIUS + x * CELL_SIZE - RADIUS,
                        RADIUS + y * CELL_SIZE - RADIUS,
                        RADIUS * 2,
                        RADIUS * 2
                    )
        last = self.last_black_move if self.turn == WHITE else self.last_white_move
#       pdb.set_trace()
        if last:
            x, y = last
            cx = RADIUS + x * CELL_SIZE
            cy = RADIUS + y * CELL_SIZE
            qp.setPen(QPen(BOARD_COLOR, 2))
            qp.drawRect(cx-1, cy-1, 3, 3)
    def mousePressEvent(self, event):
        if self.game_over_flag or self.turn != WHITE:
            return
        x = int((event.x() - RADIUS + CELL_SIZE / 2) // CELL_SIZE)
        y = int((event.y() - RADIUS + 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(1, 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 = 0
            blocked = 0
            for dir in [1, -1]:
                temp_count = 0
                temp_blocked = 0
                for i in range(1, length):
                    nx = x + dx * i * dir
                    ny = y + dy * i * dir
                    if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:
                        cell = self.board[ny][nx]
                        if cell == stone:
                            temp_count += 1
                        elif cell == EMPTY:
                            continue
                        else:
                            temp_blocked += 1
                            break
                    else:
                        temp_blocked += 1
                        break
                if dir == 1:
                    count += temp_count
                    blocked += temp_blocked
                else:
                    count += temp_count
                    blocked += temp_blocked
            if count == length -1 and blocked < 2:
                # Check if placing a stone at (x, y) would complete the sequence
                placed = False
                for dir in [1, -1]:
                    nx_check = x + dx * length * dir
                    ny_check = y + dy * length * dir
                    if 0 <= nx_check < BOARD_SIZE and 0 <= ny_check < BOARD_SIZE:
                        if self.board[ny_check][nx_check] == stone:
                            continue # Already blocked on this side
                    else:
                        continue # Already blocked on this side
                    temp_board = [row[:] for row in self.board]
                    temp_board[y][x] = stone
                    win = False
                    check_count = 1
                    for check_dir in [1, -1]:
                        for i in range(1, 5):
                            cx, cy = x + dx * i * check_dir, y + dy * i * check_dir
                            if 0 <= cx < BOARD_SIZE and 0 <= cy < BOARD_SIZE and temp_board[cy][cx] == stone:
                                check_count += 1
                            else:
                                break
                    if check_count >= 5:
                        win = True
                if count == length - 1 and blocked < 2:
                    can_extend = False
                    for dir in [1, -1]:
                        nx_extend = x + dx * length * dir
                        ny_extend = y + dy * length * dir
                        if 0 <= nx_extend < BOARD_SIZE and 0 <= ny_extend < BOARD_SIZE and self.board[ny_extend][nx_extend] == EMPTY:
                            can_extend = True
                            break
                    if can_extend or blocked == 0:
                        return True
        return False
    def evaluate_board(self, stone):
        score = 0
        opponent = WHITE if stone == BLACK else BLACK
        # Evaluate score for the given stone
        for length in range(2, 5):
            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, stone, length):
                        if length == 4:
                            score += 1000
                        elif length == 3:
                            score += 100
                        elif length == 2:
                            score += 10
        # Evaluate score for the opponent to subtract potential threats
        for length in range(2, 5):
            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, opponent, length):
                        if length == 4:
                            score -= 900  # Less priority to block than to create own 4
                        elif length == 3:
                            score -= 90
                        elif length == 2:
                            score -= 9
        return score
    def computer_move(self):
        if self.game_over_flag:
            return
        # 우선순위 1: 컴퓨터 승리 수 (5개)
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY:
                    self.place_stone(x, y, BLACK)
                    if self.check_win(x, y, BLACK):
                        self.update()
                        self.game_over("컴퓨터(흑) 승리!")
                        return
                    self.board[y][x] = EMPTY
                    self.move_history.pop()
        # 우선순위 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, BLACK, 4):
                    self.place_stone(x, y, BLACK)
                    self.turn = WHITE
                    self.update()
                    return
        # 우선순위 3: 상대방의 즉각적인 승리 차단 (5개)
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY:
                    self.place_stone(x, y, WHITE)
                    if self.check_win(x, y, WHITE):
                        self.board[y][x] = EMPTY
                        self.move_history.pop()
                        self.place_stone(x, y, BLACK)
                        self.turn = WHITE
                        self.update()
                        return
                    self.board[y][x] = EMPTY
                    self.move_history.pop()
        # 우선순위 4: 상대 열린 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
        # 우선순위 5: 컴퓨터 공격 - 열린 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
        # 우선순위 6: 상대 열린 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
        # 우선순위 8: 평가 기반 최적 수
        best_score = -float('inf')
        best_move = None
        center = BOARD_SIZE // 2
        search_range = 5  # 중앙 근처 탐색으로 연산량 감소
        for y in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):
            for x in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):
                if self.board[y][x] == EMPTY:
                    self.board[y][x] = BLACK
                    score = self.evaluate_board(BLACK) - self.evaluate_board(WHITE)
                    self.board[y][x] = EMPTY
                    if score > best_score:
                        best_score = score
                        best_move = (x, y)
        if best_move:
            x, y = best_move
            self.place_stone(x, y, BLACK)
            self.turn = WHITE
            self.update()
            return
        # 대체 수: 최근 돌 근처 나이트 이동 (더 안전한 수)
        possible_moves = []
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == BLACK:
                    for dx, dy in NALMOK_DIRECTIONS:
                        nx, ny = x + dx, y + dy
                        if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:
                            possible_moves.append((nx, ny))
        if possible_moves:
            move = random.choice(possible_moves)
            self.place_stone(move[0], move[1], BLACK)
            self.turn = WHITE
            self.update()
            return
        # 정말 둘 곳이 없다면 랜덤으로
        empty_cells = []
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY:
                    empty_cells.append((x, y))
        if empty_cells:
            move = random.choice(empty_cells)
            self.place_stone(move[0], move[1], BLACK)
            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)
    game = NalmokGame()
    sys.exit(app.exec_())

2025-05-07

ai05 NalMok

 #import pdb

import sys

import random

from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QPushButton

from PyQt5.QtGui import QPainter, QColor, QPen

from PyQt5.QtCore import Qt, QTimer, QPoint


BOARD_SIZE = 19

CELL_SIZE = 20

STONE_RADIUS = CELL_SIZE>>1


EMPTY = 0

BLACK = 1

WHITE = 2

#보드 바탕색

BOARD_COLOR = QColor(222, 184, 135)

# 나이트 이동 방향

NALMOK_DIRECTIONS = [(1, 2),(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2),  (2, -1), (2, 1)]



class NalmokGame(QWidget):

    def __init__(self):

        super().__init__()

        self.setWindowTitle("날목")

        self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE +50)


        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)


        # 컴퓨터가 흑으로 시작

        center = BOARD_SIZE // 2

        self.place_stone(center, center, BLACK)

        self.turn = WHITE


        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(BOARD_COLOR)

        qp.drawRect(0, 0, self.width(), self.height())


        qp.setPen(QPen(Qt.black, 1))

        for i in range(BOARD_SIZE):

            qp.drawLine(STONE_RADIUS, STONE_RADIUS + i * CELL_SIZE,

                            STONE_RADIUS + (BOARD_SIZE - 1) * CELL_SIZE, STONE_RADIUS + i * CELL_SIZE)

            qp.drawLine(STONE_RADIUS + i * CELL_SIZE, STONE_RADIUS,

                            STONE_RADIUS + i * CELL_SIZE, STONE_RADIUS + (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 = STONE_RADIUS + x * CELL_SIZE

            cy = STONE_RADIUS + y * CELL_SIZE

            qp.setBrush(BLACK)

            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:

                    qp.setBrush(QColor(0, 0, 0) if self.board[y][x] == BLACK else QColor(255, 255, 255))

                    qp.drawEllipse(

                        STONE_RADIUS + x * CELL_SIZE - STONE_RADIUS,

                        STONE_RADIUS + 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

#       pdb.set_trace()

        if last:

            x, y = last

            cx = STONE_RADIUS + x * CELL_SIZE

            cy = STONE_RADIUS + y * CELL_SIZE

            qp.setPen(QPen(BOARD_COLOR, 2))

            qp.drawRect(cx-1, cy-1, 3, 3)


    def mousePressEvent(self, event):

        if self.game_over_flag or self.turn != WHITE:

            return


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

        y = int((event.y() - STONE_RADIUS + 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(1, 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 = 0

            blocked = 0

            for dir in [1, -1]:

                temp_count = 0

                temp_blocked = 0

                for i in range(1, length):

                    nx = x + dx * i * dir

                    ny = y + dy * i * dir

                    if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:

                        cell = self.board[ny][nx]

                        if cell == stone:

                            temp_count += 1

                        elif cell == EMPTY:

                            continue

                        else:

                            temp_blocked += 1

                            break

                    else:

                        temp_blocked += 1

                        break

                if dir == 1:

                    count += temp_count

                    blocked += temp_blocked

                else:

                    count += temp_count

                    blocked += temp_blocked

            if count == length -1 and blocked < 2:

                # Check if placing a stone at (x, y) would complete the sequence

                placed = False

                for dir in [1, -1]:

                    nx_check = x + dx * length * dir

                    ny_check = y + dy * length * dir

                    if 0 <= nx_check < BOARD_SIZE and 0 <= ny_check < BOARD_SIZE:

                        if self.board[ny_check][nx_check] == stone:

                            continue # Already blocked on this side

                    else:

                        continue # Already blocked on this side


                    temp_board = [row[:] for row in self.board]

                    temp_board[y][x] = stone

                    win = False

                    check_count = 1

                    for check_dir in [1, -1]:

                        for i in range(1, 5):

                            cx, cy = x + dx * i * check_dir, y + dy * i * check_dir

                            if 0 <= cx < BOARD_SIZE and 0 <= cy < BOARD_SIZE and temp_board[cy][cx] == stone:

                                check_count += 1

                            else:

                                break

                    if check_count >= 5:

                        win = True


                if count == length - 1 and blocked < 2:

                    can_extend = False

                    for dir in [1, -1]:

                        nx_extend = x + dx * length * dir

                        ny_extend = y + dy * length * dir

                        if 0 <= nx_extend < BOARD_SIZE and 0 <= ny_extend < BOARD_SIZE and self.board[ny_extend][nx_extend] == EMPTY:

                            can_extend = True

                            break

                    if can_extend or blocked == 0:

                        return True

        return False


    def evaluate_board(self, stone):

        score = 0

        opponent = WHITE if stone == BLACK else BLACK

        # Evaluate score for the given stone

        for length in range(2, 5):

            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, stone, length):

                        if length == 4:

                            score += 1000

                        elif length == 3:

                            score += 100

                        elif length == 2:

                            score += 10


        # Evaluate score for the opponent to subtract potential threats

        for length in range(2, 5):

            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, opponent, length):

                        if length == 4:

                            score -= 900  # Less priority to block than to create own 4

                        elif length == 3:

                            score -= 90

                        elif length == 2:

                            score -= 9

        return score


    def computer_move(self):

        if self.game_over_flag:

            return


        # 우선순위 1: 컴퓨터 승리 수 (5개)

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    self.place_stone(x, y, BLACK)

                    if self.check_win(x, y, BLACK):

                        self.update()

                        self.game_over("컴퓨터(흑) 승리!")

                        return

                    self.board[y][x] = EMPTY

                    self.move_history.pop()


        # 우선순위 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, BLACK, 4):

                    self.place_stone(x, y, BLACK)

                    self.turn = WHITE

                    self.update()

                    return


        # 우선순위 3: 상대방의 즉각적인 승리 차단 (5개)

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    self.place_stone(x, y, WHITE)

                    if self.check_win(x, y, WHITE):

                        self.board[y][x] = EMPTY

                        self.move_history.pop()

                        self.place_stone(x, y, BLACK)

                        self.turn = WHITE

                        self.update()

                        return

                    self.board[y][x] = EMPTY

                    self.move_history.pop()


        # 우선순위 4: 상대 열린 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


        # 우선순위 5: 컴퓨터 공격 - 열린 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


        # 우선순위 6: 상대 열린 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



        # 우선순위 8: 평가 기반 최적 수

        best_score = -float('inf')

        best_move = None

        center = BOARD_SIZE // 2

        search_range = 5  # 중앙 근처 탐색으로 연산량 감소

        for y in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):

            for x in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):

                if self.board[y][x] == EMPTY:

                    self.board[y][x] = BLACK

                    score = self.evaluate_board(BLACK) - self.evaluate_board(WHITE)

                    self.board[y][x] = EMPTY

                    if score > best_score:

                        best_score = score

                        best_move = (x, y)

        if best_move:

            x, y = best_move

            self.place_stone(x, y, BLACK)

            self.turn = WHITE

            self.update()

            return


        # 대체 수: 최근 돌 근처 나이트 이동 (더 안전한 수)

        possible_moves = []

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == BLACK:

                    for dx, dy in NALMOK_DIRECTIONS:

                        nx, ny = x + dx, y + dy

                        if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:

                            possible_moves.append((nx, ny))


        if possible_moves:

            move = random.choice(possible_moves)

            self.place_stone(move[0], move[1], BLACK)

            self.turn = WHITE

            self.update()

            return


        # 정말 둘 곳이 없다면 랜덤으로

        empty_cells = []

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    empty_cells.append((x, y))

        if empty_cells:

            move = random.choice(empty_cells)

            self.place_stone(move[0], move[1], BLACK)

            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)

    game = NalmokGame()

    sys.exit(app.exec_())


ma02a ai nal mok

#import pdb

import sys

import random

from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QPushButton

from PyQt5.QtGui import QPainter, QColor, QPen

from PyQt5.QtCore import Qt, QTimer, QPoint


BOARD_SIZE = 19

CELL_SIZE = 40

STONE_RADIUS = CELL_SIZE>>1


EMPTY = 0

BLACK = 1

WHITE = 2

#보드 바탕색

BOARD_COLOR = QColor(222, 184, 135)

# 나이트 이동 방향

NALMOK_DIRECTIONS = [(1, 2),(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2),  (2, -1), (2, 1)]



class NalmokGame(QWidget):

    def __init__(self):

        super().__init__()

        self.setWindowTitle("날목")

        self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE +50)


        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 = DraggableButton("Undo", self)

        undo_button.move(10, self.height() - 40)

        undo_button.clicked.connect(self.undo_move)


        # 컴퓨터가 흑으로 시작

        center = BOARD_SIZE // 2

        self.place_stone(center, center, BLACK)

        self.turn = WHITE


        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(BOARD_COLOR)

        qp.drawRect(0, 0, self.width(), self.height())


        qp.setPen(QPen(Qt.black, 1))

        for i in range(BOARD_SIZE):

            qp.drawLine(STONE_RADIUS, STONE_RADIUS + i * CELL_SIZE,

                            STONE_RADIUS + (BOARD_SIZE - 1) * CELL_SIZE, STONE_RADIUS + i * CELL_SIZE)

            qp.drawLine(STONE_RADIUS + i * CELL_SIZE, STONE_RADIUS,

                            STONE_RADIUS + i * CELL_SIZE, STONE_RADIUS + (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 = STONE_RADIUS + x * CELL_SIZE

            cy = STONE_RADIUS + y * CELL_SIZE

            qp.setBrush(BLACK)

            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:

                    qp.setBrush(QColor(0, 0, 0) if self.board[y][x] == BLACK else QColor(255, 255, 255))

                    qp.drawEllipse(

                        STONE_RADIUS + x * CELL_SIZE - STONE_RADIUS,

                        STONE_RADIUS + 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

#       pdb.set_trace()

        if last:

            x, y = last

            cx = STONE_RADIUS + x * CELL_SIZE

            cy = STONE_RADIUS + y * CELL_SIZE

            qp.setPen(QPen(BOARD_COLOR, 2))

            qp.drawRect(cx-1, cy-1, 3, 3)


    def mousePressEvent(self, event):

        if self.game_over_flag or self.turn != WHITE:

            return


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

        y = int((event.y() - STONE_RADIUS + 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(1, 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 = 0

            blocked = 0

            for dir in [1, -1]:

                temp_count = 0

                temp_blocked = 0

                for i in range(1, length):

                    nx = x + dx * i * dir

                    ny = y + dy * i * dir

                    if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:

                        cell = self.board[ny][nx]

                        if cell == stone:

                            temp_count += 1

                        elif cell == EMPTY:

                            continue

                        else:

                            temp_blocked += 1

                            break

                    else:

                        temp_blocked += 1

                        break

                if dir == 1:

                    count += temp_count

                    blocked += temp_blocked

                else:

                    count += temp_count

                    blocked += temp_blocked

            if count == length -1 and blocked < 2:

                # Check if placing a stone at (x, y) would complete the sequence

                placed = False

                for dir in [1, -1]:

                    nx_check = x + dx * length * dir

                    ny_check = y + dy * length * dir

                    if 0 <= nx_check < BOARD_SIZE and 0 <= ny_check < BOARD_SIZE:

                        if self.board[ny_check][nx_check] == stone:

                            continue # Already blocked on this side

                    else:

                        continue # Already blocked on this side


                    temp_board = [row[:] for row in self.board]

                    temp_board[y][x] = stone

                    win = False

                    check_count = 1

                    for check_dir in [1, -1]:

                        for i in range(1, 5):

                            cx, cy = x + dx * i * check_dir, y + dy * i * check_dir

                            if 0 <= cx < BOARD_SIZE and 0 <= cy < BOARD_SIZE and temp_board[cy][cx] == stone:

                                check_count += 1

                            else:

                                break

                    if check_count >= 5:

                        win = True


                if count == length - 1 and blocked < 2:

                    can_extend = False

                    for dir in [1, -1]:

                        nx_extend = x + dx * length * dir

                        ny_extend = y + dy * length * dir

                        if 0 <= nx_extend < BOARD_SIZE and 0 <= ny_extend < BOARD_SIZE and self.board[ny_extend][nx_extend] == EMPTY:

                            can_extend = True

                            break

                    if can_extend or blocked == 0:

                        return True

        return False


    def check_special_attack_pattern(self):

        base_patterns = [(-3, -6), (-1, -2), (1, 2), (3, 6)]

        all_patterns = []

        for dx, dy in base_patterns:

            all_patterns.append((dx, dy))

            all_patterns.append((-dx, dy))

            all_patterns.append((dx, -dy))

            all_patterns.append((-dx, -dy))

            all_patterns.append((dy, dx))

            all_patterns.append((-dy, dx))

            all_patterns.append((dy, -dx))

            all_patterns.append((-dy, -dx))

        all_patterns = list(set(all_patterns))


        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] != EMPTY:

                    continue

                for pattern in [base_patterns, [(dy, dx) for dx, dy in base_patterns]]:

                    found = True

                    for dx, dy in pattern:

                        nx, ny = x + dx, y + dy

                        if not (0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == WHITE):

                            found = False

                            break

                    if found:

                        return x, y

        return None


    def evaluate_board(self, stone):

        score = 0

        opponent = WHITE if stone == BLACK else BLACK

        # Evaluate score for the given stone

        for length in range(2, 5):

            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, stone, length):

                        if length == 4:

                            score += 1000

                        elif length == 3:

                            score += 100

                        elif length == 2:

                            score += 10


        # Evaluate score for the opponent to subtract potential threats

        for length in range(2, 5):

            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, opponent, length):

                        if length == 4:

                            score -= 900  # Less priority to block than to create own 4

                        elif length == 3:

                            score -= 90

                        elif length == 2:

                            score -= 9

        return score


    def computer_move(self):

        if self.game_over_flag:

            return


        # 우선순위 1: 컴퓨터 승리 수 (5개)

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    self.place_stone(x, y, BLACK)

                    if self.check_win(x, y, BLACK):

                        self.update()

                        self.game_over("컴퓨터(흑) 승리!")

                        return

                    self.board[y][x] = EMPTY

                    self.move_history.pop()


        # 우선순위 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, BLACK, 4):

                    self.place_stone(x, y, BLACK)

                    self.turn = WHITE

                    self.update()

                    return


        # 우선순위 3: 상대방의 즉각적인 승리 차단 (5개)

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    self.place_stone(x, y, WHITE)

                    if self.check_win(x, y, WHITE):

                        self.board[y][x] = EMPTY

                        self.move_history.pop()

                        self.place_stone(x, y, BLACK)

                        self.turn = WHITE

                        self.update()

                        return

                    self.board[y][x] = EMPTY

                    self.move_history.pop()


        # 우선순위 4: 상대 열린 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


        # 우선순위 5: 컴퓨터 공격 - 열린 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


        # 우선순위 6: 상대 열린 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


        # 우선순위 7: 특별 공격 패턴 차단

        result = self.check_special_attack_pattern()

        if result:

            x, y = result

            self.place_stone(x, y, BLACK)

            self.turn = WHITE

            self.update()

            return



        # 우선순위 8: 평가 기반 최적 수

        best_score = -float('inf')

        best_move = None

        center = BOARD_SIZE // 2

        search_range = 5  # 중앙 근처 탐색으로 연산량 감소

        for y in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):

            for x in range(max(0, center - search_range), min(BOARD_SIZE, center + search_range)):

                if self.board[y][x] == EMPTY:

                    self.board[y][x] = BLACK

                    score = self.evaluate_board(BLACK) - self.evaluate_board(WHITE)

                    self.board[y][x] = EMPTY

                    if score > best_score:

                        best_score = score

                        best_move = (x, y)

        if best_move:

            x, y = best_move

            self.place_stone(x, y, BLACK)

            self.turn = WHITE

            self.update()

            return


        # 대체 수: 최근 돌 근처 나이트 이동 (더 안전한 수)

        possible_moves = []

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == BLACK:

                    for dx, dy in NALMOK_DIRECTIONS:

                        nx, ny = x + dx, y + dy

                        if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:

                            possible_moves.append((nx, ny))


        if possible_moves:

            move = random.choice(possible_moves)

            self.place_stone(move[0], move[1], BLACK)

            self.turn = WHITE

            self.update()

            return


        # 정말 둘 곳이 없다면 랜덤으로

        empty_cells = []

        for y in range(BOARD_SIZE):

            for x in range(BOARD_SIZE):

                if self.board[y][x] == EMPTY:

                    empty_cells.append((x, y))

        if empty_cells:

            move = random.choice(empty_cells)

            self.place_stone(move[0], move[1], BLACK)

            self.turn = WHITE

            self.update()

            return



    def game_over(self, message):

        self.game_over_flag = True

        QMessageBox.information(self, "게임 종료", message)

        self.close()


class DraggableButton(QPushButton):

    def __init__(self, title, parent):

        super().__init__(title, parent)

        self.setMouseTracking(True)

        self.dragging = False

        self.offset = QPoint()


    def mousePressEvent(self, event):

        if event.button() == Qt.LeftButton:

            self.dragging = True

            self.offset = event.pos()

        super().mousePressEvent(event)


    def mouseMoveEvent(self, event):

        if self.dragging:

            new_pos = self.mapToParent(event.pos() - self.offset)

            self.move(new_pos)

        super().mouseMoveEvent(event)


    def mouseReleaseEvent(self, event):

        if event.button() == Qt.LeftButton:

            self.dragging = False

        super().mouseReleaseEvent(event)


if __name__ == "__main__":

    app = QApplication(sys.argv)

    game = NalmokGame()

    sys.exit(app.exec_())