이것은 내 석사 논문을 위한 프로젝트입니다. 강력한 마작 AI를 만드는 데 관심이 있는 사람이라면 누구나 내 에이전트를 확장할 수 있습니다. 적용되는 알고리즘과 해당 알고리즘을 사용하는 이유에 대한 자세한 내용은 E-Mail로 문의해 주시기 바랍니다.
자신만의 마작 에이전트를 개발하려는 경우 이 Repo를 실시간 테스트( 실제 플레이어 사용 )용 프레임워크로 사용할 수도 있습니다. 그러면 에이전트에 가장 적합한 전략을 찾는 데 드는 모든 시간을 절약할 수 있습니다. 또한 개발 라이브러리(게임 로그 크롤링 및 전처리, 샨틴 및 승리 점수 계산 등)를 이제 https://github.com/erreurt/Mah JongKit에서 사용할 수 있습니다.
다음 업데이트가 곧 제공됩니다 :
더 나은 기능 엔지니어링이 필요합니다. Meld/Call Riichi를 호출할지 또는 Random Forest를 사용하지 않을지(조건 규칙 대신) 결정하십시오.
진행 중 : LSTM을 이용한 대기 타일 예측 모델 학습.
주의 : 동일한 IP 주소에서 4개 이상의 계정으로 봇을 병렬로 테스트하는 경우, 귀하의 IP 주소는 tenhou.net에 의해 24시간 동안 차단됩니다. (플레이어 금지 규칙에 대해 정확히 알지 못하지만 그것은 모두 내 관찰에서 추론됩니다.)
작가 | Jianyang Tang (토마스) |
---|---|
이메일 | [email protected] |
마작은 정보가 불완전한 4인용 전략 게임입니다. 지능형 마작 에이전트 개발의 주요 과제는 복잡한 게임 규칙, 방대한 검색 공간, 다수의 상대 및 불완전한 정보 등입니다. 기존의 여러 연구에서는 Monte Carlo 트리 시뮬레이션, 지도 학습을 통한 유틸리티 함수 피팅 또는 회귀 알고리즘을 통한 상대 모델을 통해 이러한 문제를 해결하려고 시도했습니다. 그럼에도 불구하고 지능적인 마작 플레이 에이전트의 성능은 여전히 최고의 인간 플레이어의 성능과는 거리가 멀습니다. 본 연구에서는 마작게임에 대한 통계분석과 인간의 전문지식을 바탕으로 지능형 마작 에이전트를 제안하였다. 최첨단 작업의 문제를 해결하기 위해 휴리스틱 기술과 다층 퍼셉트론의 배깅을 채택하여 향상된 상대 모델이 이 작업에 적용되었습니다. 실험을 통해 제안된 에이전트가 최신 에이전트보다 성능이 뛰어나며, 적용된 상대 모델은 에이전트의 성능에 유의미한 긍정적인 영향을 미치는 것으로 나타났습니다. 또한, 실험에서 몇 가지 흥미로운 점을 확인할 수 있으며 이는 향후 작업에 매우 의미가 있습니다.
일본 리치 마작의 게임 규칙은 https://en.wikipedia.org/wiki/일본어_마작을 참조하세요.
구현된 클라이언트를 사용하면 웹 브라우저에서 수행하는 대신 프로그램을 통해 직접 마작 에이전트를 실행할 수 있습니다. 일본 리치 마작 온라인 플레이 사이트는 http://tenhou.net/
왼쪽은 일본 리치 마작 테이블의 전형적인 풍경입니다. 이 그림은 디버깅용으로 구현된 GUI의 스크린샷입니다.
제안된 마작 에이전트는 tenhou.net에서 테스트되었습니다. 테스트는 두 가지 버전, 즉 방어 모델이 있는 버전과 없는 버전으로 수행되었습니다. 원시 게임 로그와 중간 게임 결과는 내 다른 저장소인 https://github.com/erreurt/Experiments-result-of-mah Jong-bot에서 찾을 수 있습니다. 실험은 Experiment_ai.py 의 에이전트 버전으로 수행되었습니다.
방어모델이 있는 버전은 526게임, 방어모델이 없는 버전은 532게임을 플레이하였다. 이는 관련 작품 2개만큼 많지는 않지만, 에이전트 성능의 융합 행동 그림에서 볼 수 있듯이 526게임이면 충분하다.
미즈카미의 확장된 작업은 현재 영문학계에서 가장 훌륭하고 신뢰할 수 있는 마작 에이전트로 볼 수 있습니다. 다음은 내 마작 에이전트의 성능과 Mizukami 에이전트의 성능을 비교한 것입니다.
[1] | [2] | [3] | [4] | |
---|---|---|---|---|
플레이한 게임 | 526 | 532 | 2634 | 1441 |
1위 비율 | 23.95% | 22.65% | 24.10% | 25.30% |
2위 비율 | 26.62% | 25.92% | 28.10% | 24.80% |
3위 비율 | 31.75% | 25.71% | 24.80% | 25.10% |
4위 비율 | 17.68% | 25.71% | 23.00% | 24.80% |
승률 | 24.68% | 26.50% | 24.50% | 25.60% |
손실률 | 13.92% | 20.21% | 13.10% | 14.80% |
고정 레벨 | 2.21 단 | 0.77 단 | 1.14 단 | 1.04 단 |
[1] 방어 모델을 갖춘 나의 마작 에이전트
[2] 방어 모델이 없는 나의 마작 에이전트
[3] Mizukami의 확장 작업 : Mizukami N., Tsuruoka Y.. 몬테카를로 시뮬레이션과 상대 모델을 기반으로 컴퓨터 마작 플레이어 구축. In: 2015 IEEE 컴퓨터 지능 및 게임(CIG) 회의, pp. 275–283. IEEE(2015)
[4] 미즈카미 외. 알. : N. Mizukami, R. Nakahari, A. Ura, M. Miwa, Y. Tsuruoka 및 T. Chikayama. 격리된 멀티 플레이어 측면의 지도 학습을 통해 4인 컴퓨터 마작 프로그램을 실현합니다. 일본정보처리학회논문집, vol. 55, 아니. 11, pp. 1–11, 2014, (일본어).
마작을 플레이하는 동안 4위로 떨어지는 것은 레벨 포인트가 감소하기 때문에 확실히 금기시됩니다. 따라서 플레이어의 4위로 떨어지는 비율은 전체 성능에 매우 중요합니다. 내 봇은 낮은 4위 비율로 인해 더 나은 고정 레벨을 가지고 있습니다.
마작 에이전트를 실행하려면 몇 가지 구성을 지정해야 합니다. main.py의 다음 예에서 볼 수 있듯이:
def run_example_ai ():
ai_module = importlib . import_module ( "agents.random_ai_example" )
ai_class = getattr ( ai_module , "RandomAI" )
ai_obj = ai_class () # [1]
player_module = importlib . import_module ( "client.mahjong_player" )
opponent_class = getattr ( player_module , "OpponentPlayer" ) # [2]
user = "ID696E3BCC-hLHNE8Wf" # [3]
user_name = "tst_tio" # [4]
game_type = '1' # [5]
logger_obj = Logger ( "log1" , user_name ) # [6]
connect_and_play ( ai_obj , opponent_class , user , user_name , '0' , game_type , logger_obj ) # play one game
def run_jianyang_ai ():
ai_module = importlib . import_module ( "agents.jianyang_ai" )
waiting_prediction_class = getattr ( ai_module , "EnsembleCLF" )
ensemble_clfs = waiting_prediction_class ()
ai_class = getattr ( ai_module , "MLAI" )
ai_obj = ai_class ( ensemble_clfs ) # [1]
opponent_class = getattr ( ai_module , "OppPlayer" ) # [2]
user = "ID696E3BCC-hLHNE8Wf" # [3]
user_name = "tst_tio" # [4]
game_type = '1' # [5]
logger_obj = Logger ( "log_jianyang_ai_1" , user_name ) # [6]
connect_and_play ( ai_obj , opponent_class , user , user_name , '0' , game_type , logger_obj )
AI 인스턴스 : 마작 에이전트의 클래스 인스턴스입니다. 이 저장소에는 세 가지 버전의 Mah Jong 에이전트가 제공됩니다. 첫 번째는 Agents.random_ai_example.py 에 있으며, 이는 잠재 개발자에게 자신의 에이전트를 구현하는 방법을 보여주는 데모 클래스입니다. 두 번째는 Agents.experiment_ai.py 에 있으며 4부에서 주어진 실험 결과는 이 AI에 의해 생성됩니다. 세 번째는 최신 AI이며 Agents.jianyang_ai.py 에 있습니다.
상대 플레이어 클래스 : 상대 플레이어의 클래스입니다. client.mah Jong_player 에서 기본 클래스 OppontPlayer를 사용할 수 있습니다. 추가 요구 사항으로 인해 OppontPlayer 클래스를 확장한 경우 이 변수를 해당 클래스로 설정해야 합니다.
User ID : tenhou.net에 등록 후 받은 예시와 같은 형태의 토큰입니다. 주의: 본인의 사용자 ID를 사용하시기 바랍니다. 동일한 ID를 다른 IP 주소로 너무 자주 사용하는 경우 tenhou.net에서 해당 계정을 일시적으로 차단합니다.
사용자 이름 : tenhou.net에 등록할 때 생성한 해당 사용자 이름입니다. 이 변수는 테스트 로그를 식별하는 용도로만 사용됩니다.
게임 유형 : 게임 유형은 8비트 정수로 인코딩됩니다. 각 비트에 대한 설명은 다음과 같습니다.
예를 들어:
- Tenhou.net does not provide all possibility of the above specified combinations. Most online players play on configurations for example "1", "137", "193", "9"
로거 : 로거를 초기화하려면 두 가지 매개변수가 필요합니다. 첫 번째는 사용자가 정의한 로거의 ID로, 개발자가 자신의 테스트 기록 이름을 자유롭게 지정할 수 있습니다.
이러한 구성을 모두 지정한 후 모든 매개변수를 connect_and_play() 에 전달하세요. 그렇다면 이제 마작 에이전트의 쇼를 시청할 시간입니다!!!
Agents.ai_interface 의 "인터페이스" 클래스에 표시된 대로 마작 봇에 대해 네 가지 기능을 구현해야 합니다. 에이전트가 AIInterface를 상속받는 것이 좋습니다. 이러한 함수에 대한 더 자세한 설명과 간단한 예를 보려면 Agents.random_ai_example.py 의 설명서를 참조하세요.
class AIInterface ( MainPlayer ):
def to_discard_tile ( self ):
raise NotImplementedError
def should_call_kan ( self , tile136 , from_opponent ):
raise NotImplementedError
def try_to_call_meld ( self , tile136 , might_call_chi ):
raise NotImplementedError
def can_call_reach ( self ):
raise NotImplementedError
to_discard_tile : 게임 상태에 대해 접근 가능한 모든 정보를 기반으로 이 함수는 삭제할 타일을 반환합니다. 반환값은 0-135 범위의 정수입니다. 마작 게임에는 총 136개의 타일이 있습니다. 즉, 34가지 종류의 타일과 각 종류별로 4개의 복사본이 있습니다. 경우에 따라 34 형식(각 숫자는 한 종류의 타일에 해당) 또는 136 형식(각 숫자는 타일에 해당)을 사용합니다. 여기서 반환은 136 형식이어야 합니다.
should_call_kan : https://en.wikipedia.org/wiki/일본어_마종#Making_melds_by_calling. 이 함수는 에이전트가 kan(Quad) 멜드를 호출해야 하는지 여부를 결정해야 합니다. Tile136은 상대방이 버린 타일을 나타내며, 에이전트가 칸 융합을 형성하는 데 사용할 수 있습니다. from_oppoint는 에이전트가 상대의 폐기(손에 타일 3개, 상대가 네 번째 타일 버리기) 또는 자신의 타일(손에 있는 타일 4개 모두)로 칸 융합을 형성하는지 여부를 나타냅니다.
try_to_call_meld : https://en.wikipedia.org/wiki/일본어_마종#Making_melds_by_calling. 이 함수는 에이전트가 Pon(Triplet)/Chi(Sequence) 멜드를 호출해야 하는지 여부를 결정합니다. Tile136은 일부 상대가 버린 136형 타일을 나타냅니다. might_call_chi는 에이전트가 Chi 멜드를 호출할 수 있는지 여부를 나타냅니다. Chi 멜드는 왼쪽 자리에 있는 상대를 버린 경우에만 호출할 수 있기 때문입니다.
can_call_reach : https://en.wikipedia.org/wiki/일본어_마종#R%C4%ABchi. 이 기능은 에이전트가 Riichi를 청구해야 하는지 여부를 결정합니다.
마작 에이전트 클래스가 AIInterface 클래스의 하위 클래스인 경우, 지정된 대로 에이전트 클래스 내부에서 다음과 같은 정보에 접근할 수 있습니다.
입장 | 데이터 유형 | 변하기 쉬운 | 설명 |
---|---|---|---|
self.tiles136 | 정수 목록 | 와이 | 136형 핸드타일 |
자기.손34 | 정수 목록 | N | 34형식의 손 타일(tile34 = Tile136//4) |
self.discard136 | 정수 목록 | 와이 | 136-from에서 에이전트의 폐기 |
self.discard34 | 정수 목록 | N | 34 형식의 에이전트 폐기 |
self.meld136 | Meld 인스턴스 목록 | 와이 | 에이전트의 호출된 멜드, client.mah Jong_meld.py 의 Meld 클래스 인스턴스 |
self.total_melds34 | 정수 목록의 목록 | N | 34형식의 에이전트의 호출된 멜드 |
self.meld34 | 정수 목록의 목록 | N | 34 형식의 에이전트의 폰/차우 멜드라고 합니다. |
자기.pon34 | 정수 목록의 목록 | N | 34형식의 에이전트의 폰 멜드라고 합니다. |
자기.chow34 | 정수 목록의 목록 | N | 34 형태의 에이전트의 콜드 차우 멜드 |
self.minkan34 | 정수 목록의 목록 | N | 34형식의 에이전트의 민칸 멜드라고 합니다. |
self.ankan34 | 정수 목록의 목록 | N | 34형식의 에이전트의 안칸 멜드라고 합니다. |
자기 이름 | 끈 | 와이 | 계정 이름 |
자체 수준 | 끈 | 와이 | 계정의 레벨 |
셀프.좌석 | 정수 | 와이 | 좌석 ID, 상담원은 항상 0입니다. |
self.dealer_seat | 정수 | 와이 | 딜러의 좌석 ID |
self.is_dealer | 부울 | N | 대리인이 딜러인지 아닌지 |
self.reach_status | 부울 | 와이 | 에이전트가 Riichi를 주장했는지 여부를 나타냅니다. |
self.just_reach() | 부울 | N | 에이전트가 리치를 주장했는지 여부 |
self.tmp_rank | 정수 | 와이 | 현재 게임의 에이전트 순위 |
자기.점수 | 정수 | 와이 | 현재 게임의 에이전트 점수 |
self.is_open_hand | 부울 | N | 에이전트가 이미 오픈 멜드를 호출했는지 여부 |
self.turn_num | 정수 | N | 현재 턴의 수 |
self.player_wind | 정수 | N | 플레이어 바람은 야쿠의 일종이다 |
self.round_wind | 정수 | N | 둥근 바람은 야쿠의 일종이다 |
self.bonus_honors | 정수 목록 | 와이 | 야쿠가 있는 모든 캐릭터 타일 |
상대의 해당 ID를 나타내는 i가 1,2,3인 self.game_table.get_player(i)를 호출하여 상대 클래스의 인스턴스에 액세스할 수 있습니다.
입장 | 데이터 유형 | 변하기 쉬운 | 설명 |
---|---|---|---|
.discard136 | 정수 목록 | 와이 | 136에서 관찰된 상대의 폐기 |
.discard34 | 정수 목록 | N | 관찰된 상대가 버린 34형식 |
.meld136 | Meld 인스턴스 목록 | 와이 | 관찰된 상대의 호출된 멜드 |
.total_melds34 | 정수 목록의 목록 | N | 관찰된 상대의 콜드멜드를 34형식으로 |
.meld34 | 정수 목록의 목록 | N | 34 형태의 관찰된 상대의 호출된 폰/차우 융합 |
.pon34 | 정수 목록의 목록 | N | 34형식으로 관찰된 상대의 폰 멜드를 호출합니다. |
.chow34 | 정수 목록의 목록 | N | 34 형태로 관찰된 상대의 콜드 차우 융합 |
.minkan34 | 정수 목록의 목록 | N | 34형식으로 관찰된 상대의 호출된 민칸 융합 |
.ankan34 | 정수 목록의 목록 | N | 관찰된 상대의 호출된 안칸이 34형으로 결합됩니다. |
.safe_tiles | 정수 목록 | 와이 | 에이전트에게 절대적으로 안전한 34형 타일, 즉 관찰된 상대는 이 타일로 승리할 수 없습니다. |
.이름 | 끈 | 와이 | 상대방의 이름 |
.수준 | 끈 | 와이 | 상대의 레벨 |
.좌석 | 정수 | 와이 | 관찰된 상대의 좌석 ID |
.dealer_seat | 정수 | 와이 | 딜러의 좌석 ID |
.is_dealer | 부울 | N | 관찰한 상대가 딜러인지 아닌지 |
.reach_status | 부울 | 와이 | 관찰된 상대가 리치를 주장했는지 여부를 나타냅니다. |
.just_reach() | 부울 | N | 관찰된 상대가 방금 리치를 주장했는지 여부 |
.tmp_rank | 정수 | 와이 | 현재 게임에서 관찰된 상대의 순위 |
.점수 | 정수 | 와이 | 현재 게임에서 관찰된 상대의 점수 |
.is_open_hand | 부울 | N | 관찰된 상대가 이미 오픈 멜드를 콜했는지 여부 |
.turn_num | 정수 | N | 현재 턴의 수 |
.player_wind | 정수 | N | 플레이어 바람은 야쿠의 일종이다 |
.round_wind | 정수 | N | 둥근 바람은 야쿠의 일종이다 |
.bonus_honors | 정수 목록 | 와이 | 야쿠가 있는 모든 캐릭터 타일 |
게임 테이블에 대한 정보에 액세스하려면 self.game_table을 호출하면 됩니다.
입장 | 데이터 유형 | 변하기 쉬운 | 설명 |
---|---|---|---|
.bot | 에이전트 클래스의 인스턴스 | 와이 | 에이전트의 클래스 인스턴스 |
.get_player(i) | 상대 클래스의 인스턴스 | 와이 | 상대방의 클래스 인스턴스, i=1,2,3 |
.dealer_seat | 정수 | 와이 | 딜러의 좌석 ID |
.bonus_indicator | 정수 목록 | 와이 | 136형식의 보너스 지표 |
.round_number | 정수 | 와이 | 어림수 |
.reach_sticks | 정수 | 와이 | 테이블 위에 리치 스틱이 몇 개 있습니까? 다음에 승리하는 플레이어는 리치 스틱의 모든 포인트를 받게 됩니다. |
.honba_sticks | 정수 | 와이 | 테이블 위에 혼바 막대기가 몇 개 있습니까? 플레이어가 이기면 혼바 스틱에 따라 추가 점수를 받게 됩니다. |
.count_ramaining_tiles | 정수 | 와이 | 아직 풀리지 않은 남은 타일의 수 |
.노출된 | 정수 목록 | 와이 | 목록의 각 요소는 이 특정 타일의 복사본이 몇 개나 공개되었는지 나타냅니다(폐기, 열린 혼합, 보너스 표시 등). |
.round_win | 정수 | N | 둥근 바람은 야쿠의 일종이다 |
.bonus_tiles | 정수 목록 | N | 보너스 타일 목록. 손 타일에 보너스 타일이 나타날 때마다 야쿠로 간주됩니다. |
.last_discard | 정수 | N | 상대방이 가장 최근에 버린 타일입니다. 이 타일은 규칙에 따라 절대적으로 안전합니다. |
나의 교수님인 Johannes Fürnkranz (TU Darmstadt Knowledge Engineering Group)
내 상사 Tobias Joppen(TU Darmstadt Knowledge Engineering Group)