게임 이름:날목 (python3 으로 개발)
import sys
import math # 중앙 거리를 계산하기 위해 추가
from PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox
from PyQt6.QtGui import QPainter, QPen, QColor, QBrush
from PyQt6.QtCore import Qt, QPoint
class FixedNalGomoku(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("세벌의 날목 (Fixed AI)")
self.setFixedSize(640, 640)
self.grid_size = 30
self.margin = 50
self.board = [[0] * 19 for _ in range(19)]
self.current_player = 1 # 흑(컴퓨터) 선공
self.directions = [(2, 1), (1, 2), (-1, 2), (-2, 1)]
self.win_stones = []
# 첫 수는 무조건 중앙(천원)
self.board[9][9] = 1
self.current_player = 2
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.fillRect(self.rect(), QColor("#E6C38A"))
# 바둑판 선 및 화점
painter.setPen(QPen(Qt.GlobalColor.black, 1))
for i in range(19):
painter.drawLine(self.margin, self.margin + i * self.grid_size, self.margin + 18 * self.grid_size, self.margin + i * self.grid_size)
painter.drawLine(self.margin + i * self.grid_size, self.margin, self.margin + i * self.grid_size, self.margin + 18 * self.grid_size)
painter.setBrush(QBrush(Qt.GlobalColor.black))
for r in [3, 9, 15]:
for c in [3, 9, 15]:
painter.drawEllipse(QPoint(self.margin + c * self.grid_size, self.margin + r * self.grid_size), 3, 3)
# 돌 그리기
for y in range(19):
for x in range(19):
if self.board[y][x] != 0:
center = QPoint(self.margin + x * self.grid_size, self.margin + y * self.grid_size)
color = Qt.GlobalColor.black if self.board[y][x] == 1 else Qt.GlobalColor.white
painter.setBrush(QBrush(color))
painter.setPen(QPen(QColor("#CCCCCC") if self.board[y][x] == 2 else Qt.GlobalColor.black, 1))
painter.drawEllipse(center, 13, 13)
if (y, x) in self.win_stones:
painter.setBrush(QBrush(QColor("lime")))
painter.setPen(Qt.PenStyle.NoPen)
painter.drawEllipse(center, 4, 4)
def mousePressEvent(self, event):
if self.current_player != 2 or self.win_stones: return
x = round((event.position().x() - self.margin) / self.grid_size)
y = round((event.position().y() - self.margin) / self.grid_size)
if 0 <= x < 19 and 0 <= y < 19 and self.board[y][x] == 0:
self.board[y][x] = 2
win, stones = self.check_win(y, x, 2)
if win:
self.win_stones = stones
self.update()
self.end_game("세벌 님 승리!")
else:
self.current_player = 1
self.ai_move()
self.update()
def ai_move(self):
best_score = -1
best_moves = [] # 동점일 경우를 위해 리스트로 관리
for y in range(19):
for x in range(19):
if self.board[y][x] == 0:
score = self.evaluate_move(y, x)
if score > best_score:
best_score = score
best_moves = [(y, x)]
elif score == best_score:
best_moves.append((y, x))
if best_moves:
# 동점일 경우 중앙에서 가장 가까운 곳 선택
best_move = min(best_moves, key=lambda pos: math.sqrt((pos[0]-9)**2 + (pos[1]-9)**2))
y, x = best_move
self.board[y][x] = 1
win, stones = self.check_win(y, x, 1)
if win:
self.win_stones = stones
self.update()
self.end_game("컴퓨터 승리!")
self.current_player = 2
self.update()
def evaluate_move(self, y, x):
score = 0
for dy, dx in self.directions:
# 공격 가중치와 방어 가중치를 전략적으로 배분
score += self.get_line_score(y, x, 1, dy, dx) * 1.5 # 공격 중시
score += self.get_line_score(y, x, 2, dy, dx) * 1.2 # 방어 필수
# 중앙 선호도 점수 (0~10점 사이의 아주 작은 보너스)
dist_from_center = math.sqrt((y-9)**2 + (x-9)**2)
score += (15 - dist_from_center)
return score
def get_line_score(self, y, x, player, dy, dx):
count = 1
open_ends = 0 # 양끝이 열려있는지 확인 (3-3 등 파악용)
for direction in [1, -1]:
ty, tx = y + dy*direction, x + dx*direction
while 0 <= ty < 19 and 0 <= tx < 19 and self.board[ty][tx] == player:
count += 1
ty, tx = ty + dy*direction, tx + dx*direction
if 0 <= ty < 19 and 0 <= tx < 19 and self.board[ty][tx] == 0:
open_ends += 1
# 가중치 대폭 상향
if count >= 5: return 100000
if count == 4: return 10000 if open_ends > 0 else 5000
if count == 3: return 2000 if open_ends == 2 else 500
if count == 2: return 100
return 0
def check_win(self, y, x, player):
for dy, dx in self.directions:
stones = [(y, x)]
for direction in [1, -1]:
ty, tx = y + dy*direction, x + dx*direction
while 0 <= ty < 19 and 0 <= tx < 19 and self.board[ty][tx] == player:
stones.append((ty, tx))
ty, tx = ty + dy*direction, tx + dx*direction
if len(stones) >= 5:
return True, stones[:5]
return False, []
def end_game(self, message):
QMessageBox.information(self, "게임 종료", message)
self.board = [[0] * 19 for _ in range(19)]
self.win_stones = []
self.board[9][9] = 1 # 리셋 후에도 흑(AI)이 중앙 선공
self.current_player = 2
if __name__ == "__main__":
app = QApplication(sys.argv)
window = FixedNalGomoku()
window.show()
sys.exit(app.exec())
댓글 없음:
댓글 쓰기