경기 김포시 고촌읍 신곡로 114-8
방송에 나온 곳은 운양걸포점.
롤링 라이스. 강화도 용흥궁 근처 식당
강화군 강화읍 북문길 14
롤링 세트 메뉴 18500원
단품으로 하면 롤링떡볶이 4500 김치치즈볶음밥 8500 라면 5000원
음료 1을 500원에 주는 꼴.
사진은 나중에.
사장님 건강해보이기에 나이 물으니 이 가게 12년 했다고 하시며 여러가지 운동 이야기. 나이는 안 가르쳐주심.
#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_())
#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_())
#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_())