RUST 4. Implementing Poker Analysis Library (Rust Part)
안녕하세요.
이번에는 포커 토너먼트 분석툴을 만들어본 경험을 풀어보려고 합니다. 제 포커 토너먼트 분석툴은 GGNetwork의 Pokercraft에서 다운로드 받을 수 있는 데이터를 분석하는 툴로, 그 툴로 만든 분석파일들은 다음과 같습니다.
취미 프로젝트 치고는 코딩을 꽤 많이 한 편이라, 글로 풀어보고 싶었습니다. 이 글에서는 그 중 Rust와 연관이 있는 부분만 골라서 풀어보겠습니다.
Parallel computing¶
러스트 모듈에 있는 거의 모든 헤비한 연산들은
rayon
의 병렬 컴퓨팅을 활용하도록 구현되었습니다.
이 과정에서 &mut T
같은 변수들을 for_each
의 closure에 캡쳐할 수 없는 등등의 삽질을 겪었네요.
// Code provided by Google AI
use rayon::prelude::*;
fn main() {
let mut total = 0;
let numbers = vec![1, 2, 3, 4, 5];
// This will result in a compile error
numbers.par_iter()
.for_each(|&x| {
total += x; // ERROR: Cannot capture `total` as mutable
});
}
Bankroll analysis¶
뱅크롤 분석을 Python 단에서 진행하기에는 연산량이 너무 많았기 때문에 Rust 모듈에서 구현했습니다.
All-in equity calculation¶
포커에서 올인을 하게 되면, 정확한 결과값을 계산하는 방법은 가능한 모든 보드에 대해서 승패를 시뮬레이션을 하는 것입니다. 프리플랍 헤즈업 올인의 경우, 가능한 보드의 개수는 약 171만개입니다. 병렬 컴퓨팅을 활용하긴 하는데, 그렇게 하더라도 여전히 프리플랍 올인을 매번 계산하는 것은 정말 빡세기 때문에, 최소한 HU(헤즈업) 시나리오에 대해서는 cache를 만들기로 했습니다.
HU preflop cache를 만들 때 suit-symmetric한 시나리오는 제외하고 계산하기로 했습니다. 예를 들어서 AsKs vs QsJs의 확률은 AdKd vs QdJd의 확률과 동일합니다. 이런 중복된 시나리오는 제거하는 것입니다.
References
계산된 cache는 여기에서 다운받으실 수 있습니다.
.gz
압축을 해제하면 .txt
파일이 나오는데,
매 라인마다 "핸드1 vs 핸드2 = 핸드1승리횟수 핸드2승리횟수 무승부횟수"가 써져 있습니다.
Scientific libraries¶
FFT, Gaussian CDF 역함수 계산 등을 할 때 이미 구현된 라이브러리들을 가져와서 썼습니다.
Self utilities¶
Iterator wrapper¶
Box<dyn Iterator<Item = T>>
는 rayon::iter::ParallelBridge
를 impl하지 않습니다.
그래서 임의의 iterator를 래핑하는 struct를 구현했습니다.
Fixed sized combination¶
itertools::combinations
는 iterator element type이 Vec<T>
입니다.
저는 rayon
을 통해 실행되는 매 루프에서 작은 allocation이 자주 일어나지 않기를 원했습니다.
그래서 그런 코드를 작성하는 과정에서 FixedSizedCombinationIterator<T, K>
를 만들었습니다.
이 struct는 iterator element type이 [T; K]
입니다.
다른 파트들은 다음 글에서 읽으실 수 있습니다.
글을 읽어주셔서 감사합니다.