2025-10-05

Before choosuk

추석 앞두고 벌초. 10.2.목 밤에 출발 10.3. 0시 넘어 충청북도 친척집 도착.
그리고 벌초, 그리고 식사.
벌초1
불특정 다수가 볼 수 있는 곳이라 자세한 설명은 생략.
개인정보보호법과는 관련이 없다. 개인정보는 산 사람에 대한 것이므로. 그래도 자세한 내용을 누구나 볼 수 있게 하는 건 아니지.

벌초2
그리고 좀 더 올라간 다른 분. 
찾아가는 길이 쉽지 않다. 함께 간 친척분의 도음을 받음.

벌초 후 식사
별초 후 식사는 여기서.
갈비 맛 있게 잘 먹었음.
친척들과 함께.


Solve fbterm errors

fbterm 실행시 에러 해결

1. fbterm can't open frame buffer device
sudo usermod -aG video your_username

~$ sudo usermod -aG ma
[sudo] ma 암호: 
ma은(는) sudoers 파일에 없습니다.
이번 시도는 관리자에게 보고했습니다.

visudo /etc/sudoers
아래 줄을 추가하고 저장.
ma ALL=(ALL:ALL) ALL
/etc/sudoers 파일을 직접 건드리는 건 보안상 위험.



2025-10-02

memo uim dvorak hangul issue

알겠습니다! 로마자 모드를 사용하지 않고 Dvorak 레이아웃에서 3벌식 한글 입력을 제대로 작동하도록 설정하는 데 집중하겠습니다. 이전 답변의 ~/.uim.d/ 기반 커스텀 키맵 설정이 가장 적합한 해결책입니다. 아래는 로마자 모드를 배제하고, ~/.uim.d/를 사용해 Dvorak용 3벌식 키맵을 설정하는 방법을 간결히 정리하고, 추가로 콘솔 환경(fbterm) 최적화 팁을 보강합니다. Debian 13과 uim-byeoru 기준입니다.
해결책: ~/.uim.d/로 Dvorak 3벌식 키맵 설정
1. ~/.uim.d/ 디렉토리 설정

디렉토리 생성 (없을 경우):
bashmkdir -p ~/.uim.d


2. Dvorak용 키맵 파일 생성

파일 생성: ~/.uim.d/dvorak-byeoru.scm
bashnano ~/.uim.d/dvorak-byeoru.scm

아래 내용 추가 (표준 3벌식 키맵을 Dvorak으로 변환, 초성/중성/종성 전체 포함):
scheme;;; ~/.uim.d/dvorak-byeoru.scm
;;; Dvorak 3beol layout for uim-byeoru

(require "byeoru.scm")

;; Dvorak 3beol 레이아웃 정의
(byeoru-define-layout dvorak-3beol
  ;; 초성 (Choseong)
  ("p" (choseong-giyeok . 1))    ; QWERTY 'r' → Dvorak 'p' = ㄱ
  ("f" (choseong-nieun . 1))     ; QWERTY 'e' → Dvorak 'f' = ㄴ
  ("g" (choseong-digeut . 1))    ; QWERTY 'w' → Dvorak 'g' = ㄷ
  ("c" (choseong-rieul . 1))     ; QWERTY 's' → Dvorak 'c' = ㄹ
  ("r" (choseong-mieum . 1))     ; QWERTY 'f' → Dvorak 'r' = ㅁ
  ("o" (choseong-bieup . 1))     ; QWERTY 'd' → Dvorak 'o' = ㅂ
  ("s" (choseong-siot . 1))      ; QWERTY ';' → Dvorak 's' = ㅅ
  ("u" (choseong-ieung . 1))     ; QWERTY 'u' → Dvorak 'u' = ㅇ
  ("h" (choseong-jieut . 1))     ; QWERTY 'j' → Dvorak 'h' = ㅈ
  ("t" (choseong-chieut . 1))    ; QWERTY 'k' → Dvorak 't' = ㅊ
  ("n" (choseong-kieuk . 1))     ; QWERTY 'l' → Dvorak 'n' = ㅋ
  ("e" (choseong-tieut . 1))     ; QWERTY 'i' → Dvorak 'e' = ㅌ
  ("l" (choseong-pieup . 1))     ; QWERTY 'p' → Dvorak 'l' = ㅍ
  ("b" (choseong-hieut . 1))     ; QWERTY 'n' → Dvorak 'b' = ㅎ
  ;; 복합 초성
  ("P" (choseong-ssanggiyeok . 1)) ; Shift+p = ㄲ
  ("G" (choseong-ssangdigeut . 1)) ; Shift+g = ㄸ
  ("O" (choseong-ssangbieup . 1))  ; Shift+o = ㅃ
  ("S" (choseong-ssangsiot . 1))   ; Shift+s = ㅆ
  ("H" (choseong-ssangjieut . 1))  ; Shift+h = ㅉ

  ;; 중성 (Jungseong)
  ("a" (jungseong-a . 1))        ; QWERTY 'k' → Dvorak 'a' = ㅏ
  ("o" (jungseong-ae . 1))       ; QWERTY 'l' → Dvorak 'o' = ㅐ
  ("i" (jungseong-ya . 1))       ; QWERTY 'h' → Dvorak 'i' = ㅑ
  ("j" (jungseong-yae . 1))      ; QWERTY 'g' → Dvorak 'j' = ㅒ
  ("e" (jungseong-eo . 1))       ; QWERTY 'i' → Dvorak 'e' = ㅓ
  ("u" (jungseong-e . 1))        ; QWERTY 'u' → Dvorak 'u' = ㅔ
  ("d" (jungseong-yeo . 1))      ; QWERTY 'y' → Dvorak 'd' = ㅕ
  ("k" (jungseong-ye . 1))       ; QWERTY 'v' → Dvorak 'k' = ㅖ
  ("q" (jungseong-o . 1))        ; QWERTY 'x' → Dvorak 'q' = ㅗ
  ("w" (jungseong-wa . 1))       ; QWERTY ',' → Dvorak 'w' = ㅘ
  ("v" (jungseong-wae . 1))      ; QWERTY '.' → Dvorak 'v' = ㅙ
  ("z" (jungseong-oe . 1))       ; QWERTY '/' → Dvorak 'z' = ㅚ
  (";" (jungseong-yo . 1))       ; QWERTY 'z' → Dvorak ';' = ㅛ
  ("'" (jungseong-u . 1))        ; QWERTY 'q' → Dvorak ''' = ㅜ
  ("," (jungseong-weo . 1))      ; QWERTY 'm' → Dvorak ',' = ㅝ
  ("." (jungseong-we . 1))       ; QWERTY ',' → Dvorak '.' = ㅞ
  ("/" (jungseong-wi . 1))       ; QWERTY '/' → Dvorak '/' = ㅟ
  ("y" (jungseong-yu . 1))       ; QWERTY 't' → Dvorak 'y' = ㅠ
  ("x" (jungseong-eu . 1))       ; QWERTY 'b' → Dvorak 'x' = ㅡ
  ("m" (jungseong-i . 1))        ; QWERTY 'm' → Dvorak 'm' = ㅣ

  ;; 종성 (Jongseong, 초성과 유사)
  ("p" (jongseong-giyeok . 1))   ; QWERTY 'r' → Dvorak 'p' = ㄱ
  ("f" (jongseong-nieun . 1))    ; QWERTY 'e' → Dvorak 'f' = ㄴ
  ("g" (jongseong-digeut . 1))   ; QWERTY 'w' → Dvorak 'g' = ㄷ
  ("c" (jongseong-rieul . 1))    ; QWERTY 's' → Dvorak 'c' = ㄹ
  ("r" (jongseong-mieum . 1))    ; QWERTY 'f' → Dvorak 'r' = ㅁ
  ("o" (jongseong-bieup . 1))    ; QWERTY 'd' → Dvorak 'o' = ㅂ
  ("s" (jongseong-siot . 1))     ; QWERTY ';' → Dvorak 's' = ㅅ
  ("u" (jongseong-ieung . 1))    ; QWERTY 'u' → Dvorak 'u' = ㅇ
  ("h" (jongseong-jieut . 1))    ; QWERTY 'j' → Dvorak 'h' = ㅈ
  ("t" (jongseong-chieut . 1))   ; QWERTY 'k' → Dvorak 't' = ㅊ
  ("n" (jongseong-kieuk . 1))    ; QWERTY 'l' → Dvorak 'n' = ㅋ
  ("e" (jongseong-tieut . 1))    ; QWERTY 'i' → Dvorak 'e' = ㅌ
  ("l" (jongseong-pieup . 1))    ; QWERTY 'p' → Dvorak 'l' = ㅍ
  ("b" (jongseong-hieut . 1))    ; QWERTY 'n' → Dvorak 'b' = ㅎ
  ("P" (jongseong-ssanggiyeok . 1)) ; Shift+p = ㄲ
  ("G" (jongseong-ssangdigeut . 1)) ; Shift+g = ㄸ
  ("O" (jongseong-ssangbieup . 1))  ; Shift+o = ㅃ
  ("S" (jongseong-ssangsiot . 1))   ; Shift+s = ㅆ
  ("H" (jongseong-ssangjieut . 1))  ; Shift+h = ㅉ
)

;; 기본 레이아웃을 Dvorak 3beol로 설정
(set! byeoru-default-layout 'dvorak-3beol)

;; 한글 모드 전환 시 키맵 새로 고침 (Dvorak 반영)
(set! byeoru-refresh-keymap-on-hangul-switch #t)

설명:

위 코드는 표준 3벌식(QWERTY 기준)을 Dvorak 키로 변환한 완전한 매핑입니다.
Dvorak 키는 QWERTY와 다른 위치를 가지므로, 각 자음/모음이 Dvorak의 물리적 키에 맞게 재정의됨 (예: QWERTY 'r'=ㄱ → Dvorak 'p'=ㄱ).
byeoru-refresh-keymap-on-hangul-switch를 t로 설정해 한글 모드로 전환 시 Dvorak 레이아웃을 강제로 반영.
필요 시 /usr/share/uim/byeoru.scm을 참고해 추가 기호(숫자, 특수문자) 매핑 보완.



3. 콘솔 환경 설정 (fbterm 최적화)

콘솔 키맵을 Dvorak으로 설정:
bashsudo dpkg-reconfigure console-setup

"Keyboard layout" → "English (US)", "Keymap" → "dvorak".
UTF-8 인코딩 유지.


fbterm 실행 전 키맵 로드:
bashloadkeys dvorak
fbterm

uim 환경 변수 설정:
bashexport GTK_IM_MODULE=uim
uim-xim --engine=byeoru &


4. 테스트 및 디버깅

uim 재시작:
bashpkill uim-xim
uim-xim --engine=byeoru &

fbterm에서 한글 입력 테스트:

한영 전환 (기본: Shift+Space).
Dvorak 키로 입력 (예: 'p' 누르면 ㄱ, 'a' 누르면 ㅏ 출력 확인).


오류 확인:
bashuim -d

/tmp/uim.log나 stderr로 키매핑 오류 확인.
특정 키가 안 맞으면 ~/.uim.d/dvorak-byeoru.scm에서 해당 키 수정.



5. uim-pref-gtk 보완 (선택)
로마자 모드를 안 쓰더라도 GUI로 설정 보완:

실행:
bashuim-pref-gtk

Byeoru 탭:

"Korean keyboard layout" → "3beol" 확인.
"Refresh keyboard map at each switch into Hangul mode" 체크 (필수, Dvorak 반영).
한영 전환 키 (Shift+Space 등) 확인.


저장 후 uim 재시작.

문제 지속 시

매핑 오류: dvorak-byeoru.scm의 특정 키 (예: 'p'가 ㄱ 대신 다른 글자 출력) 수정.
fbterm 호환성: fbterm 버전 확인 (fbterm --version), 최신인지 확인 (sudo apt update && sudo apt install fbterm).
로그 공유: 오류 로그나 특정 키 입력 결과 (예: 'p'를 눌렀는데 X 나옴)를 알려주시면 매핑 조정 도움드립니다.

요약
~/.uim.d/dvorak-byeoru.scm에 Dvorak용 3벌식 키맵을 정의하고, byeoru-refresh-keymap-on-hangul-switch를 활성화하면 fbterm에서 한글 입력이 Dvorak 키로 정상 작동합니다. 로마자 모드를 배제했으니, 3벌식 기반으로 정확한 자판 입력이 가능할 겁니다. 추가 질문이나 특정 키 문제 있으면 말씀해 주세요!

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 
YOUR_USERNAME ALL=(root) NOPASSWD: /usr/bin/loadkeys dvorak

[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

My .fbtermrc D2Coding fontsize font-width font-height Setting

# Lines starting with '#' are ignored.
# Note that end-of-line comments are NOT supported, comments must be on a line of their own.

# font family names/pixelsize used by fbterm, multiple font family names must be seperated by ','
# and using a fixed width font as the first is strongly recommended
font-names=D2Coding
font-size=20

# force font width (and/or height), usually for non-fixed width fonts
# legal value format: n (fw_new = n), +n (fw_new = fw_old + n), -n (fw_new = fw_old - n)
font-width=10
font-height=24

# default color of foreground/background text
# available colors: 0 = black, 1 = red, 2 = green, 3 = brown, 4 = blue, 5 = magenta, 6 = cyan, 7 = white
color-foreground=7
color-background=0

# max scroll-back history lines of every window, value must be [0 - 65535], 0 means disable it
history-lines=1000

# up to 5 additional text encodings, multiple encodings must be seperated by ','
# run 'iconv --list' to get available encodings.
text-encodings=

# cursor shape: 0 = underline, 1 = block
# cursor flash interval in milliseconds, 0 means disable flashing
cursor-shape=0
cursor-interval=500

# additional ascii chars considered as part of a word while auto-selecting text, except ' ', 0-9, a-z, A-Z
word-chars=._-

# change the clockwise orientation angle of screen display
# available values: 0 = 0 degree, 1 = 90 degrees, 2 = 180 degrees, 3 = 270 degrees
screen-rotate=0

# specify the favorite input method program to run
input-method=

# treat ambiguous width characters as wide
#ambiguous-wide=yes

2026 trend korea horse power

2025.09.29. 19:30 교보문고 전미영 발표

2026 trend
2026 trend

horse power
HORSE POWER

2026 treand: HORSE POWER
Human in the loop
Oh my feelings! The Feelconomy
Result on Demand: Zero click
Self Directed Preparation Peady core
Efficient organizations through AI Transformation
Pixelated Life
Observant Consumers: Price Decoding
Widen your Health intelligence
Everyone is anisland the 1.5 Households
Returning to the Fundamentals

책 내용을 옮기는 건 저작권 문제가 있어서... 책 보실 분은 사서 보셔요.
https://product.kyobobook.co.kr/detail/S000217467412

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

fbterm 에서 한글 입력 해 보고자 fcitx5도 해보고, uim도 해 봤는데 한글 입력 안 됨.
일단 중지.
나중에 다시 시도 해 볼 생각은 있음.

일단 절반의 성공. 아래 링크.

2025-09-24

fcitx5-configtool Dvorak and 3beolsik

Debian 13으로 올리고  ibus-hangul 잘 쓰고 있다가 fcitx5-hangul 로 바꿈.

Ctrl-space로 한영 전환 되게 설정은 했다.
그런데 한글입력이 이상하다. 두벌식도, 세벌식도 아닌 요상한 배열

gemini chatgpt 검색 등등을 통해 해결.
그 문제의 원인은 뭐였냐면...설명이 쉽지 않네. 풀어서 얘기하면...
쿼티 기준으로 k 칠 때 한글ㄱ 나와야되는데 
Dvorak 기준으로 k 칠 때 한글ㄱ이 나오는 게 문제.

해결 방법은?
왼쪽 메뉴에서 한글. 그 아래에 있는 버튼 클릭. 그 아래 
시스템 키보드 자판 버튼 선택.
자판 영어(미국)
아래 그림 참고.

Dvorak 자판과 한글 자판 

저렇게 설정해서 잘 쓰고 있었는데 설정을 이리저리 건드리다 보니 한글 이라는 항목이 안 보임. 
* 한국어와 한글은 다름.

나중에 잘못한 듯. 아래 링크 참고해서 다시 시도해야 될듯.
https://forcecore.tistory.com/1434

2025-09-21

nabi REMOVED from testing

한글 입력기 나비가 사라집니다. 대안인 ibus가 있어서 큰 문제는 없을 겁니다.
https://tracker.debian.org/pkg/nabi

2025-09-20

ddtp

https://ddtp.debian.org/ddtss/index.cgi/ko
로그인 아이디
패스워드 입력하고
진행하면 됨.
My ID sebuls
비번은 당연히 비공개.
DDTP에 대한 설명은
https://www.debian.org/international/l10n/ddtp.ko.html

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

시골향기. 수제비 먹으러 감.
왜 갔냐면? 어제 아내가 TV 봤는데 그 집 방송 나왔다고. 전현무가 맛있게 먹더라고.
우리가 간 곳은 김포 고촌.
경기 김포시 고촌읍 신곡로 114-8
방송에 나온 곳은 운양걸포점.
식당 앞에 차가 줄지어 서 있어서 들어가지 못하고 그 근처 주차하고 걸어감.
아내와 함께 한 시간 가까이 기다려서 먹음.
우거지수제비 23000원. 아내와 함께 잘 먹고.
보리밥 13000원. 집에 있는 우리 아이들에게 주려고 포장.

수제비

메뉴판



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_())

ai06NalMok

#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
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(RADIUS, RADIUS + i * CELL_SIZE,
                            RADIUS + (BOARD_SIZE - 1) * CELL_SIZE, RADIUS + i * CELL_SIZE)
            qp.drawLine(RADIUS + i * CELL_SIZE, RADIUS,
                            RADIUS + i * CELL_SIZE, 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 = RADIUS + x * CELL_SIZE
            cy = 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(
                        RADIUS + x * CELL_SIZE - RADIUS,
                        RADIUS + y * CELL_SIZE - RADIUS,
                        RADIUS * 2,
                        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 = RADIUS + x * CELL_SIZE
            cy = 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() - RADIUS + CELL_SIZE / 2) // CELL_SIZE)
        y = int((event.y() - 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_())