forked from Dogordog/SPP
/
gamestate.py
executable file
·204 lines (179 loc) · 8.09 KB
/
gamestate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import re
from messageparser import MessageParser
from action import Action
from enums import ActionType, Round
class GameState(object):
def __init__(self, message):
self.message_parser = MessageParser(message)
# hole, [['2c', '2d'],...] for 6 players
self.hole = self.message_parser.hole
# board ['2c', '2d', '2h', ...] for at most 5 board cards
self.board = self.message_parser.board
self.viewing_player = self.message_parser.get_position()
self.betting_string = self.message_parser.get_betting_string(rd=None)
self.board_string = self.message_parser.get_board_string(rd=None)
# a two-dimension list, dim-1 is round, dim-2 is i-th action of a round
self.betting_action = self.get_betting_action()
# a one-dimension list, store each {action string} as an element
self.action_list = []
# store each {action object} of all round actions in a one-dimension list
for each_round_action in self.betting_action:
for action in each_round_action:
self.action_list.append(Action(action))
# set up basic data structure
self.spent = [50, 100, 0, 0, 0, 0]
self.active = [True] * 6
self.fold = [False] * 6
self.allin = [False] * 6
self.pot = 150
self.max_bet = 100
self.current_player = 2 # player at seat 2 is first player to act in PREFLOP round
self.finished = False
self.round = Round.PREFLOP
self.boards = None # first round have no board cards
self.holes = None # hole array for 6 players
self.min_no_limit_raise_to = 2 * 100
self.max_no_limit_raise_to = 20000
self.next_round_flag = False
self.call_number = 0
# update [hole] and [board]
self.holes = self.message_parser.get_hole_card(position=None)
self.boards = self.message_parser.get_board_card(rd=None)
# after setting up basic data structure, start to do each action and update data structure
cnt = 0
for action in self.action_list:
cnt+=1
self.do_action(action)
def do_action(self, action):
# [1.0] do action, update player [spent] and [active]
if action.type == ActionType.CALL:
self.spent[self.current_player] = self.max_bet
self.call_number += 1
# if current player called ALLIN action -> not active
if self.max_bet == 20000:
self.active[self.current_player] = False
self.allin[self.current_player] = True
#if current player folded -> not active
elif action.type == ActionType.FOLD:
self.active[self.current_player] = False
self.fold[self.current_player] = True
else: # must be a raise action
self.call_number = 1
# a raise action happened, min_no_limit_raise_to need to be updated
if action.amount + action.amount - max(self.spent) > self.min_no_limit_raise_to:
self.min_no_limit_raise_to = action.amount + action.amount - max(self.spent)
# make sure it <= 20000
self.min_no_limit_raise_to = min([self.min_no_limit_raise_to, 20000])
# update {max_bet}
self.max_bet = action.amount
self.spent[self.current_player] = action.amount
# if current player raised to stack size -> not active
if action.amount == 20000:
self.active[self.current_player] = False
self.allin[self.current_player] = True
# if all players choose all in, then game ends, which no active players
if self.active.count(True) == 0:
self.finished = True
return
# [3.0] if all active player spent same amount, which means they are reaching next round
amount_set = set()
for p, amount in zip(self.active, self.spent):
if p:
amount_set.add(amount)
next_round_reaching_flag = len(amount_set) == 1 and self.call_number == self.fold.count(False)
self.next_round_flag = next_round_reaching_flag
if next_round_reaching_flag:
# reset call number
self.call_number = 0
# we are going to reach next round
# if current round == 4, then there is no more next round and the game ends here
if self.round == Round.RIVER:
self.finished = True
# there are next round
else:
# update round
self.round += 1
# update min_no_limit_raise_to
self.min_no_limit_raise_to += self.max_bet
self.min_no_limit_raise_to = min([self.min_no_limit_raise_to, 20000])
# find next active player from seat 0
next_player = 0
while not self.active[next_player]:
next_player = (next_player + 1) % 6
self.current_player = next_player
else:
# we are still at current round
# update {current player}, find next active player
# if more than one active player left, find next active player
if self.active.count(True) > 1:
# game is not finished yet
# find next active player
next_player = (self.current_player + 1) % 6
while not self.active[next_player]:
next_player = (next_player + 1) % 6
self.current_player = next_player
elif self.active.count(True) == 1:
# game may finish now
# if there is no all-in player, which means other players all folded, only one player left
if self.allin.count(True) == 0:
# game ends
self.finished = True
# else, there are all-in player, the only one player who is active is the next current player
else:
self.current_player = self.active.index(True)
else:
# active player number is 0, which means they are at least one player all-in, the rest are folded
# game is finished
self.finished = True
# split betting string into single betting actions
# if rd=None, by default, handle betting string of all rounds
def get_betting_action(self, rd=None):
pattern = re.compile(r'r\d+|f|c')
if rd is not None:
string = self.betting_string[rd]
current_round_action = []
# parse string into sigle action string
for m in pattern.finditer(string):
current_round_action.append(m.group())
return current_round_action
else:
betting_action = []
for string in self.betting_string:
# parse string into single action string
current_round_action = []
for m in pattern.finditer(string):
current_round_action.append(m.group())
betting_action.append(current_round_action)
return betting_action
# who just did an action
def get_current_player(self):
return self.current_player
# return active player
# a list containing True/False
def get_active_player(self):
return self.active
def get_active_player_number(self):
return self.active.count(True)
def get_max_bet(self):
return self.max_bet
def get_min_bet(self):
return self.min_bet
def get_pot(self):
return sum(self.pot)
def get_next_valid_raise_size(self):
return [self.min_no_limit_raise_to, self.max_no_limit_raise_to]
def is_my_turn(self):
return self.viewing_player == self.current_player
# str1 = 'MATCHSTATE:1:31:r300r900r3000ccccc/r9000ffffc/cc/cc:|JdTc||||/2c2d2h/3c/3d'
#s = "MATCHSTATE:4:2:fr16524cccc/cr16799r18449:||||Td6c|/Qd9sJc"
s = "MATCHSTATE:1:5:r200fccff:|4h2s||||"
g = GameState(s)
# print(g.current_player)
print(g.active)
print(g.holes)
print(g.next_round_flag)
# print(g.boards)
#
# print(g.get_next_valid_raise_size())
# print(g.finished)
# print(g.max_bet)