놀이터 › 장기 › 장기 컴퓨터는 어떻게 생각하나, 네 단계를 숫자로 나눈 과정
장기 컴퓨터는 어떻게 생각하나, 네 단계를 숫자로 나눈 과정
난이도를 네 단계로 나누는 것은 쉽습니다. 그 네 단계가 진짜로 다른지 확인하는 것이 어렵습니다. 화면으로는 네 단계 모두 멀쩡하게 잘 둡니다. 단계끼리 맞붙여 승률을 재고 나서야 최고수가 어려움에게 열두 판을 내리 지고 있다는 것을 알았습니다. 아래는 그 과정을 숫자와 함께 적은 것입니다.
기본 방식
상대는 알파베타 탐색을 씁니다. 내가 둘 수 있는 수를 하나씩 둬 보고, 그 뒤에 상대가 둘 수를 둬 보고, 다시 내가 둘 수를 둬 보는 식으로 앞을 읽습니다. 끝까지 가서 점수를 매긴 뒤 가장 좋은 쪽을 고릅니다.
점수는 남은 기물의 가치 합에 자리 점수를 조금 더한 값입니다. 알파베타는 이미 진 가지를 중간에 잘라내는 방법이라, 좋은 수를 먼저 볼수록 더 많이 잘라냅니다. 그래서 잡는 수를 먼저 봅니다. 비싼 기물을 잡는 수가 가장 앞입니다.
깊이를 하나 올릴 때마다 얼마나 늘어나나
| 몇 수 앞 | 본 판의 개수 | 걸린 시간 |
|---|---|---|
| 1수 | 37개 | 0.001초 |
| 2수 | 1,117개 | 0.001초 |
| 3수 | 2,825개 | 0.007초 |
| 4수 | 47,789개 | 0.048초 |
| 5수 | 173,668개 | 0.376초 |
한 수 더 보려면 대략 그 전의 몇십 배를 봐야 합니다. 장기는 한 자리에서 둘 수 있는 수가 평균 37개쯤 되기 때문입니다. 자가 대국 첫 60수 동안 재 보니 평균 37.3개였습니다.
처음에는 한 수에 1.5초를 쓰고도 두 수 앞밖에 못 봤습니다. 수를 하나 만들 때마다 판 전체를 훑어 궁이 잡히는지 검사했기 때문입니다. 탐색 중에는 그 검사를 빼고, 궁이 잡히는 가지는 점수로 걸러내게 바꿨습니다. 실제로 둘 수를 고를 때만 한 번 제대로 검사합니다.
네 단계
| 단계 | 읽는 깊이 | 실수 | 한 수에 보는 판 | 걸린 시간 |
|---|---|---|---|---|
| 쉬움 | 1수 | 35% 확률로 2~4등 수 | 34개 | 0.001초 |
| 보통 | 2수 | 15% 확률로 2~3등 수 | 1,240개 | 0.001초 |
| 어려움 | 4수 | 없음 | 69,883개 | 0.097초 |
| 최고수 | 4수 + 주고받기 끝까지 | 없음 | 547,021개 | 1.511초 |
깊이 하나로만 가르면 아래 두 단계가 똑같이 바보 같고 위 두 단계는 구분이 안 됩니다. 그래서 깊이와 실수 확률을 같이 움직였습니다. 실수는 아무 수나 두는 것이 아니라 2등이나 3등 수를 두는 것으로 제한했습니다.
최고수가 어려움에게 열두 판을 내리 졌습니다
단계를 다 만들고 인접한 두 단계를 자동으로 맞붙였습니다. 선후공을 번갈아 가며 열두 판씩 두게 했습니다.
| 대결 | 처음 결과 |
|---|---|
| 보통 vs 쉬움 | 12승 0패 (100%) |
| 어려움 vs 보통 | 12승 0패 (100%) |
| 최고수 vs 어려움 | 0승 12패 (0%) |
최고수가 전패했습니다. 난이도가 거꾸로였습니다. 화면으로는 네 단계 모두 멀쩡히 두는 것처럼 보였습니다.
원인 두 가지
첫째, 최고수는 시간 안에서 깊이를 1, 2, 3으로 올려 가며 읽습니다. 시간이 다 되면 중간에 멈추는데, 그때 얕게 때운 값으로 채워진 결과가 그 전까지 제대로 읽어 둔 결과를 덮어썼습니다. 깊이를 올리려는 시도가 오히려 수를 나쁘게 만들었습니다. 끝까지 못 읽은 깊이는 통째로 버리게 고쳤습니다. 승률이 0%에서 50%가 됐습니다.
둘째, 홀수 깊이에서 읽기를 끊는 것이 문제였습니다. 다섯 수를 읽으면 마지막이 내가 두는 차례라, 내가 기물을 잡는 장면까지만 보고 되잡히는 장면은 못 봅니다. 그래서 거저 던지는 수를 좋다고 봅니다. 읽는 깊이를 짝수로 맞추니 양쪽 수를 같은 만큼 읽게 됐습니다.
짝수로 맞추고 나니 최고수가 어려움과 5대 5가 됐습니다. 같은 1.5초 안에 깊이 6을 못 끝내서 결국 둘 다 깊이 4로 끝나기 때문이었습니다. 그래서 마지막 깊이에서 잡는 수만 더 따라가, 주고받기가 끝난 자리에서 점수를 매기게 했습니다.
| 고친 단계 | 최고수 vs 어려움 승률 |
|---|---|
| 처음 | 0% |
| 끝까지 못 읽은 깊이 버림 | 50% |
| 읽는 깊이를 짝수로 맞춤 | 100% |
| 자리 점수를 모든 단계에 적용 | 80% |
마지막에 80%로 내려온 것은 어려움도 같이 세졌기 때문입니다. 단계를 나눌 때 위가 아래를 70~80% 이기면 적당하다고 보고 있어서, 이 상태를 그대로 두었습니다.
명단에 올린 기보를 서버가 다시 둬 봅니다
전체 명단을 두면서 같이 풀어야 했던 문제가 있습니다. 화면에서 보낸 결과를 그대로 믿으면, 보내는 내용만 바꿔서 아무나 1등이 될 수 있습니다. 그러면 명단이 의미를 잃습니다.
그래서 이름을 올릴 때 결과만 보내지 않고 기보 전체를 보냅니다. 서버는 같은 규칙 엔진으로 빈 판에서 시작해 그 기보를 한 수씩 다시 둬 봅니다. 중간에 규칙에 안 맞는 수가 하나라도 있으면 거기서 멈추고 받지 않습니다.
| 보낸 것 | 서버가 하는 말 |
|---|---|
| 마지막 한 수를 뺀 기보 | 아직 안 끝난 판이에요 |
| 진 편으로 신고 | 이긴 판만 올릴 수 있어요 |
| 수를 아무렇게나 바꾼 기보 | 기보가 규칙에 안 맞아요 |
| 무르기를 쓴 판 | 무르기를 쓴 판은 올릴 수 없어요 |
| 진짜 이긴 판 | 올라갑니다 |
규칙 엔진이 화면과 완전히 분리되어 있어서 가능한 방법입니다. 같은 파일을 화면에서도 쓰고 서버에서도 씁니다. 규칙이 두 벌로 갈라져 서로 다르게 판단하는 일이 생기지 않습니다.
지금 상태
| 대결 | 10판 결과 |
|---|---|
| 보통 vs 쉬움 | 10승 0패 (100%) |
| 어려움 vs 보통 | 10승 0패 (100%) |
| 최고수 vs 어려움 | 8승 2패 (80%) |
아래 두 쌍은 아직 100%라 간격이 넓습니다. 쉬움과 보통 사이를 더 촘촘하게 만드는 것은 남은 숙제입니다.
상대의 생각은 화면과 분리된 별도 작업으로 돌립니다. 그래서 최고수가 1.5초를 쓰는 동안에도 화면이 멈추지 않습니다. 그 작업이 안 뜨는 환경에서는 화면 쪽에서 대신 계산하는데, 그때는 잠깐 멈춥니다.