![]() |
벌초1 |
2025-10-05
Before choosuk
Solve fbterm errors
2025-10-02
memo uim dvorak hangul issue
Refresh keyboard map at each switch into Hangul mode
Global settings 탭: "Specify default IM engine" 활성화하고, 기본 엔진을 "byeoru"로 설정.
Byeoru 탭으로 이동:
"Korean keyboard layout"을 "3beol" (또는 "strict3final" 등 사용 중인 레이아웃)로 확인.
중요 옵션: "Refresh keyboard map at each switch into Hangul mode"를 활성화하세요. (한영 전환 시마다 키맵을 새로 고침하여 Dvorak 레이아웃을 반영합니다.)
"Use romaja input" 옵션을 활성화해 보세요. (로마자 기반 입력으로 전환하면 위치 매핑 문제가 사라집니다. Dvorak 문자를 로마자로 입력해 한글로 변환. 예: "annyeong" → "안녕". 한글 자판 학습 중이라면 임시로 유용.)
아직 안 해 봄.
byeoru hangul 3beol with Dvorak issue.
현재 상황. 벼루에서 한글 입력시 드보락과 함께 사용하면 한글 배열이 제대로 안 나옴. 그 문제 해결하려고 시도 중 망함.
같은 문제가 이미 github 에 보고되었다. 결론은 뭐지? https://github.com/uim/uim/issues/33 closed 되었는디? 나는 왜 아직도 같은 문제로 헤매고 있을까? 답은 뭘까?
사실 세벌식 만의 문제가 아니고 Dvorak 자판 쓰면 두벌식 사용자에게도 같은 문제 있을 듯.
아직은 미해결. gemini 설명대로 따라 하다가 uim-pref-gtk 죽는 상황 됨. 어디를 고쳐야 될지 몰라서 환경 설정 초기화
uim-pref-gtk 환경 설정 초기화 방법: .uim.d/ 디렉토리를 다 지우고 uim-pref-gtk 실행하면 기본 파일 자동 생성 됨. 다시 시도해 볼것 아래 내용
~/.uim.d/customs/custom-byeoru.scm
파일에 아래 두 줄 추가
(define byeoru-x-keyboard-layout "us")
(set! default-byeoru-x-keyboard-layout byeoru-x-keyboard-layout)
아래 내용 확인
(define byeoru-refresh-xkb-map-at-switch-on? #f) ; <-- 이 라인을 찾아 #f 로 설정
~/.uim.d/customs/custom-direct.scm
파일에
(define direct-x-keyboard-layout "dvorak")
(set! default-direct-x-keyboard-layout direct-x-keyboard-layout)
custom-byeoru-keys1.scm 파일 내용? 아래 내용 있는지 볼 것.
(define byeoru-on-key '("<Alt>Right")) ; 한글 켜기 (Right Alt)
(define byeoru-on-key? (make-key-predicate '("<Alt>Right")))
(define byeoru-latin-key '("<Alt>Right")) ; 로마자 켜기 (Right Alt)
(define byeoru-latin-key? (make-key-predicate '("<Alt>Right")))
(define byeoru-conversion-key '("F9"))
(define byeoru-conversion-key? (make-key-predicate '("F9")))
; ... 나머지 generic 키들은 그대로 둡니다.
custom-global-keys1.scm 파일 내용?
2025-10-01
sudo Without entering the password
특정 명령어를 암호 입력 없이 실행하기 예시.
터미널에서 'sudo visudo' 명령을 실행합니다.
2. 파일의 가장 마지막에 다음 두 줄을 추가합니다.(여기서 YOUR_USERNAME 부분을 실제 사용자 이름으로 변경해야 합니다.)
이 설정은 YOUR_USERNAME이 비밀번호 없이 loadkeys us와 loadkeys dvorak를
루트 권한으로 실행할 수 있도록 허용합니다.
YOUR_USERNAME ALL=(root) NOPASSWD: /usr/bin/loadkeys us
[input] can't change kernel keymap table, all shortcuts will NOT work, see SECURITY NOTES section of man page for solution.
[input] can't change kernel keymap table, all shortcuts will NOT work, see SECURITY NOTES section of man page for solution.
에러 날 때 여러 가지 경우가 있는데, fbterm에서 이런 에러가 난다면
sudo setcap 'cap_sys_tty_config+ep' /usr/bin/fbterm
2025-09-30
My .fbtermrc
2026 trend korea horse power
2025.09.29. 19:30 교보문고 전미영 발표
![]() |
2026 trend |
![]() |
HORSE POWER |
2025-09-28
fbterm input hangul success
fbterm 실행하면 한글이 잘 나온다. 그런데 입력은? 어렵다. 불가능하다는 얘긴 아님.
uim 설치해서 uim으로 설정. 환경 설정하는 건 GUI 도움 받음.
LANG=en_US.UTF-8 uim-pref-gtk 에서 설정하면 됨.
한영 전환 Ctrl-space 그런데 한글이 안 나옴. Shift-space 한영전환 안 됨 이거 저거 시도하다가 Ctrl-Space 로 한영 전환 성공. 그런데 한글 배열이 이상하게 나옴. 내가 영어 드보락 쓰는데 이것과 세벌식이 얽히면 이런 문제가. 이 설정 관련 시도는 앞으로 계속할 예정.
일단 콘솔에서 한글과 영어 다 나오게 하는 건 성공.
fbterm -- uim-fep -u byeoru
이 명령으로 콘솔에서 한글 입력과 출력 성공.
한글이 이상하게 나오는 건 아래 글 참고해서 해결할 수 있을 듯.
https://sebuls.blogspot.com/2025/09/fcitx5-configtool.html
2025-09-27
When login: Not shown
이게 버그인지 아닌지는 모르겠는데 콘솔에서 Ctrl-D 여러번 치다 보면 login: 화면도 안 나오고 커서만 깜빡일 때가 있다.
아래와 같이 해주면 된다 tty1의 숫자를 2, 3, 4 등으로 바꾸면 해당 가상 콘솔에 login: 보여준다.
sudo systemctl restart getty@tty1.service
2025-09-26
fbterm give up. retry
2025-09-24
fcitx5-configtool Dvorak and 3beolsik
![]() |
Dvorak 자판과 한글 자판 |
2025-09-21
nabi REMOVED from testing
2025-09-20
ddtp
2025-09-14
xfce4-kbdleds work in debian 13
Debian 12에서 잘 되던 게 Debian 13 으로 올린 다음 안 된다.
NumLock 상태를 보여주는 플러그인.
여러 시행착오 끝에 해결 했는데 나중에 보니 답이 README.md에 있었네.
그게 왜 안 보였던 걸까?
https://github.com/oco2000/xfce4-kbdleds-plugin/blob/master/README.md
여기서
./autogen.sh
./configure --prefix=/usr
sudo make install
이것만 보고 따라 하다 왜 안 되지? 하고 여기 저기 헤매다가 바로 아래 줄에 있는 걸 못 봤네요.
./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu
이 한 줄을 무심히 넘겼다가 수많은 시행착오의 반복.
--libdir=/usr/lib/x86_64-linux-gnu 이 한 줄 안 쓴 것 때문에 설치 다 된 플러그인이 화면에 안 나타남.
헤매면서 이것 저것 얻은 것도 많긴 하다.
lks-indicator
Debian 12에서 잘 쓰던게 13으로 올리니 안 되는 게 있다.
NumLock 상태를 보여주는 LED가 키보드에 없어서 프로그램 하나 설치해서 쓰던 게 있었는데... 그 프로그램에 13에서 안 된다.
대안 프로그램을 찾았다. lks-indicator
그런데 이 프로그램은 NumLock 켜지면 빨간색 꺼지면 녹색을 표시해준다.
내 맘에 안 들어서 켜지면 녹색, 꺼지면 빨간색으로 되게 살짝 바꿈.
바꾼 부분에 Sebul Change 라고 코멘트.
소스 코드를 여기에 그대로 붙이니 이상하게 나오네.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###########################################################
# Author: Serg Kolo <1047481448@qq.com>
# Date: July 16, 2012
# Purpose: Simple indicator of Caps, Num, and Scroll Lock
# keys for Ubuntu
#
# Written for: http://askubuntu.com/q/796985/295286
# Tested on: Ubuntu 16.04 LTS
# Color changed by Sebul See below. Tested on Debian 13. Sep 14, 2025
###########################################################
#
# Licensed under The MIT License (MIT).
# See included LICENSE file or the notice below.
#
# Copyright © 2016 Sergiy Kolodyazhnyy <1047481448@qq.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the 'Software'), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import gi
gi.require_version('AyatanaAppIndicator3', '0.1')
from gi.repository import GLib as glib
from gi.repository import AyatanaAppIndicator3 as appindicator
from gi.repository import Gtk as gtk
import os
import subprocess
import argparse
class LockKeyStatusIndicator(object):
def __init__(self, show_all=False, ignore_keys=None, monochrome=False):
self.app = appindicator.Indicator.new('LKS', '',
appindicator.IndicatorCategory.APPLICATION_STATUS)
self.app.set_status(appindicator.IndicatorStatus.ACTIVE)
self.monochrome = monochrome
self.show_all = show_all
self.app_menu = gtk.Menu()
self.quit_app = gtk.MenuItem('Quit')
self.quit_app.connect('activate', self.quit)
self.quit_app.show()
self.app_menu.append(self.quit_app)
if ignore_keys is None:
self.ignore_keys = []
else:
self.ignore_keys = ignore_keys
self.app.set_menu(self.app_menu)
self.app_path = os.path.dirname(os.path.realpath(__file__))
self.icon_path = self.app_path
if self.app_path == '/usr/bin':
self.icon_path = '/usr/share/lks-indicator/'
else:
self.icon_path = self.app_path
self.red_icon = os.path.join(self.icon_path, 'red.png')
self.green_icon = os.path.join(self.icon_path, 'green.png')
self.monochrome_icon = os.path.join(self.icon_path, 'lks-icon-monochrome.png')
self.update_label()
def run(self):
try:
gtk.main()
except KeyboardInterrupt:
pass
def quit(self, data=None):
gtk.main_quit()
def run_cmd(self, cmdlist):
try:
stdout = subprocess.check_output(cmdlist)
except subprocess.CalledProcessError:
pass
else:
if stdout is not None:
return stdout.decode('utf-8').rstrip('\n')
def key_status(self):
label = ''
status = []
keys = {
'3' : 'C',
'7' : 'N',
'11' : 'S'
}
for line in self.run_cmd(['xset', 'q']).split('\n') :
if 'Caps Lock:' in line:
status = line.split()
for index in 3, 7, 11:
if keys[str(index)] in self.ignore_keys:
pass
elif status[index] == 'on':
label += ' [' + keys[str(index)] + '] '
elif self.show_all:
label += keys[str(index)]
return label
def update_label(self):
label_text = self.key_status()
if not self.monochrome:
if '[' in label_text:
self.app.set_icon(self.green_icon) # Sebul change
else:
self.app.set_icon(self.red_icon) # Sebul change
else:
self.app.set_icon(self.monochrome_icon)
label_text = label_text.replace('[C]',u'\u24B8')
label_text = label_text.replace('[N]',u'\u24C3')
label_text = label_text.replace('[S]',u'\u24C8')
self.app.set_label(label_text, '')
glib.timeout_add_seconds(1, self.set_app_label)
def set_app_label(self):
self.update_label()
def main():
arg_parser = argparse.ArgumentParser(
description='''lks-indicator - Indicates on/off status of Lock keys.''',
formatter_class=argparse.RawTextHelpFormatter)
arg_parser.add_argument(
'--show-all', action='store_true',
help='Show all keys in label', required=False)
arg_parser.add_argument(
'-m','--monochrome', action='store_true',
help='Use monochrome icon')
arg_parser.add_argument(
'--ignore-keys', type=str,
help='Ignore specified keys (C, N or S)',
nargs='+', required=False)
args = arg_parser.parse_args()
indicator = LockKeyStatusIndicator(
show_all=args.show_all,
ignore_keys=args.ignore_keys,
monochrome=args.monochrome)
indicator.run()
if __name__ == '__main__':
main()
2025-09-13
9.5-9.8
2025.9.5 - 9.8 수안보 여행
수안보연수원, 충주박물관, 탄금대
영화식당: 여러가지 나물. 고기만 좋아하는 사람들은 아쉬울 수도. 나물만 나오는데 가격은 그리 싸지 않음.
청운식당: 수안보 주민이 추천한 꿩고기 집.
2025-08-16
SigolHyanggi
경기 김포시 고촌읍 신곡로 114-8
방송에 나온 곳은 운양걸포점.
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_())