第二の対ジョブ
始まりのリンク
序文
私は麻雀を好みます。
労働者の特定の部門
秋チャン傑:バックエンド
日チェン甲斐:フロントエンドは、
労働力の非常にシンプルな部門です。
PSPテーブル
PSP2.1 | パーソナルソフトウェアプロセス段階 | 推定時間がかかる(分) | 実際の時間がかかる(分) |
---|---|---|---|
プランニング | 計画 | 120 | 80 |
推計 | このタスクが必要とどのくらいの時間を見積もります | 120 | 80 |
開発 | 開発 | 2990 | 3330 |
分析 | (新しい技術を学ぶ含む)ニーズ分析 | 60 | 60 |
デザインスペック | 設計ドキュメントの生成 | 30 | 30 |
デザインレビュー | デザインレビュー | 30 | 30 |
標準コーディング | コードの仕様(開発のための適切な規範の開発) | 30 | 180 |
設計 | 具体的な設計 | 180 | 240 |
コーディング | 具体的なコーディング | 2500 | 2550 |
コードレビュー | コードレビュー | 60 | 120 |
テスト | 検査(セルフテスト、修正、変更をコミット) | 100 | 120 |
報告 | レポート | 80 | 100 |
試験報告書 | テストレポート | 30 | 30 |
サイズ測定 | コンピューティングのワークロード | 10 | 10 |
死後&プロセス改善計画 | 後知恵とは、プロセス改善計画を提案します | 40 | 60 |
トータル | 3190 | 3510 |
問題解決のためのアイデアの説明や解説の設計と実装
ネットワークインタフェースを使用します
次のように処理上の考慮事項のため、便宜上、上流のモジュール永福アクセスサーバからのpython安らかAPI要求を使用し、5つの機能で包装します。
インタフェース機能 | 機能 |
---|---|
ログインする | ログオン要求を送信します |
登録 | 登録要求を送信します |
find_info | 情報戦争の問い合わせ |
順位 | ランキングを見ます |
Srank | 戦争の個人的なリストへのアクセス |
内部コードの組織の設計と実現
支度をしています
我々が検討中のターゲットは現在、私たちの手を入れると13枚のカードを取得したときに最大のブランドを考えると、他の人が置くことができないなどの対象の設計プロセスの開始前に、私たちのチームは、いくつかの簡単な数学的分析を行います私たちは、現在のカードを置くよりも、より。この状態では、我々はその後、T / N、カードの種類がいくつかの他のカードタイプがトンで再生することができ入れ、カードタイプNの合計数を設定することができ、単にデッキの理想的な勝率考えることができます。
\ [C = \ FRAC {T} {N} \]
重みはシンプルに設定することができます
\ [\ LG(\ FRAC {N} {N T})= W]
また、考慮すべき相手の手の中に表示されますが、多くの場合ではない意思決定を行うためのカードの手だけを考慮するのに十分な、確率は私達のカード型の組み合わせよりも優れていることができ、我々はこの確率がSに設定されている参照してください。C×(1-S)は、比較的単純でデッキの成功率と考えることができます。
\ [P = C *(1-S)\]
多くの場合、Cが大きい場合、sの小さい、これは証明されていません。
ここでは、アルゴリズムの実装プロセスは、次のとおりです。
準備:底に時間を節約して、構築することができ利用できる図書館カードの書かれた基本的な枠組みを探して、ここにポーカーと呼ばれる非常に簡単なサードパーティのライブラリです。中国の文書、各カードの初期化の最も単純なを使用して。前端と後端のpythonを使用して達成されて、私はこれのPyQtでフロントページを達成するために前例のないHTML + CSSを使用した唯一の先端接触を書き込むために使用しました。
最初のステップ:事前にライセンス重みと各種製剤の種類ごとleveldictフラグを定義
レディ機能 | 機能 |
---|---|
make_suit | カラー辞書の現在のハンドを取得 |
make_rank | カード値のdictの現在のハンドを取得 |
Weight_init | 行列ブランドの重みの初期化 |
Times_init | 初期化好奇心行列の重み |
Compare_down | 二枚のカード最初の桟橋の大きさの比較 |
Compare_up | (注ぐかどうかをカード2 Sandunのデッキに相当する)は、2枚のカードの比較第二又は第三の桟橋桟橋サイズ |
Compare_gap | 水を注ぐ場合は第一及び第二桟橋桟橋のカードのデッキを比較 |
Compare_combo | 勝ち負けの計算完全に2枚のカード |
ヒント:そのような最初のカードタイプなどの量(に接続)は、第2のピア3第三のピア5(ストレート)の0埠頭(サン・カード)であり、重量[0] [3] [5]に初期化されます(0 + 3 + 5)/ 100そう他。各位置のための好奇心の重みは、それが使用されている場合、トレーニングによって、多分、その後、1に初期化されます。
ステップ2:実際DFSクラス、0-4は、3枚のカードを散乱する可能性があるすべての可能な(最初の桟橋グレードを通じて何の脳ではない、ストレートフラッシュに最初の0-9二桟橋散乱されるカードがあるかもしれませんSandunの共感)を描くことができるボードの現在の組成物の種類の5×10×10個の可能な組み合わせの合計
类一:choose_action_option(Hand,color_dict,rank_dict)
参数:手牌和前面两个函数得到的两个dict
功能:根据当前手牌给出各种牌型的dict
返回:
类内方法 | 功能 | 返回 |
---|---|---|
Flush_Collection | 判断同花顺 | 同花顺花色和该顺最后一张牌牌值的list |
Bomb | 判断炸弹 | 炸弹牌牌值的list |
Gourd | 判断葫芦 | 葫芦牌中三条部分和可能的所有对子组成的list |
Flush | 判断同花 | 同花的花色牌和能组成同花的5张牌中最大的牌值 |
Collection | 判断顺子 | 该顺最后一张牌牌值的list |
Three | 判断三条 | 三条牌牌值的list |
Pair | 判断对子 | 对子牌牌值的list |
Tips:此处后面为什么是函数而不是类了呢。。当然是因为方便调试!(懒得写了。函数比较方便。。)
主要函数 | 功能 | 返回 |
---|---|---|
choose_step_sb(choose_) | 根据类返回的dict返回所有可能的牌型 | 包含了0-9所有可能牌型的dict |
action_step_sb(Hand,choose_,shape,rank,suit) | 根据牌型dict对当前手牌进行更改 | 某墩的牌型和剩下的手牌 |
action_step_add(Hand_three,Hand_two,Hand_rest,shape,rank,suit) | 将三墩牌完善 | 包含了三墩牌和各自牌型大小的dict |
judge_right | 判断牌型的合理性 | True表示该三墩牌摆的合法 |
Tips:这里仅仅获得了手牌的一种排法的dict,包含了三墩的牌和各自的大小
第三步:由第二步得到的各种牌型可能训练权重
没错!这里我还是因为方便调试!只写了一个函数来训练它,甚至都没脸画表了!
主要函数:begin_game(myhand_need,Weight,Times,beta,is_Train,Auto,b,real)
参数 | 说明 |
---|---|
Weight | 牌型矩阵,决定了选某种牌型组合的可能性 |
Times | 好奇心の行列は、各ブランドのトレーニングが発生した回数を記録します |
ベータ | 私は頻繁に減少トレーニング時間のカードタイプについての好奇心のいくつかの種類を使用し、好奇心カットインデックスの名前 |
is_Train | それはトレーニングの状況、真の訓練であるかどうか、Falseのときに、現在可能な最大出力カードタイプ |
オート | トレーニングマニュアルまたは自動のトレーニング |
B | 他の人の場合は理事会、他の3枚のカードのis_Trainと比較した場合、そうでない場合はなし |
リアル | それは、実際のゲームシーンがあるかどうか |
開始するには精神遅滞からの訓練の最初のステップ、:法律上の最初の行は、第三埠頭から何の脳、無トリック脳の二大種類の最大値を取得しないための第二段階にする必要があり、他の3枚のカードをテーブルの上にカードです手の兄弟とすべての組み合わせによる無脳、自分自身との巨大な放電は勝ち負けがあるの重みを変更するには(おそらくバックと呼ばれるように値しない)へのご褒美の復帰に応じて、大幅に比較的大きな脳ずに排出することができます。
第二段階のトレーニング、IQの使用は、知的障害ビート:テーブルの上に、より合理的な重み行列を取得するための最初のステップの完了後にトレーニング、真=真の調整、他の3枚のカードがまだかなり大きいですが、彼らは既存のカードの種類に基づいています可能な最大放電結果は兄弟リターンでデッキとの比較を見つけるためにカード型の行列に行きます。
第三段階のトレーニング、IQビートIQの使用、4人が、あなたの手札からカードの右のタイプを探しているテーブルには、回帰を最大重み行列カードタイプを緩和することがあります。
ヒント:水の勝ち負けされる報酬
の戻り道を:
\ [R =(S)* \ FRAC {β}は{\ SQRT {N(S)}} \]
数r(S)すなわち、水を勝ち負け、β即ち好奇心低下指数、N(S)即ち、行列メモリタイムズ
実行されるアルゴリズムのフローチャートの説明のキーとキー部
2つの初期機能の辞書を介して得られた辞書とカラーカード値
各カードタイプの辞書を介して取得choose_step_sb(またはなし)
action_step_sbとaction_step_add振り子のメソッドを介してカードの完全なデッキを取得
この振り子方式の正当性を介して取得Judge_right
トレーニングを開始するには正当なことです。
前端と後端の残りの部分
次のようにメインページのフロントエンドが分割されます
インターフェイスと独立PY、統一されたファイルmain.py、mainBox単一多形主催ファイルに対応するクラス。
インターフェース | クラス名 |
---|---|
ログインして歓迎 | 指数 |
登録 | 登録 |
主なインタフェース | Naenindeksh |
クエリーインターフェイス | 調べる |
自动出牌结果界面 | ResultSingle |
战局信息界面 | Result |
个人中心 | Home |
排行榜 | Rank |
个人战绩 | SingleRank |
与后端的接口如下:
接口函数 | 功能 |
---|---|
login | 登陆验证 |
register | 注册 |
play | 进行自动游戏 |
find_infp | 查看战局信息 |
rank | 查看排行榜 |
srank | 查看个人记录 |
最终效果如下:
由于上传大小限制,拆分成5个部分来演示。
关键代码解释
算法
reward = 0
#得出手牌和牌桌其他人的输赢水
reward += Compare_Combo(finish_hand,b[0])
reward += Compare_Combo(finish_hand,b[1])
reward += Compare_Combo(finish_hand,b[2])
#好奇心衰减水
reward = reward * ((beta)/math.sqrt(Times[int(shape_1)][int(shape_2)][int(shape_3)]))
Times[int(shape_1)][int(shape_2)][int(shape_3)]+=1
#回归更改权重
Weight[int(shape_1)][int(shape_2)][int(shape_3)]+=(int(reward)/1000)
前端:
# 登陆界面切换主界面
ind.show_mainindex_sg.connect(show_mainindex)
# 登陆界面切换注册界面
ind.show_register_sg.connect(show_register)
# 注册界面切登陆界面
regind.register_ok_sg.connect(register_ok)
# 主界面切自动对战
mainind.auto_pressed_sg.connect(show_result)
# 主界面切用户中心
mainind.home_pressed_sg.connect(show_home)
# 主界面切搜索
mainind.search_pressed_sg.connect(show_search)
# 搜索切结果
searchind.search_sg.connect(show_id)
# 搜索返回
searchind.back_sg.connect(back_off)
# 自动对战返回
sresultind.result_exit_sg.connect(sresult_exit)
# 结果返回
resultind.result_exit_sg.connect(result_exit)
# 用户中心返回
homeind.home_exit_sg.connect(home_exit)
# 用户中心切排行榜
homeind.rank_sg.connect(show_rank)
# 用户中心切个人战绩
homeind.single_rank_sg.connect(show_single_rank)
# 排行榜返回
rankind.rk_comeback_sg.connect(rank_exit)
# 个人战绩返回
sgrankind.single_rk_comeback_sg.connect(single_rank_exit)
# 详情
sgrankind.detail_sg.connect(show_de)
sys.exit(app.exec())
后端:
def play(token):
for i in range(1):
try:
url = "https://api.shisanshui.rtxux.xyz/game/open"
headers = {'x-auth-token': token}
response = requests.request("POST", url, headers=headers)
result = response.text.encode("utf8")
result = json.loads(result)
print(result)
id = result['data']['id']
card = result['data']['card']
card = card.replace("10", 'T')
card = card.replace("*", '@')
hand_card = card.split()
url = "https://api.shisanshui.rtxux.xyz/game/submit"
Weight = pickle.load(open('./resource/model/a.txt', 'rb'))
Times = pickle.load(open('./resource/model/b.txt', 'rb'))
beta = 0.9
myhand = []
for i in range(13):
myhand.append(Card(str(hand_card[i][1]) + str(hand_card[i][0])))
print(myhand)
myhand.sort()
b = Game.begin_game(myhand, Weight, Times, beta, is_Train=False, Auto=False, b=None, real=True)
three = b['level_3'][0]
shape_3 = b['level_3'][1]
two = b['level_2'][0]
shape_2 = b['level_2'][1]
one = b['level_1'][0]
shape_1 = b['level_1'][1]
a = []
c = ''
for i in range(3):
c = c + str(one[i])[1] + str(one[i])[0]
if (i != 2):
c += ' '
c = c.replace("T", '10')
c = c.replace("@", '*')
a.append(c)
c = ''
for i in range(5):
c = c + str(two[i])[1] + str(two[i])[0]
if (i != 4):
c += ' '
c = c.replace("T", '10')
c = c.replace("@", '*')
a.append(c)
c = ''
for i in range(5):
c = c + str(three[i])[1] + str(three[i])[0]
if (i != 4):
c += ' '
c = c.replace("T", '10')
c = c.replace("@", '*')
a.append(c)
payload = str({'id': id, 'card': a})
payload = payload.replace(': ', ':')
payload = payload.replace(', ', ',')
payload = payload.replace("'", '"')
headers = {
'content-type': "application/json",
'x-auth-token': token
}
response = requests.request("POST", url, data=payload, headers=headers)
result = response.text.encode("utf8")
result = json.loads(result)
status = result['status']
msg = result['data']['msg']
if (msg != 'Success'):
print("11111111111")
print(myhand)
print(three)
print(two)
print(one)
break
flag_hand = []
for i in range(13):
f = str(myhand[i])
q = str(f[1]) + str(f[0])
q = q.replace("T", '10')
flag_hand.append(q)
except:
need = {'status': 1}
print(need)
return need
need = {'status': status, 'id': id, 'msg': msg, 'origin_cards': flag_hand, 'cards': []}
flag_one = []
for i in range(3):
a = str(one[i])
b = a[1] + a[0].replace("T", '10')
flag_one.append(b)
flag_two = []
for i in range(5):
a = str(two[i])
b = a[1] + a[0].replace("T", '10')
flag_two.append(b)
flag_three = []
for i in range(5):
a = str(three[i])
b = a[1] + a[0].replace("T", '10')
flag_three.append(b)
flag = {'lv': level_dict[str(shape_1)], 'card': flag_one}
need['cards'].append(flag)
flag = {'lv': level_dict[str(shape_2)], 'card': flag_two}
need['cards'].append(flag)
flag = {'lv': level_dict[str(shape_3)], 'card': flag_three}
need['cards'].append(flag)
print(need)
return need
性能分析与改进
描述你改进的思路
1.最开始的回归方式是单纯根据输赢水/100,这样可能会使一些很少遇到的情况出现偏差,它本来应该比另外一种经常出现的大,可经常出现的牌型出现的次数过多导致过分更改它的权重产生误判,所以使用了最近用来解决RL中sparse reward问题的好奇心的方法(其实就是个最简单的计数)。
2.因为由表得出make_suit也就是遍历得到花色dict的函数是最耗时的,估计因为是每次手牌的预处理都有用到它,所以第一墩13张牌时仍让它遍历,第二墩8张牌时就从先前的dict减去少掉5张牌的花色,第三墩类似,会稍微减少一点时间
3.在训练完十万个单位后,为了减少训练时间,不用让它遍历每种情况,只要是它经过的情况都用一个矩阵来标1,下次就不用再次访问同样牌型的情况
4.前端则是由于py的特性,导致本身启动时间较慢,唯一想到的解决方法是减少初始加载的模块数或者用c编写系统接口。
5.只有有网络请求,则网络请求部分往往是耗时最大的,该次作业也是一样。
展示性能分析图和程序中消耗最大的函数
万万没想到居然是make_suit。这玩意不就遍历个手牌把花色记录下来居然花这么多时间。惊了。
单元测试
覆盖率
poker是用来初始化卡牌的基础库,这个就不关键了,可见控制出牌发牌逻辑的Game的覆盖率是87,虽然低但还算勉强。
Github的代码签入记录。
Github readme
遇到的代码模块异常或结对困难及解决方法
问题描述(2分)
1.手牌合法性检测
2.因为是遍历手牌牌型的所有情况,两种情况的两副牌有可能出现某一墩完全一样的情况,无法比较大小。
3.底层poker库的Card类型和接口提供的卡牌样式完全不同,本身花色类型和前端接口也是八字不合,会出现❤这种无法保存的现象
4.网络请求失败导致整个程序崩溃。
5.多界面切换的挂起导致的内存损耗。
6.队友的屁股真的翘
做过哪些尝试(2分)
1.本来是想写一个判断手牌牌型和最大牌大小的顺便也方便前端,但因为后面训练的时候需要进行两副牌输赢水的计算,干脆就把每墩之间的比较独立写出,若合法情况则是第三墩赢第二墩,第二墩赢第一墩,这种牌型就是合法的。
2.根据鸵鸟算法,那么我们就把它........直接返回不输不赢吧
3.手动更改底层库
4.增加异常处理
5.修改控件件的父子关系。
是否解决(2分)
1.解决了
2.根据鸵鸟算法,解决了
3.解决了,哭了
4.解决了
5.不完美解决
有何收获(2分)
1.大千世界无奇不有,换种思路展开新世界
2.操作系统教会了我们非常关键的一种算法,使我受益良多
3.在开展项目前因先沟通好前后端的接口设计问题,同时将功能合理区分开,降低耦合度。
4.完成不使用ps纯手码前端成就。
评价你的队友
值得学习的地方(2分)
1.你是小队之光
2.你是天选之子
3.你是人工智障带师
4.你是力量的象征
5.敏捷,果敢
6.速度很快
7.屁股真的翘
需要改进的地方(2分)
1.别用jupyter开发这种项目了,会死人了。
学习进度条
学习表
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 407 | 407 | 15 | 15 | 学到了十三水的玩法,了解了原型设计工具的使用方法 |
2 | 230 | 637 | 14 | 14 | 制定了基本实现思想和前端窗体框架 |
3 | 1200 | 1837 | 20 | 20 | 实现基本交互逻辑 |
4 | 1045 | 2882 | 20 | 20 | 实现接口对接和后端处理 |
牢骚
单说算法,采用这样一个算法其实只是个尝试,十三水这个游戏本身的决策不算复杂,单一情况下的所有解也比较的少,正确率最高的解法应该还是根据胜率去穷举最好,在这方面ai所体现的优势便没有那么明显,当然本身训练过程也发现某些牌型出现的情况较少,导致对其的权重较低,而往往出现概率小的牌是更大的牌,这就很危险了,所以在开始引入数学模型的基础权值再进行调整。还是偶尔能看到一些权值系统下天马行空的想法的。
再说设计本身,我们接口的设计比较的简洁,也减少了各自开发的难度,各司其职,干扰较少。前端用了PyQt,emmm,怎么说,有优有劣,我可能会更喜欢html+css的组合,qss本身作为类似css的样式表,其选择器和功能都些许不如css,总之还是不错的,曾经几度按下自己想打开ps的手,最后还是去画了一张背景出来。