ffzeroHua commited on
Commit
fe06770
·
verified ·
1 Parent(s): 4b4b36f

Upload MjaiToPaiju.py

Browse files
Files changed (1) hide show
  1. MjaiToPaiju.py +258 -0
MjaiToPaiju.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+
3
+ tiles_tenhou = {
4
+ '1m': 0, '2m': 1, '3m': 2, '4m': 3, '5m': 4, '5mr': 4.5, '6m': 5, '7m': 6, '8m': 7, '9m': 8,
5
+ '1p': 9, '2p': 10, '3p': 11, '4p': 12, '5p': 13, '5pr': 13.5, '6p': 14, '7p': 15, '8p': 16, '9p': 17,
6
+ '1s': 18, '2s': 19, '3s': 20, '4s': 21, '5s': 22, '5sr': 22.5, '6s': 23, '7s': 24, '8s': 25, '9s': 26,
7
+ 'E': 27, 'S': 28, 'W': 29, 'N': 30, 'P': 31, 'F': 32, 'C': 33, '?': 34
8
+ }
9
+
10
+ def process_mjai_events(events, self_id):
11
+ state = {
12
+ 'meta': {},
13
+ 'self': self_id,
14
+ 'status': 'processing',
15
+ 'last_actor': 0,
16
+ 'players': [{'reach': False, 'discards': [], 'hand': [], 'melds': []} for _ in range(4)]
17
+ }
18
+
19
+ # 方向映射
20
+ direction_map = {
21
+ 1: 'next', 2: 'front', 3: 'prev'
22
+ }
23
+
24
+ current_player = 0
25
+ reach_declared = [False] * 4
26
+ reach_discard_pending = [False] * 4
27
+
28
+ def is_same_tile(t1, t2):
29
+ """判断两张牌是否相同(考虑红宝牌)"""
30
+ if t1 == t2:
31
+ return True
32
+ if t1.endswith('r') and t1[:-1] == t2:
33
+ return True
34
+ if t2.endswith('r') and t2[:-1] == t1:
35
+ return True
36
+ return False
37
+
38
+ def kakan_to_pon(player_idx, pai):
39
+ """找到可以加杠的碰副露"""
40
+ for i, meld in enumerate(state['players'][player_idx]['melds']):
41
+ if meld['type'] == 'pon':
42
+ if is_same_tile(pai, meld['tiles'][0]):
43
+ state['players'][player_idx]['melds'][i]['type'] = 'kakan'
44
+ state['players'][player_idx]['melds'][i]['key2'] = pai
45
+ break
46
+ return None
47
+ self_last_tsumo = None
48
+ last_event_type = ''
49
+ for event in events:
50
+ event_type = event.get('type')
51
+
52
+ if event_type == 'start_kyoku':
53
+ state['meta'] = {
54
+ 'bakaze': event['bakaze'],
55
+ 'kyoku': event['kyoku'],
56
+ 'honba': event['honba'],
57
+ 'kyotaku': event['kyotaku'],
58
+ 'scores': event['scores'],
59
+ 'oya': event['oya'],
60
+ 'round_info': f"{event['bakaze']}{event['kyoku']}-{event['honba']}",
61
+ 'dora_markers': [],
62
+ 'score_delta': [0, 0, 0, 0],
63
+ 'ura_dora_markers': []
64
+ }
65
+ state['meta']['dora_markers'].append(event['dora_marker'])
66
+ for i in range(4):
67
+ state['players'][i]['hand'] = event['tehais'][i][:]
68
+ state['players'][i]['melds'] = []
69
+ state['players'][i]['discards'] = []
70
+ state['players'][i]['reach'] = False
71
+ state['players'][i]['hand'] = sorted(state['players'][i]['hand'], key=lambda x: tiles_tenhou[x])
72
+ elif event_type == 'hora':
73
+ actor = event['actor']
74
+ state['players'][actor]['hand'] = event['tiles']
75
+ state['status'] = 'hora'
76
+ state['meta',]['score_delta'] = event['delta']
77
+ state['meta']['ura_dora_markers'] = event['ura_dora']
78
+ print(state)
79
+
80
+ elif event_type == 'ryukyoku':
81
+ state['status'] = 'ryukyoku'
82
+ state['meta']['score_delta'] = event['delta']
83
+
84
+ elif event_type == 'tsumo':
85
+ actor = event['actor']
86
+ pai = event['pai']
87
+ state['players'][actor]['hand'].append(pai)
88
+ if actor == self_id:
89
+ self_last_tsumo = pai
90
+
91
+ elif event_type == 'dahai':
92
+ actor = event['actor']
93
+ pai = event['pai']
94
+ tsumogiri = event['tsumogiri']
95
+ if last_event_type == 'tsumo' and actor == self_id:
96
+ tsumogiri = (pai == self_last_tsumo)
97
+
98
+ discard_info = {
99
+ 'pai': pai,
100
+ 'tsumogiri': tsumogiri,
101
+ 'reach_decl': False
102
+ }
103
+
104
+ if reach_discard_pending[actor]:
105
+ discard_info['reach_decl'] = True
106
+ reach_discard_pending[actor] = False
107
+ reach_declared[actor] = True
108
+ state['players'][actor]['reach'] = True
109
+
110
+ state['players'][actor]['discards'].append(discard_info)
111
+
112
+ if pai in state['players'][actor]['hand']:
113
+ state['players'][actor]['hand'].remove(pai)
114
+ elif '?' in state['players'][actor]['hand']:
115
+ state['players'][actor]['hand'].remove('?')
116
+
117
+ elif event_type == 'reach':
118
+ actor = event['actor']
119
+ reach_discard_pending[actor] = True
120
+
121
+ elif event_type == 'reach_accepted':
122
+ if 'scores' in event:
123
+ state['meta']['scores'] = event['scores']
124
+
125
+ elif event_type == 'pon':
126
+ actor = event['actor']
127
+ target = event['target']
128
+ consumed = event['consumed']
129
+ pai = event['pai']
130
+
131
+ rel_pos = direction_map[(target - actor) % 4]
132
+
133
+ meld_info = {
134
+ 'type': 'pon',
135
+ 'tiles': consumed,
136
+ 'key': pai,
137
+ 'callee': rel_pos
138
+ }
139
+
140
+ for tile in consumed:
141
+ if tile in state['players'][actor]['hand']:
142
+ state['players'][actor]['hand'].remove(tile)
143
+ elif '?' in state['players'][actor]['hand']:
144
+ state['players'][actor]['hand'].remove('?')
145
+
146
+ state['players'][actor]['melds'].append(meld_info)
147
+
148
+ target_river = state['players'][target]['discards']
149
+
150
+ if target_river[-1]['pai'] == pai:
151
+ if target_river[-1]['reach_decl']:
152
+ reach_discard_pending[target] = True
153
+ state['players'][target]['discards'] = state['players'][target]['discards'][:-1]
154
+
155
+ elif event_type == 'chi':
156
+ actor = event['actor']
157
+ target = event['target']
158
+ consumed = event['consumed']
159
+ pai = event['pai']
160
+
161
+ rel_pos = direction_map[(target - actor) % 4]
162
+
163
+ meld_info = {
164
+ 'type': 'chi',
165
+ 'tiles': consumed,
166
+ 'key': pai,
167
+ 'callee': rel_pos
168
+ }
169
+
170
+ for tile in consumed:
171
+ if tile in state['players'][actor]['hand']:
172
+ state['players'][actor]['hand'].remove(tile)
173
+ elif '?' in state['players'][actor]['hand']:
174
+ state['players'][actor]['hand'].remove('?')
175
+
176
+ state['players'][actor]['melds'].append(meld_info)
177
+
178
+ target_river = state['players'][target]['discards']
179
+
180
+ if target_river[-1]['pai'] == pai:
181
+ if target_river[-1]['reach_decl']:
182
+ reach_discard_pending[target] = True
183
+ state['players'][target]['discards'] = state['players'][target]['discards'][:-1]
184
+
185
+ elif event_type == 'daiminkan':
186
+ actor = event['actor']
187
+ target = event['target']
188
+ consumed = event['consumed']
189
+ pai = event['pai']
190
+
191
+ rel_pos = direction_map[(target - actor) % 4]
192
+
193
+ meld_info = {
194
+ 'type': 'daiminkan',
195
+ 'tiles': consumed,
196
+ 'key': pai,
197
+ 'callee': rel_pos
198
+ }
199
+
200
+ for tile in consumed:
201
+ if tile in state['players'][actor]['hand']:
202
+ state['players'][actor]['hand'].remove(tile)
203
+ elif '?' in state['players'][actor]['hand']:
204
+ state['players'][actor]['hand'].remove('?')
205
+
206
+ state['players'][actor]['melds'].append(meld_info)
207
+
208
+ target_river = state['players'][target]['discards']
209
+
210
+ if target_river[-1]['pai'] == pai:
211
+ if target_river[-1]['reach_decl']:
212
+ reach_discard_pending[target] = True
213
+ state['players'][target]['discards'] = state['players'][target]['discards'][:-1]
214
+
215
+ elif event_type == 'kakan':
216
+ actor = event['actor']
217
+ pai = event['pai']
218
+
219
+ # 找到对应的碰副露并升级为加杠
220
+ kakan_to_pon(actor, pai)
221
+
222
+ if pai in state['players'][actor]['hand']:
223
+ state['players'][actor]['hand'].remove(pai)
224
+ elif '?' in state['players'][actor]['hand']:
225
+ state['players'][actor]['hand'].remove('?')
226
+
227
+ elif event_type == 'ankan':
228
+ actor = event['actor']
229
+ consumed = event['consumed']
230
+
231
+ meld_info = {
232
+ 'type': 'ankan',
233
+ 'tiles': consumed,
234
+ 'callee': 'self'
235
+ }
236
+
237
+ state['players'][actor]['melds'].append(meld_info)
238
+
239
+ for tile in consumed:
240
+ if tile in state['players'][actor]['hand']:
241
+ state['players'][actor]['hand'].remove(tile)
242
+ elif '?' in state['players'][actor]['hand']:
243
+ state['players'][actor]['hand'].remove('?')
244
+
245
+ elif event_type == 'dora':
246
+ state['meta']['dora_markers'].append(event['dora_marker'])
247
+
248
+ last_event_type = event_type
249
+ if 'actor' in event and event['type'] not in ('tsumo', 'hora'):
250
+ state['players'][actor]['hand'] = sorted(state['players'][actor]['hand'], key=lambda x: tiles_tenhou[x])
251
+ if 'actor' in event:
252
+ state['last_actor'] = event['actor']
253
+ return state
254
+
255
+ # ���用示例
256
+ if __name__ == "__main__":
257
+ events = json.load(open('parsedlogs.txt', 'r'))
258
+ print(process_mjai_events(events, 2))