Skip to content

Commit

Permalink
Added code analysis comments, collated structure with gitignore, and …
Browse files Browse the repository at this point in the history
…added local fake server mode.
  • Loading branch information
747929791 committed May 31, 2020
1 parent 123344e commit a2dd52b
Show file tree
Hide file tree
Showing 33 changed files with 410 additions and 4,184 deletions.
110 changes: 110 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Byte-compiled / optimized / DLL files
*/__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

#UltraEdit
*.bak

#AI
logger/log_jianyang_ai_1/
332 changes: 2 additions & 330 deletions README.md

Large diffs are not rendered by default.

Binary file removed agents/__pycache__/ai_interface.cpython-36.pyc
Binary file not shown.
Binary file removed agents/__pycache__/jianyang_ai.cpython-36.pyc
Binary file not shown.
Binary file removed agents/__pycache__/random_ai_example.cpython-36.pyc
Binary file not shown.
8 changes: 8 additions & 0 deletions agents/jianyang_ai.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from analysis.analyzer import *
import datetime
import os
import pickle
Expand Down Expand Up @@ -1330,6 +1331,7 @@ def expect_score(x_x):

class MLAI(AIInterface):

@LogTrace
def __init__(self, ensemble_clfs):
super().__init__()
self.ensemble_clfs = ensemble_clfs
Expand All @@ -1342,10 +1344,12 @@ def __init__(self, ensemble_clfs):
self.dis_funcs = {3: self._dis_3_st, 2: self._dis_2_st, 1: self._dis_1_st, 0: self._dis_1_st}
self.riichi_waiting = None

@LogTrace
def init_state(self):
super().init_state()
self.erase_states()

@LogTrace
def to_discard_tile(self):
if self.called_reach:
return self.tile_34_to_136(self.to_discard_after_reach)
Expand Down Expand Up @@ -1557,6 +1561,7 @@ def _can_discard_chr(self, candidate, hand_ana):
return False
return True

@LogTrace
def can_discard(self, t34, hand_ana):
if t34 > 26:
return self._can_discard_chr(t34, hand_ana)
Expand Down Expand Up @@ -1610,6 +1615,7 @@ def can_discard(self, t34, hand_ana):

return can_discard

@LogTrace
def can_call_reach(self):
"""
:return: False, 0 if can not call reach, else True, corresponding_to_be_discarded_tile
Expand All @@ -1630,6 +1636,7 @@ def can_call_reach(self):

return False, 0

@LogTrace
def should_call_kan(self, tile136, from_opponent):
tile34 = tile136 // 4
hand_ana = self._get_hand_ana()
Expand Down Expand Up @@ -1727,6 +1734,7 @@ def can_kan():

return False, False

@LogTrace
def try_to_call_meld(self, tile136, might_call_chi):
# check if bot can win this tile
if self.reach_status:
Expand Down
Binary file removed agents/utils/__pycache__/wait_calc.cpython-36.pyc
Binary file not shown.
Binary file removed agents/utils/__pycache__/win_calc.cpython-36.pyc
Binary file not shown.
51 changes: 51 additions & 0 deletions analysis/analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#coding=utf-8
from functools import wraps
import inspect
import sys

def LogTrace(func):
if '--stdio' in sys.argv:
LogTrace.out=sys.stdout
else:
LogTrace.out=open('FuncTrace.log','w')
LogTrace.FuncArgsList=[] #if func.name in this list, print args
print(inspect.getargspec(func))
@wraps(func)
def wrapper(*args,**kwargs):
if not hasattr(LogTrace,'level'):
LogTrace.level=0
LogLevel=''.join([' ' for i in range(LogTrace.level)])
LogStr=LogLevel+'I : '+func.__module__+'.'+func.__name__
try:
spec=inspect.getcallargs(func,*args,**kwargs)
if 'self' in spec:
spec.pop('self')
LogStr+=' '+str(spec)
except Exception:
LogStr+=' ParseArgError'
#LogTrace.out.write(LogStr+'\n')
#print(inspect.getargspec(func))
#print(inspect.getsource(func))
LogTrace.level+=1
ret = func(*args,**kwargs)
LogTrace.level-=1
LogStr=LogLevel+'O : '+func.__module__+'.'+func.__name__
try:

LogStr+=' '+str(ret)
except Exception:
LogStr+=' ParseRetError'
#LogTrace.out.write(LogStr+'\n')
LogTrace.out.flush()
return ret

return wrapper

def AnaLog(s,msg):
if not hasattr(AnaLog,'out'):
if '--stdio' in sys.argv:
AnaLog.out=sys.stdout
else:
AnaLog.out=open('AnaMsg.log','w')
AnaLog.out.write(s+' : '+str(msg)+'\n')
AnaLog.out.flush()
57 changes: 57 additions & 0 deletions analysis/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#coding=utf-8
from analyzer import *

class A():
@LogTrace
def __init__(self,b=2):
self.b=2
@LogTrace
def q(self,a):
return g(a)
def __call__(self):
print(self)

class B():
f=A()

@LogTrace
def f(a,b=0,*c,**d):
return g(a-1)+g(a-2)

@LogTrace
def g(a):
return h(a-1)+h(a-2)


@LogTrace
def p():
pass

@LogTrace
def h(a):
p()
if a<2: return 1
return h(a-1)+h(a-2)

def _decorator(foo):
def magic( self ) :
print("start magic")
foo( self )
print("end magic")
return magic

class Test(object):

@_decorator
def bar( self ) :
print("normal call")

test = Test()

test.bar()

if __name__=='__main__':
a=A()
a.q(5)
#print(f(5))
AnaLog(['123','456'])
104 changes: 104 additions & 0 deletions analysis/分析文档.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
包格式分析:

所有牌采用tile136格式传输,tile136//4对应tile34格式,其中AI讲将{16: 35, 52: 36, 88: 37}视为特殊的三个红宝牌(tile136:tile34)
传输给bot的所有牌tile136不能相同

LookingForAGame期间:
先send <JOIN lobby_type,game_type寻找房间 (lobby_type固定为'0')
<REJOIN : _send('<JOIN t="{}, r" />'.format(game_type))
<GO : _send('<GOK /') & _send('<NEXTREADY />') & parse_game_type & _parse_game_rule
<UN : parse_names_and_levels
n0 n1 n2 n3 : URL编码的自己开始逆时针4个用户名
dan="9,11,10,10" 从自己开始逆时针四个人的段位(9=1级,10=初段,11=二段)
<TAIKYOKU : parse_log_link 保存replay链接有关
<LN : _send(self._pxr_tag())

GameLoop在tenhou_client.continue_game,对_get的msg按\0分割后一次parse每个包的类型
<INIT : _handle_initial_msg
<REINIT : _handle_reconnection_msg
<T : 自己draw tile
<U <V <W : 逆时针顺序其余三家
<DORA : _handle_new_bonus_indicator
<REACH : _handle_reach_claim (step="1")
<AGARI : _handle_round_end
<RYUUKYOKU : _handle_liuju
<N who= : _handle_meld_call(msg, meld_tile)
TenhouParser.is_discard_msg : _handle_opponent_discard
owari : _handle_final_scores
<PROF : continue_game = False
msg has win_suggestions : _call_win
after draw:
win_suggestions = ['t="16"', 't="48"'] _send('<N type="7" />') #主动和牌
't="64"':九種九牌 _send('<N type="9" />') #主动流局

_handle_initial_msg :
format :
seed="round_number,honba_sticks,reach_sticks,?,?,bonus_tile_indicator1,bonus_tile_indicator2......."
ten="scores0,scores1,scores2,scores3"
oya="dealer"
hai="tile1,tile2,...,tile13"
'round_number': round_info[0], #0=东1局
'honba_sticks': round_info[1], #连庄棒
'reach_sticks': round_info[2], #立直棒
'bonus_tile_indicator': round_info[5:], #明宝牌
'dealer': dealer, #庄家
'scores': scores #四家分数
'tiles' : parse_initial_hand #起始手牌(tile136)

_handle_draw_tile :
format :
<T136\>
如果含有t="32"表示可以立直
TenhouParser.parse_tile :
ex. <E133/> -> 133 (r'^<[tefgEFGTUVWD]+\d*')
''

_handle_new_bonus_indicator :
format :
<DORA hai="tile136" />

_handle_reach_claim :
format :
<REACH who="0-3" step="1"/>

_handle_round_end :
format :
<AGARI ba="0,1" hai="4,7,8,12,16,18,23,27,69,70,71,88,89,90" machi="27" ten="50,8000,1" yaku="1,1,2,1,54,2,53,0" doraHai="50" doraHaiUra="46" who="1" fromWho="0" sc="250,-80,240,90,250,0,250,0" />
关键字段:who(胡牌者0-3),fromWho(点炮者0-3),machi(点炮牌),hai(胡牌者手牌),ten(符数?、点数、?)

_handle_liuju :
format :
<RYUUKYOKU ba="0,0" sc="170,10,410,10,250,10,170,-30" hai1="0,1,50,55,81,85,88" hai2="46,47,58,61,65,79,82" hai3="tile136..." />

_handle_opponent_discard :
format <--:
r"^<[defgDEFG]+\d*"
'd': 0, 'e': 1, 'f': 2, 'g': 3 小写字母座位顺序lower() ,小写为摸切,大写非摸切
包含"t="说明有吃碰杠操作
if t="3"' in msg or 't="7" in msg :bot可以选择杠牌
t="1" 可以碰 t="4" 可以吃
如果包含t=需要send<N\>包回复是否吃碰杠
format -->:
<N type="3" hai0="26" hai1="29" />
type=1表示碰操作,type=3表示吃操作
return -1 表示自己出牌或者杠
return -2 表示以缓存完毕无需其他操作
return >=0表示尝试吃碰,记录一下吃碰的牌等服务器返回<N\>结果

_handle_meld_call :
#某人成功吃碰杠
format <--:
<N who="0=3" m="(int)data"\>
有很复杂的编码,详见parser
如果是自己吃碰需要回复一个<D\>包指明要出的牌


send format :
<REACH hai="tile136"/> #宣告立直
<N type="5" hai="meld_tile136"/> #加杠
<N type="4" hai="meld_tile136"/> #暗杠
<N type="3" hai0="tile136" hai1="tile136" /> #回应吃
<N type="2" /> #回应明杠
<N type="1" hai0="tile136" hai1="tile136" /> #回应碰
<N\> #回应吃碰杠,不操作
<D p="tile136"/> #出牌
Binary file removed client/__pycache__/mahjong_meld.cpython-36.pyc
Binary file not shown.
Binary file removed client/__pycache__/mahjong_player.cpython-36.pyc
Binary file not shown.
Binary file removed client/__pycache__/mahjong_table.cpython-36.pyc
Binary file not shown.
Binary file removed client/__pycache__/mahjong_tile.cpython-36.pyc
Binary file not shown.
Binary file removed client/__pycache__/tenhou_client.cpython-36.pyc
Binary file not shown.
Binary file removed client/__pycache__/tenhou_parser.cpython-36.pyc
Binary file not shown.
Loading

0 comments on commit a2dd52b

Please sign in to comment.