import sys
import random
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
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 NalmokGame(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("날목 (Nalmok)")
self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE)
self.board = [[EMPTY for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
self.turn = BLACK # 컴퓨터가 흑으로 먼저
center = BOARD_SIZE // 2
self.board[center][center] = BLACK # 첫 수 중앙에 둠
self.last_move = (center, center)
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(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
)
# 마지막 수 표시
if self.last_move:
x, y = self.last_move
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.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.board[y][x] = WHITE
self.last_move = (x, y)
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 find_user_threat(self, count_required):
best_priority = None
for y in range(BOARD_SIZE):
for x in range(BOARD_SIZE):
if self.board[y][x] == WHITE:
for dx, dy in NALMOK_DIRECTIONS:
stones = [(x, y)]
for i in range(1, count_required):
nx, ny = x + dx * i, y + dy * i
if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == WHITE:
stones.append((nx, ny))
else:
break
if len(stones) == count_required:
bx, by = x - dx, y - dy
ex, ey = x + dx * count_required, y + dy * count_required
b_open = 0 <= bx < BOARD_SIZE and 0 <= by < BOARD_SIZE and self.board[by][bx] == EMPTY
e_open = 0 <= ex < BOARD_SIZE and 0 <= ey < BOARD_SIZE and self.board[ey][ex] == EMPTY
if b_open and e_open:
return (bx, by) # 열린 삼 우선
elif b_open and best_priority is None:
best_priority = (bx, by)
elif e_open and best_priority is None:
best_priority = (ex, ey)
return best_priority
def computer_move(self):
for threat_level in [4, 3, 2]:
threat_move = self.find_user_threat(threat_level)
if threat_move:
x, y = threat_move
self.board[y][x] = BLACK
self.last_move = (x, y)
if self.check_win(x, y, BLACK):
self.update()
self.game_over("컴퓨터(흑) 승리!")
return
self.turn = WHITE
self.update()
return
# 공격 우선 로직으로 나머지 수를 두기
best_score = float('-inf')
best_move = None
for y in range(BOARD_SIZE):
for x in range(BOARD_SIZE):
if self.board[y][x] == EMPTY:
self.board[y][x] = BLACK
if self.check_win(x, y, BLACK):
self.board[y][x] = EMPTY
best_move = (x, y)
break
score = self.evaluate_board(BLACK) - 2 * self.evaluate_board(WHITE)
self.board[y][x] = EMPTY
if score > best_score:
best_score = score
best_move = (x, y)
if best_move and best_score >= 1000:
break
if best_move:
x, y = best_move
self.board[y][x] = BLACK
self.last_move = (x, y)
if self.check_win(x, y, BLACK):
self.update()
self.game_over("컴퓨터(흑) 승리!")
return
self.turn = WHITE
self.update()
def evaluate_board(self, stone):
score = 0
for y in range(BOARD_SIZE):
for x in range(BOARD_SIZE):
if self.board[y][x] == stone:
for dx, dy in NALMOK_DIRECTIONS:
count = 1
for i in range(1, 5):
nx, ny = x + dx * i, y + dy * i
if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == stone:
count += 1
else:
break
if count == 5:
score += 100000
elif count == 4:
score += 10000
elif count == 3:
score += 500
elif count == 2:
score += 100
return score
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 game_over(self, message):
QMessageBox.information(self, "게임 종료", message)
self.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
game = NalmokGame()
sys.exit(app.exec_())
댓글 없음:
댓글 쓰기