Posts for: #Programming

달무티 CPU 플레이어 만들기 Part 8: 봇을 고치다 — 리드 지키기 계획과 아껴 두기

잘 두는 사람에게서 찾은 세 가지 습관을 hard 봇에 넣었습니다. 바꾸기 전과 후의 판단 흐름을 그림으로 나란히 놓고, 새 봇이 구버전 봇들 사이에서 얼마나 세졌는지, 그리고 아직 말할 수 없는 것이 무엇인지를 코드 없이 정리합니다.

[Read more]

달무티 CPU 플레이어 만들기 Part 7: 잘 두는 사람은 봇과 어디서 다르게 두는가

hard 봇을 열 판 중 여섯 판 이긴 사용자의 결정 6,746개를, 같은 순간 봇이 냈을 수와 하나씩 비교했습니다. 대부분은 같았습니다. 갈라진 자리는 셋이었습니다. 강한 한두 장으로 리드를 지키고, 초반에 최상위 카드를 아끼고, 받을 때 딱 이길 만큼만 씁니다.

[Read more]

달무티 CPU 플레이어 만들기 Part 6: 사람이 열 판 중 여섯 판을 이기는 hard 봇

hard 봇 세 명을 상대로 사람이 판의 59.8% 를 1등으로 끝냈습니다. 공정하다면 25% 여야 합니다. 가장 많이 플레이한 사용자에게는 hard 와 easy 의 차이가 0.2%p 였습니다. 코드 없이, 무엇을 어떻게 쟀고 왜 봇이 사람에게 읽혔는지를 정리합니다.

[Read more]

DHH 의 Rails World 2026 키노트 Part 2: 옹호와 반박, 그리고 제3의 길

DHH 의 Rails World 2026 키노트에 쏟아진 반응을 Hacker News 스레드 두 개, 참석자와 커뮤니티 구성원의 블로그, GeekNews 댓글, 그리고 이틀 뒤 Aaron Patterson 의 폐막 키노트까지 모아 비판·옹호·절충의 세 갈래로 정리합니다. 쟁점은 AI 가 아니라 「Rails 는 어디 갔나」였습니다.

[Read more]

DHH 의 Rails World 2026 키노트 Part 1: 「연필을 내려놓아라」 — 그가 실제로 말한 것

2026년 9월 23일 Rails World 2026 개막 키노트에서 DHH 는 37signals 가 손으로 코드를 쓰는 일을 그만두었고, HEY 를 Rails 웹앱에서 네이티브 앱 6개와 Rust 백엔드로 다시 만들고 있다고 밝혔습니다. 한 시간짜리 연설을 챕터 순서대로 재구성하고, 그가 제시한 숫자들을 1차 출처와 대조합니다.

[Read more]

오목 엔진 Rapfi 해부 Part 4: 직접 빌드해서 돌려 보기

Rapfi 를 소스에서 빌드하고 Piskvork 프로토콜로 직접 대화해 봅니다. 탐색 로그 읽는 법, 같은 국면이 자유룰과 렌쥬에서 어떻게 달라지는지, 花月 주형의 Multi-PV 분석, STRENGTH 로 기력 낮추기, MCTS 탐색기, 파이썬으로 엔진 부리기까지 Apple M1 Max 에서 실제로 실행한 결과로 보여 줍니다.

[Read more]

오목 엔진 Rapfi 해부 Part 3: Rapfi 는 어떻게 수를 고르는가

Rapfi 의 소스 코드를 따라가며 한 수를 고르기까지의 의사결정 과정을 해부합니다. 선 패턴 16종과 네 방향 조합 14종으로 판을 읽는 법, 렌쥬 금수의 정확한 판정, 고전 평가와 NNUE(mix9svq)를 섞어 쓰는 방식, Stockfish 에서 가져온 알파-베타 탐색과 VCF 잎 탐색, 선택 사항인 MCTS, 시간 관리와 멀티스레드까지 정리합니다.

[Read more]

Hugo 로 트럼프 카드 그리기: cards shortcode

포커 같은 트럼프 게임 글의 예제를 글자가 아니라 카드 그림으로 보여 주기 위해 Hugo shortcode 두 개를 만들었습니다. As Kh 같은 포커 표준 표기를 SVG 카드로 바꾸는 cards(도해)와 card(문장 안 한 장)의 사용법, 이미지·이모지·mermaid 대신 SVG 를 고른 이유, 무늬를 이모지가 아니라 글자로 그린 이유, 그리고 실제 렌더링 예를 정리했습니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 8: 유행, 성능, 그리고 무엇이 중요한가

«A Philosophy of Software Design» 19~21장으로 8부작을 맺습니다. struct embedding 으로 흉내 낸 구현 상속에서 ‘덮어쓴’ 메서드가 부모의 호출에는 적용되지 않는 함정을 재현하고 인터페이스 합성으로 바꾸며, 저자의 TDD 비판을 공정하게 옮기고 반론을 덧붙입니다. 20장의 RAMCloud Buffer 예제를 Go 로 옮겨 ‘측정 먼저’ 를 실제로 해 본 결과는 책의 2배가 아니라 10% 였고, 그 숫자를 그대로 싣습니다. 마지막으로 시리즈 전체를 위험 신호 표 하나로 정리합니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 7: 기존 코드 수정, 일관성, 명백함

«A Philosophy of Software Design» 16~18장을 Go 코드로 읽습니다. 경로 탈출 버그를 Save 한 곳에만 땜질해서 Load 와 Delete 에 구멍을 남기는 수정과, ID 타입을 도입해 세 함수를 한 번에 막는 수정을 비교하고, ‘없음’ 을 네 가지 방식으로 표현하는 저장소를 한 가지로 통일하며, 제네릭 Pair 의 First·Second 를 이름 있는 struct 로 바꾸고, 이벤트 버스 뒤에 숨은 호출 흐름을 직선으로 펴는 과정을 Before → After 로 보여 줍니다. gofmt 가 이 책의 17장을 언어 차원에서 구현한 것이라는 이야기도 합니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 6: 주석과 이름

«A Philosophy of Software Design» 12~15장을 Go 코드로 읽습니다. 이름을 그대로 풀어 쓴 주석을 단위와 경계 조건을 채운 주석으로 바꾸고, 저자가 6개월을 쏟은 Sprite 의 block 버그를 Go 의 named type 으로 컴파일 시점에 잡아내며, 14.5절에서 저자가 Go 스타일 가이드와 갈라서는 지점을 양쪽 논거 그대로 싣습니다. 여섯 줄짜리 주석 없이는 설명할 수 없던 Search 함수를 주석부터 다시 써서 설계를 고치는 과정도 보여 줍니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 5: 에러의 존재를 없애라

«A Philosophy of Software Design» 10장을 Go 코드로 읽습니다. 예외가 없는 Go 에서 이 장은 ‘if err != nil 을 어떻게 줄이는가’ 라는 질문이 됩니다. 없는 노트를 지우면 에러를 내던 Delete 를 ‘없는 상태로 만든다’ 로 재정의해 시그니처에서 error 를 지우고, 범위 밖이면 패닉하던 Snippet 을 항상 정의되는 함수로 바꾸고, 짧은 읽기를 io.ReadFull 로 감추고, 핸들러마다 반복되던 http.Error 를 한 곳으로 모으고, nil 검사를 부르던 ‘선택 없음’ 을 빈 선택으로 없앱니다. 저자가 Tcl 의 unset 을 설계 실수로 꼽는 이유와, 이 원칙을 지나치게 밀면 어떻게 되는지도 함께 봅니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 4: 합칠 것인가 나눌 것인가, 그리고 두 번 설계하기

«A Philosophy of Software Design» 9장과 11장을 Go 코드로 읽습니다. ‘20줄 넘으면 나눠라’ 를 따르느라 공유 상태 구조체를 주고받는 함수 다섯 개가 된 임포트 코드를 하나로 되돌리고, 텍스트 타입 안에 커서 undo 까지 끌어안은 설계를 범용 History 와 특수 목적 Action 으로 갈라내며, 줄 단위·범위 단위 두 인터페이스를 둘 다 Go 로 써 보고 상위 코드의 길이로 판정합니다. 책이 Clean Code 와 갈라서는 지점도 이 장에 있습니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 3: 범용성, 계층, 그리고 복잡성 끌어내리기

«A Philosophy of Software Design» 6~8장을 Go 코드로 읽습니다. 백스페이스 키마다 메서드를 하나씩 둔 텍스트 타입을 Insert 와 Delete 두 개로 줄이면서 여러 줄 선택 삭제가 공짜로 생기는 과정, 다섯 메서드 중 넷이 저장소를 그대로 부르는 ‘서비스 계층’ 을 걷어내는 과정, 함수 네 단계를 관통하는 pass-through 인자를 App 구조체로 거두는 과정, 그리고 재시도 간격을 설정값으로 떠넘기는 대신 응답 시간을 재서 스스로 정하게 만드는 과정을 Before → After 로 보여 줍니다. Go 의 context.Context 가 책의 context 와 다른 이유도 짚습니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 2: 깊은 모듈과 정보 은닉

«A Philosophy of Software Design» 4~5장을 Go 코드로 읽습니다. 객체 세 개를 조립해야 노트 하나를 읽을 수 있는 얕은 모듈을 함수 하나짜리 깊은 모듈로 바꾸고, 내부 map 을 그대로 돌려주는 인터페이스가 어떻게 정보를 누출하는지, ‘읽고 나서 파싱한다’ 는 시간적 분해가 왜 같은 지식을 두 곳에 복사하게 만드는지를 실제로 컴파일되는 Before → After 로 보여 줍니다. go doc 출력 줄 수로 인터페이스 크기를 세는 방법도 소개합니다.

[Read more]

Go 로 읽는 소프트웨어 설계의 철학 Part 1: 복잡성은 어디서 오는가

존 오스터하우트의 «A Philosophy of Software Design» 을 Go 코드로 다시 읽는 8부작의 첫 편입니다. 책이 말하는 복잡성의 세 가지 증상(변경 증폭·인지 부하·모름)과 두 가지 원인(의존성·모호성)을 각각 Before → After Go 코드로 보여 주고, 전술적 땜질이 어떻게 쌓이는지와 전략적 수정이 무엇을 다르게 하는지를 같은 버그 하나로 비교합니다. Go 가 이 책과 어디서 부딪히는지도 미리 밝혀 둡니다.

[Read more]

훌라 CPU 플레이어 만들기 Part 8: 처음 보는 판에서 재다

훌라 hard 봇의 값을 동결하고, 한 번도 쓰지 않은 딜 256개에서 최종 검증을 합니다. 실험이 고른 값을 일부러 쓰지 않은 자리와 그 이유, 시험 문제를 열기 전에 합격선을 못 박는 절차, 인원이 늘수록 두 배씩 벌어진 결과, 그리고 Part 4 의 여덟 자리 중 무엇이 메워졌고 무엇이 메운 줄 알았는데 아니었는지를 결산합니다.

[Read more]

훌라 CPU 플레이어 만들기 Part 7: 저울의 추를 하나씩 빼 보니

훌라 hard 봇을 시뮬레이터로 잽니다. 같은 딜에서 hard 와 easy 를 바꿔 앉혀 딜 운을 지우는 방법, 도구가 만들어지자마자 잡은 “다른 봇을 재고 있었다” 는 버그, 8 seed 로는 아무 말도 못 하고 32 seed 에서야 방향이 보인 첫 성적, 그리고 손으로 정한 추 열한 개를 하나씩 빼 보니 넷은 5만 번의 결정에서 수를 하나도 바꾸지 않았다는 사실까지 다룹니다.

[Read more]

훌라 CPU 플레이어 만들기 Part 6: 남의 차례에 끼어들고 판을 접는 판단

훌라 hard 봇의 땡큐와 스톱 판단을 다룹니다. 땡큐는 가져갔을 때와 안 가져갔을 때를 같은 자로 재야 한다는 것, 봇을 남의 차례에 깨우는 배선이 없으면 시뮬레이터에서 98% 를 가져가던 봇이 실제 서버에서는 한 번도 못 건다는 것, 일반 스톱은 기대값으로 판단하되 첫 구현에서 건 스톱의 36% 가 스톱박이 나 확률부터 고쳐야 했다는 이야기입니다.

[Read more]

훌라 CPU 플레이어 만들기 Part 5: 한 턴을 통째로 그려 보는 hard 봇

훌라 hard 봇이 자기 차례에 무엇을 알고 어떻게 한 수를 고르는지 다룹니다. 지금까지 공개된 카드를 적어 두는 장부, 이번 턴에 할 수 있는 모든 행동 순서를 끝까지 그려 보는 계획, 남은 손패를 재는 추 일곱 개짜리 저울, 그리고 저울보다 위에 있는 규칙까지 카드 게임 용어로 설명합니다.

[Read more]