NalMok

세벌의 이미지

오목의 변형 게임 날목을 구상하고 PyQt로 만들었어요. 더 개선할 부분 있으면 알려주셔요.

#NalMok V13
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 = 30
STONE_RADIUS = 12
 
EMPTY = 0
BLACK = 1
WHITE = 2
 
# 나이트 이동 방향
NALMOK_DIRECTIONS = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)]
 
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)
 
class NalmokGame(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("날목")
        self.setFixedSize(BOARD_SIZE * CELL_SIZE, BOARD_SIZE * CELL_SIZE)
 
        self.board = [[EMPTY for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
        self.move_history = []
        self.game_over_flag = False
 
        self.last_black_move = None
        self.last_white_move = None
 
        undo_button = 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(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:
                    qp.setBrush(QColor(0, 0, 0) if self.board[y][x] == BLACK else QColor(255, 255, 255))
                    qp.drawEllipse(
                        CELL_SIZE // 2 + x * CELL_SIZE - STONE_RADIUS,
                        CELL_SIZE // 2 + y * CELL_SIZE - STONE_RADIUS,
                        STONE_RADIUS * 2,
                        STONE_RADIUS * 2
                    )
 
        last = self.last_black_move if self.turn == WHITE else self.last_white_move
        if last:
            x, y = last
            cx = CELL_SIZE // 2 + x * CELL_SIZE
            cy = CELL_SIZE // 2 + y * CELL_SIZE
            qp.setPen(QPen(Qt.red, 2))
            qp.drawEllipse(cx - 5, cy - 5, 10, 10)
 
    def mousePressEvent(self, event):
        if self.game_over_flag or self.turn != WHITE:
            return
 
        x = int((event.x() - CELL_SIZE // 2 + CELL_SIZE / 2) // CELL_SIZE)
        y = int((event.y() - CELL_SIZE // 2 + CELL_SIZE / 2) // CELL_SIZE)
 
        if 0 <= x < BOARD_SIZE and 0 <= y < BOARD_SIZE and self.board[y][x] == EMPTY:
            self.place_stone(x, y, WHITE)
            if self.check_win(x, y, WHITE):
                self.update()
                self.game_over("사용자(백) 승리!")
                return
 
            self.turn = BLACK
            self.update()
            QTimer.singleShot(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 = 1
            blocked = 0
            for i in range(1, length):
                nx = x + dx * i
                ny = y + dy * i
                if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:
                    cell = self.board[ny][nx]
                    if cell == stone:
                        count += 1
                    elif cell == EMPTY:
                        continue
                    else:
                        blocked += 1
                        break
                else:
                    blocked += 1
                    break
            if count == length and blocked == 0:
                return True
        return False
 
    def 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
        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
                        blocked = 0
                        for i in range(1, 5):
                            nx, ny = x + dx * i, y + dy * i
                            if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:
                                if self.board[ny][nx] == stone:
                                    count += 1
                                elif self.board[ny][nx] != EMPTY:
                                    blocked += 1
                                    break
                            else:
                                blocked += 1
                                break
                        if blocked == 0:  # 열린 패턴
                            if count == 4:
                                score += 100
                            elif count == 3:
                                score += 50
                            elif count == 2:
                                score += 10
        return score
 
    def computer_move(self):
        if self.game_over_flag:
            return
 
        # 우선순위 1: 컴퓨터 승리 수
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY:
                    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: 상대 승리 차단
        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()
 
        # 우선순위 3: 특별 공격 패턴 차단
        result = self.check_special_attack_pattern()
        if result:
            x, y = result
            self.place_stone(x, y, BLACK)
            self.turn = WHITE
            self.update()
            return
 
        # 우선순위 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: 평가 기반 최적 수
        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
 
        # 대체 수: 최근 돌 근처 나이트 이동
        base_x, base_y = self.last_black_move if self.last_black_move else (center, center)
        for dx, dy in NALMOK_DIRECTIONS:
            nx = base_x + dx
            ny = base_y + dy
            if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:
                self.place_stone(nx, ny, BLACK)
                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 = 20
STONE_RADIUS = 9
 
EMPTY = 0
BLACK = 1
WHITE = 2
 
# 나이트 이동 방향
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(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 - 2, cy - 2, 4, 4)
 
    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(
                        CELL_SIZE // 2 + x * CELL_SIZE - STONE_RADIUS,
                        CELL_SIZE // 2 + y * CELL_SIZE - STONE_RADIUS,
                        STONE_RADIUS * 2,
                        STONE_RADIUS * 2
                    )
 
        last = self.last_black_move if self.turn == WHITE else self.last_white_move
#        pdb.set_trace()
        if last:
            x, y = last
            cx = CELL_SIZE // 2 + x * CELL_SIZE
            cy = CELL_SIZE // 2 + y * CELL_SIZE
            qp.setPen(QPen(Qt.green, 2))
            qp.drawRect(cx - 1, cy - 1, 2, 2)
 
    def mousePressEvent(self, event):
        if self.game_over_flag or self.turn != WHITE:
            return
 
        x = int((event.x() - CELL_SIZE // 2 + CELL_SIZE / 2) // CELL_SIZE)
        y = int((event.y() - CELL_SIZE // 2 + CELL_SIZE / 2) // CELL_SIZE)
 
        if 0 <= x < BOARD_SIZE and 0 <= y < BOARD_SIZE and self.board[y][x] == EMPTY:
            self.place_stone(x, y, WHITE)
            if self.check_win(x, y, WHITE):
                self.update()
                self.game_over("사용자(백) 승리!")
                return
 
            self.turn = BLACK
            self.update()
            QTimer.singleShot(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 = 1
            blocked = 0
            for i in range(1, length):
                nx = x + dx * i
                ny = y + dy * i
                if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:
                    cell = self.board[ny][nx]
                    if cell == stone:
                        count += 1
                    elif cell == EMPTY:
                        continue
                    else:
                        blocked += 1
                        break
                else:
                    blocked += 1
                    break
            if count == length and blocked == 0:
                return True
        return False
 
    def 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
        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
                        blocked = 0
                        for i in range(1, 5):
                            nx, ny = x + dx * i, y + dy * i
                            if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE:
                                if self.board[ny][nx] == stone:
                                    count += 1
                                elif self.board[ny][nx] != EMPTY:
                                    blocked += 1
                                    break
                            else:
                                blocked += 1
                                break
                        if blocked == 0:  # 열린 패턴
                            if count == 4:
                                score += 100
                            elif count == 3:
                                score += 50
                            elif count == 2:
                                score += 10
        return score
 
    def computer_move(self):
        if self.game_over_flag:
            return
 
        # 우선순위 1: 컴퓨터 승리 수
        for y in range(BOARD_SIZE):
            for x in range(BOARD_SIZE):
                if self.board[y][x] == EMPTY:
                    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: 상대 승리 차단
        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
###
        # 우선순위 3: 특별 공격 패턴 차단
        result = self.check_special_attack_pattern()
        if result:
            x, y = result
            self.place_stone(x, y, BLACK)
            self.turn = WHITE
            self.update()
            return
 
###
 
        # 우선순위 7: 평가 기반 최적 수
        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
 
        # 대체 수: 최근 돌 근처 나이트 이동
        base_x, base_y = self.last_black_move if self.last_black_move else (center, center)
        for dx, dy in NALMOK_DIRECTIONS:
            nx = base_x + dx
            ny = base_y + dy
            if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE and self.board[ny][nx] == EMPTY:
                self.place_stone(nx, ny, BLACK)
                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_())

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.