대부호 CPU 봇의 성적표는 처음부터 몬테카를로였고, 지금 운영 중인 봇은 상대 손패를 열여섯 번 뽑아 판 끝까지 두어 보고 수를 고릅니다. 시리즈가 검산하지 않고 남긴 장부의 위험 근사를 실제 게임의 3,479개 평가에서 몬테카를로로 다시 재고, 운영 봇의 탐색을 4,000개 표본으로 재현했습니다.
Posts for: #Golang
몬테카를로 시뮬레이션 Part 1: 솔리테어 한 판의 확률
몬테카를로 시뮬레이션은 계산으로 구하기 어려운 확률을 무작위 시행을 세어서 구하는 방법입니다. 울람의 솔리테어에서 시작한 역사와 표본 평균, 1/√n 으로 줄어드는 오차를 정리하고, 몬티 홀·생일 문제·드 메레의 주사위를 Go 로 돌려 봅니다.
뉴컴의 역설 Part 2: 증거냐 원인이냐 — 두 결정 이론과 서로의 코드를 읽는 프로그램
뉴컴의 역설에서 갈린 두 직관은 두 개의 결정 이론이 되었습니다. 내 선택이 무엇의 증거인지를 보는 증거적 결정 이론과, 내 선택이 무엇을 일으키는지를 보는 인과적 결정 이론입니다. “그렇게 똑똑하면 왜 부자가 아니냐” 는 놀림과 그 답, 각 이론을 곤란하게 만드는 흡연 병변과 다마스쿠스의 죽음과 사이코패스 버튼, 독약을 마실 의도에 돈을 주는 억만장자, 그리고 뉴컴의 문제가 사실 쌍둥이와 하는 죄수의 딜레마라는 주장까지 정리합니다. 마지막에는 예측자가 내 코드를 실행해 보는 뉴컴과, 서로의 소스 코드를 읽는 프로그램들의 죄수의 딜레마를 Go 로 돌려 봅니다.
뉴컴의 역설 Part 1: 상자 하나인가, 둘 다인가 — 예측자가 이미 정해 둔 상금
당신의 선택을 거의 틀림없이 맞히는 예측자가 이미 상자를 채워 두었습니다. 투명한 상자에는 1,000달러, 불투명한 상자에는 100만 달러 또는 아무것도 없습니다. 하나만 가져갈 것인가, 둘 다 가져갈 것인가. 1969년 철학자 노직이 소개한 뒤 반세기 넘게 사람들을 정확히 둘로 갈라 온 뉴컴의 역설을 정리합니다. 규칙과 두 논증, 1973년 『사이언티픽 아메리칸』 독자 편지와 철학자 설문, 3만 명이 넘게 참여한 신문 투표의 결과, 그리고 예측자가 얼마나 정확해야 한 상자가 유리해지는지를 Go 로 직접 계산해 봅니다.
공유지의 비극 Part 1: 목동은 왜 소를 한 마리 더 들이는가 — 하딘의 목초지
모두에게 열린 목초지에서 목동들은 소를 한 마리씩 더 들이다가 풀밭을 망칩니다. 1968년 『사이언스』에 실린 개릿 하딘의 「공유지의 비극」이 실제로 무엇을 말했는지(주제는 목초지가 아니라 인구였습니다), 목동 수에 따라 마을의 이익이 어떻게 녹아내리는지 Go 로 직접 돌려 본 결과, 아리스토텔레스와 1833년 옥스퍼드 강의까지 거슬러 올라가는 이 생각의 뿌리, 하딘의 처방과 그 그림자, 그리고 그가 30년 뒤 스스로 인정한 단어 하나의 누락까지 정리합니다.
최후통첩 게임과 공정성 Part 5: 영화·문학·게임 속 분배 — 거절할 수 없는 제안에서 해적 게임까지
이야기와 게임이 그린 분배를 분류 흐름도로 정리하고 시리즈를 마무리합니다. 신을 속인 프로메테우스의 고기 나누기, “내 것도 네 것도 되지 말고 나누라” 는 솔로몬 앞의 여인, 똑같이 나눴다가 죽은 이솝의 당나귀, 다 주고 나서 “한 명은 왜 필요하냐” 는 말을 들은 리어 왕, 1만 달러는 거절했지만 총구 앞에서 1천 달러를 받아들인 『대부』의 밴드 리더, 같은 금광에서 주는 사람과 주지 않는 사람이 갈린 『시에라 마드레의 황금』, 35% 를 받아들이게 만든 『브레이킹 배드』, 그리고 1% 만 주고도 통과되는 해적 게임을 Go 로 풀어 봅니다.
몬티 홀 문제 Part 2: 진행자의 규칙이 답을 정한다 — 몬티 폴, 몬티 크롤, 사촌 문제들
몬티 홀 문제의 3분의 2 는 진행자가 반드시 염소 문을 열고 반드시 바꿀 기회를 준다는 가정에서만 나옵니다. 진짜 몬티 홀이 “그건 그날 기분에 달렸다” 고 말한 이유, 진행자가 미끄러져 우연히 문을 열면 반반이 되는 몬티 폴, 어느 문이 열렸는지까지 정보가 되는 몬티 크롤, 맞혔을 때만 바꾸라고 권하는 심술궂은 진행자를 Go 시뮬레이션과 베이즈의 비로 계산하고, 베르트랑의 상자·세 죄수·아들 딸 문제 같은 사촌 문제를 거쳐 사례 편에서 쓸 분류 흐름도를 세웁니다.
몬티 홀 문제 Part 1: 세 개의 문과 만 통의 편지 — 바꾸면 왜 3분의 2 인가
게임쇼에서 문 하나를 고르자 진행자가 다른 문을 열어 염소를 보여 주고 “바꾸시겠습니까?” 라고 묻습니다. 바꾸면 이길 확률이 3분의 2 가 되는 이유를 세 가지 방법과 Go 시뮬레이션 100만 번으로 보이고, 1975년 셀빈의 편지와 진짜 몬티 홀의 답장, 1990년 매릴린 보스 사반트의 칼럼에 쏟아진 박사들의 항의 편지, 진행자 본인이 거실에서 다시 해 본 실험, 그리고 시뮬레이션을 보고서야 마지못해 받아들인 에르되시까지 이 문제의 역사를 정리합니다.
대부호 CPU 플레이어 만들기 Part 7: 처음 보는 판에서 재다
대부호 hard 봇의 최종 시험입니다. 시험 문제를 열기 전에 합격선 여섯 개를 커밋으로 못 박은 절차, 한 번도 쓰지 않은 딜 128개에서 한 번만 돌린 결과(easy 대비 −0.746, 4인 1등 비율 61%, 반칙패 0), 봇의 손패와 결정 단계를 수마다 눈으로 읽은 재생, 기본 봇으로 올리면서 남긴 뒷길, 그리고 이 결과로 말할 수 있는 것과 아직 말할 수 없는 것을 정리합니다.
대부호 CPU 플레이어 만들기 Part 6: 항목을 하나씩 꺼 보니
대부호 hard 봇을 시뮬레이터로 잽니다. 운영 서버와 같은 결정 경로를 타면서 수 하나를 6ms 에서 30µs 로 줄인 메모리 엔진, 봇 하나를 셋 사이에 앉히는 양방향 비교가 왜 필요했는지, 같은 봇끼리 정확히 0 이 나오는 장치 검사, 그리고 hard 의 판단 항목을 하나씩 켜고 꺼 본 결과 규칙 세 개가 개선의 91% 를 차지했고 두 항은 해로워서 지웠으며 문턱 하나를 네 배로 올린 이야기입니다.
대부호 CPU 플레이어 만들기 Part 5: easy 와 hard 는 어디서 갈라지는가
대부호 easy 봇과 hard 봇이 같은 자리에서 무엇을 다르게 보고 다르게 고르는지를 그림 하나로 비교합니다. hard 의 결정은 «즉시 끝내기 → 확정 종료 줄 → 정상 종료 가드 → 위협 차단 → 점수» 순이고, 점수는 easy 의 수를 기본으로 두고 문턱을 넘을 때만 바꿉니다. 네 가지 점수 항(계획 진척·통제·온존·8컷)과 받을 수 있어도 패스하는 이유, 조공 돌려주기까지 카드 게임 용어로 설명합니다.
대부호 CPU 플레이어 만들기 Part 4: 혁명을 따라가는 손패 계획
대부호 hard 봇이 자기 손패를 어떻게 보는지 다룹니다. 남은 카드를 최소 몇 수에 낼 수 있는지와 그 마지막 수가 합법인지를 따로 세는 이유, 같은 숫자 4장을 내면 서열이 뒤집혀 끝내기 카드가 바뀌는 것을 부분집합 표에 홀짝 하나로 담는 방법, 그리고 상대가 아무도 받을 수 없다는 장부의 증명을 이어 붙여 만드는 확정 종료 줄을 설명합니다.
대부호 CPU 플레이어 만들기 Part 3: 본 것을 잊지 않는 장부
대부호 hard 봇이 무엇을 보는지 다룹니다. 사람 화면에 이미 보이는 것만 시야에 더한 이유, 54장을 내 손패·나온 카드·위치를 아는 카드·미확인 카드 넷으로 나누는 장부, 패스한 자리의 위험이 정확히 0 인 대부호만의 사정, 그리고 “이 수를 내면 누가 받을 수 있는가” 를 재는 위험 점수가 왜 확률이 아니라 근사 점수인지를 설명합니다.
대부호 CPU 플레이어 만들기 Part 2: 규칙 네 줄짜리 easy 봇
대부호 easy 봇을 규칙 네 줄로 만듭니다. 리드는 가장 약한 숫자를 통째로 내되 계단이 더 털면 계단, 받기는 이기는 가장 약한 수, 반칙 끝내기는 다른 수가 있으면 피하고, 조공은 가장 약한 카드를 돌려줍니다. 시간 초과 자동 수와 같은 함수여야 하는 이유, 이 봇이 우연히 잘하는 것, 그리고 열 판에 한 판꼴로 반칙패로 끝나는 이유를 정리합니다.
대부호 CPU 플레이어 만들기 Part 1: 달무티 봇을 그대로 못 쓰는 이유
대부호(大富豪)를 디지털 게임으로 만들 때 CPU 플레이어를 어떻게 설계했는지 다루는 시리즈입니다. Part 1 에서는 달무티·렉시오·훌라 봇을 만들며 얻은 교훈을 정리하고, 대부호의 규칙 열 가지가 봇에게 왜 새로운 문제인지 짚은 뒤, 봇이 볼 수 있는 것과 돌려줘야 하는 것의 계약을 Go 코드로 못 박습니다.
오목 엔진 Rapfi 해부 Part 5: Rapfi 로 오목·렌쥬 서비스를 만들 때
Rapfi 를 엔진으로 오목·렌쥬 서비스를 만들 때 고려할 점을 정리합니다. 서버·브라우저(WASM)·앱 배치에 따라 달라지는 GPLv3 의무, 상태를 가진 Piskvork 프로토콜을 다루는 프로세스 모델과 Go 래퍼, 판 크기와 렌쥬 오프닝 규정의 공백, 초보자용 난이도 설계의 함정, 자원·버전 관리까지 실측과 소스 근거로 짚습니다.
Go 로 읽는 소프트웨어 설계의 철학 Part 8: 유행, 성능, 그리고 무엇이 중요한가
«A Philosophy of Software Design» 19~21장으로 8부작을 맺습니다. struct embedding 으로 흉내 낸 구현 상속에서 ‘덮어쓴’ 메서드가 부모의 호출에는 적용되지 않는 함정을 재현하고 인터페이스 합성으로 바꾸며, 저자의 TDD 비판을 공정하게 옮기고 반론을 덧붙입니다. 20장의 RAMCloud Buffer 예제를 Go 로 옮겨 ‘측정 먼저’ 를 실제로 해 본 결과는 책의 2배가 아니라 10% 였고, 그 숫자를 그대로 싣습니다. 마지막으로 시리즈 전체를 위험 신호 표 하나로 정리합니다.
Go 로 읽는 소프트웨어 설계의 철학 Part 7: 기존 코드 수정, 일관성, 명백함
«A Philosophy of Software Design» 16~18장을 Go 코드로 읽습니다. 경로 탈출 버그를 Save 한 곳에만 땜질해서 Load 와 Delete 에 구멍을 남기는 수정과, ID 타입을 도입해 세 함수를 한 번에 막는 수정을 비교하고, ‘없음’ 을 네 가지 방식으로 표현하는 저장소를 한 가지로 통일하며, 제네릭 Pair 의 First·Second 를 이름 있는 struct 로 바꾸고, 이벤트 버스 뒤에 숨은 호출 흐름을 직선으로 펴는 과정을 Before → After 로 보여 줍니다. gofmt 가 이 책의 17장을 언어 차원에서 구현한 것이라는 이야기도 합니다.
Go 로 읽는 소프트웨어 설계의 철학 Part 6: 주석과 이름
«A Philosophy of Software Design» 12~15장을 Go 코드로 읽습니다. 이름을 그대로 풀어 쓴 주석을 단위와 경계 조건을 채운 주석으로 바꾸고, 저자가 6개월을 쏟은 Sprite 의 block 버그를 Go 의 named type 으로 컴파일 시점에 잡아내며, 14.5절에서 저자가 Go 스타일 가이드와 갈라서는 지점을 양쪽 논거 그대로 싣습니다. 여섯 줄짜리 주석 없이는 설명할 수 없던 Search 함수를 주석부터 다시 써서 설계를 고치는 과정도 보여 줍니다.
Go 로 읽는 소프트웨어 설계의 철학 Part 5: 에러의 존재를 없애라
«A Philosophy of Software Design» 10장을 Go 코드로 읽습니다. 예외가 없는 Go 에서 이 장은 ‘if err != nil 을 어떻게 줄이는가’ 라는 질문이 됩니다. 없는 노트를 지우면 에러를 내던 Delete 를 ‘없는 상태로 만든다’ 로 재정의해 시그니처에서 error 를 지우고, 범위 밖이면 패닉하던 Snippet 을 항상 정의되는 함수로 바꾸고, 짧은 읽기를 io.ReadFull 로 감추고, 핸들러마다 반복되던 http.Error 를 한 곳으로 모으고, nil 검사를 부르던 ‘선택 없음’ 을 빈 선택으로 없앱니다. 저자가 Tcl 의 unset 을 설계 실수로 꼽는 이유와, 이 원칙을 지나치게 밀면 어떻게 되는지도 함께 봅니다.