poker-reinforcement-learning 0.1.8__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- poker_reinforcement_learning/__init__.py +10 -0
- poker_reinforcement_learning/client.py +220 -0
- poker_reinforcement_learning/deck.py +99 -0
- poker_reinforcement_learning/dummy_agent.py +240 -0
- poker_reinforcement_learning/limp_agent.py +236 -0
- poker_reinforcement_learning/poker_host.py +340 -0
- poker_reinforcement_learning/server.py +415 -0
- poker_reinforcement_learning-0.1.8.dist-info/LICENSE.txt +674 -0
- poker_reinforcement_learning-0.1.8.dist-info/METADATA +897 -0
- poker_reinforcement_learning-0.1.8.dist-info/RECORD +12 -0
- poker_reinforcement_learning-0.1.8.dist-info/WHEEL +5 -0
- poker_reinforcement_learning-0.1.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,415 @@
|
|
1
|
+
# server.py
|
2
|
+
|
3
|
+
import socket
|
4
|
+
import threading
|
5
|
+
from .poker_host import Player, TexasHoldem
|
6
|
+
import json
|
7
|
+
import time
|
8
|
+
|
9
|
+
class ServerPlayer(Player):
|
10
|
+
def __init__(self, player_number, chips=100, client_socket=None):
|
11
|
+
super().__init__(player_number, chips)
|
12
|
+
# ServerPlayer info is redundant, really only need the player_number and client_socket
|
13
|
+
self.client_socket = client_socket
|
14
|
+
|
15
|
+
# Server Class
|
16
|
+
class Server:
|
17
|
+
def __init__(self,num_players):
|
18
|
+
# initialize server socket with localhost ip address on port 5555
|
19
|
+
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
20
|
+
self.host = "127.0.0.1"
|
21
|
+
self.port = 5555
|
22
|
+
self.server_socket.bind((self.host, self.port))
|
23
|
+
self.server_socket.listen(num_players)
|
24
|
+
print("Server is listening for connections...")
|
25
|
+
# Array for player socket
|
26
|
+
self.players = {}
|
27
|
+
# Game imported from poker_host
|
28
|
+
self.game = TexasHoldem(num_players)
|
29
|
+
self.num_players = num_players
|
30
|
+
self.num_players_connected = 0
|
31
|
+
self.lock = threading.Lock()
|
32
|
+
self.dealer_check_event = threading.Event()
|
33
|
+
# Players Recieved Message Counter
|
34
|
+
self.prm = 0
|
35
|
+
# Condition to play more rounds of poker
|
36
|
+
self.keep_playing = True
|
37
|
+
# Keeps track of players leaving
|
38
|
+
self.players_connected = True
|
39
|
+
|
40
|
+
|
41
|
+
# Function to send game state from the server to the client. Game state includes the board information as well as whose turn it is. Game state info is in json format
|
42
|
+
def send_game_state(self, player):
|
43
|
+
game_state = self.game.get_game_state(player.player_number)
|
44
|
+
json_data = json.dumps(game_state)
|
45
|
+
print(f"Sending game state to Player {player.player_number}: {json_data}")
|
46
|
+
player.client_socket.send(json_data.encode())
|
47
|
+
|
48
|
+
def reset_thread_management(self):
|
49
|
+
self.lock = threading.Lock()
|
50
|
+
self.dealer_check_event = threading.Event()
|
51
|
+
|
52
|
+
# PRM: Players recieved message. Reset to 0 for new message
|
53
|
+
def reset_prm_counter(self):
|
54
|
+
self.prm = 0
|
55
|
+
|
56
|
+
def send_game_state_to_all(self):
|
57
|
+
for _ , player in self.players.items():
|
58
|
+
self.send_game_state(player)
|
59
|
+
|
60
|
+
def send_global_message(self, message):
|
61
|
+
for _ , player in self.players.items():
|
62
|
+
player.client_socket.send(str(message).encode())
|
63
|
+
|
64
|
+
def check_for_early_win(self):
|
65
|
+
return list(self.game.players_in_round.values()).count(True) == 1
|
66
|
+
|
67
|
+
def get_early_winner(self):
|
68
|
+
for key, value in self.game.players_in_round.items():
|
69
|
+
if value is True:
|
70
|
+
return key
|
71
|
+
|
72
|
+
def round(self, name):
|
73
|
+
|
74
|
+
# all_in() checks for bet_amounts that empty a players chips
|
75
|
+
def all_in(player, bet_amount):
|
76
|
+
# Get the players chips
|
77
|
+
temp_chips = self.game.get_game_state(player.player_number)['chips'][player.player_number]
|
78
|
+
|
79
|
+
# Check if the bet is too large
|
80
|
+
if ((temp_chips - bet_amount) <= 0):
|
81
|
+
print(f"player: {player.player_number}")
|
82
|
+
print(f"chips: {temp_chips}")
|
83
|
+
print(f"bet flag: {bet_amount}")
|
84
|
+
|
85
|
+
# Player puts in all their chips
|
86
|
+
bet_amount = temp_chips
|
87
|
+
print(f"Player {player.player_number} is ALL IN!.")
|
88
|
+
self.send_global_message(f"Player {player.player_number} is ALL IN!.")
|
89
|
+
self.game.all_in = True
|
90
|
+
# current bet is the amount of chips they can bet + the chips they already bet = all their chips
|
91
|
+
self.game.current_bet = bet_amount + self.game.bets[player.player_number]
|
92
|
+
# Give back chips from side pot
|
93
|
+
# Fix Current pot
|
94
|
+
for player_number , other_player_bet in self.game.bets.items():
|
95
|
+
if other_player_bet > self.game.current_bet:
|
96
|
+
# Calculate the difference between the current bet and the players bets that are higher
|
97
|
+
diff = other_player_bet - self.game.current_bet
|
98
|
+
print(f"Diff: {diff}")
|
99
|
+
# Give back chips that go over current bet
|
100
|
+
self.game.cum_bets[player_number] -= diff
|
101
|
+
self.game.players[player_number].chips += diff
|
102
|
+
other_player_bet = self.game.current_bet
|
103
|
+
self.players[player_number].client_socket.send(str(f"GIVE~{diff}~").encode())
|
104
|
+
print(f"Gave player {player_number} {diff} chips")
|
105
|
+
|
106
|
+
return bet_amount
|
107
|
+
|
108
|
+
self.game.round = name
|
109
|
+
self.send_global_message(f"-----------------------------------------------------------------------\n{name} Round")
|
110
|
+
self.game.set_round(name)
|
111
|
+
print(f"\nRound Called: {name}")
|
112
|
+
# Big/Small Blind
|
113
|
+
if (name == "Pre-flop"):
|
114
|
+
for i in range(2,0,-1):
|
115
|
+
player = self.players[self.game.current_player]
|
116
|
+
if (self.game.bets[player.player_number] < 0):
|
117
|
+
self.game.bets[player.player_number] = 0
|
118
|
+
self.game.place_bet(player.player_number, i)
|
119
|
+
# Notify players of bets
|
120
|
+
if i == 1:
|
121
|
+
print(f"Player {player.player_number} placed the small blind.")
|
122
|
+
self.send_global_message(f"Player {player.player_number} placed the small blind.")
|
123
|
+
player.client_socket.send("SMALL".encode())
|
124
|
+
else:
|
125
|
+
print(f"Player {player.player_number} placed the big blind.")
|
126
|
+
self.send_global_message(f"Player {player.player_number} placed the big blind.")
|
127
|
+
player.client_socket.send("BIG".encode())
|
128
|
+
self.game.switch_player()
|
129
|
+
|
130
|
+
# Check if betting needs to continue
|
131
|
+
while(not self.game.check_bets_matched()):
|
132
|
+
# Set the current player
|
133
|
+
player = self.players[self.game.current_player]
|
134
|
+
if (self.game.bets[player.player_number] < self.game.current_bet):
|
135
|
+
# Send all users game state. The current player will be the only one who can interact
|
136
|
+
self.send_game_state_to_all()
|
137
|
+
|
138
|
+
# If the player folded, pass them
|
139
|
+
if self.game.players_in_round[player.player_number] == True:
|
140
|
+
|
141
|
+
# Get the active players choice
|
142
|
+
action = player.client_socket.recv(1024).decode()
|
143
|
+
if action == "1": # Check/Call
|
144
|
+
if (self.game.bets[player.player_number] < 0):
|
145
|
+
self.game.bets[player.player_number] = 0
|
146
|
+
if(self.game.bets[player.player_number] == self.game.current_bet):
|
147
|
+
print(f"Player {player.player_number} checks.")
|
148
|
+
self.send_global_message(f"Player {player.player_number} checks.")
|
149
|
+
else:
|
150
|
+
print(f"Player {player.player_number} calls.")
|
151
|
+
self.send_global_message(f"Player {player.player_number} checks.")
|
152
|
+
|
153
|
+
bet_amount = self.game.current_bet - self.game.bets[player.player_number]
|
154
|
+
|
155
|
+
|
156
|
+
# All in
|
157
|
+
bet_amount = all_in(player, bet_amount)
|
158
|
+
|
159
|
+
|
160
|
+
print(f"Player {player.player_number} bets {bet_amount} chips.")
|
161
|
+
self.game.place_bet(player.player_number, bet_amount)
|
162
|
+
elif action == "2": # Raise Bet
|
163
|
+
if (self.game.bets[player.player_number] < 0):
|
164
|
+
self.game.bets[player.player_number] = 0
|
165
|
+
bet_amount = int(player.client_socket.recv(1024).decode()) + self.game.current_bet
|
166
|
+
|
167
|
+
# All in
|
168
|
+
bet_amount = all_in(player, bet_amount)
|
169
|
+
|
170
|
+
self.game.place_bet(player.player_number, bet_amount)
|
171
|
+
print(f"Player {player.player_number} bets {bet_amount} chips.")
|
172
|
+
self.send_global_message(f"Player {player.player_number} bets {bet_amount} chips.")
|
173
|
+
|
174
|
+
elif action == "3": # Fold
|
175
|
+
print(f"Player {player.player_number} folds.")
|
176
|
+
self.send_global_message(f"Player {player.player_number} folds.")
|
177
|
+
self.game.players_in_round[player.player_number] = False
|
178
|
+
# self.players[i].hand = [] # Remove player's hand
|
179
|
+
else: # Theoretically never gets called
|
180
|
+
print("Invalid choice. Please choose again.")
|
181
|
+
print(f"Bets matched: {self.game.check_bets_matched()}")
|
182
|
+
print(f"Bets: {self.game.bets}")
|
183
|
+
print(f"Early Win Detected: {self.check_for_early_win()}")
|
184
|
+
|
185
|
+
|
186
|
+
if (self.game.check_bets_matched()):
|
187
|
+
break
|
188
|
+
if (self.check_for_early_win()):
|
189
|
+
winner = self.get_early_winner()
|
190
|
+
print(f"Winner (by betting): {winner}")
|
191
|
+
# self.send_global_message("END")
|
192
|
+
break
|
193
|
+
else:
|
194
|
+
self.game.switch_player()
|
195
|
+
print(f"Current Player: {self.game.current_player}")
|
196
|
+
# self.send_game_state(self.players[self.game.current_player])
|
197
|
+
print('\n')
|
198
|
+
else:
|
199
|
+
pass
|
200
|
+
else:
|
201
|
+
self.game.switch_player()
|
202
|
+
# if (self.game.check_bets_matched()):
|
203
|
+
# break
|
204
|
+
# Logic that the server runs to interact with the clients and handle game management
|
205
|
+
def handle_client(self, player):
|
206
|
+
def keep_playing_game(client_socket):
|
207
|
+
with self.lock:
|
208
|
+
self.game.reset_bets()
|
209
|
+
self.game.reset_players()
|
210
|
+
self.game.reset_deck()
|
211
|
+
self.game.reset_pot()
|
212
|
+
print(f"Player has sent their response")
|
213
|
+
answer = client_socket.recv(1024).decode()
|
214
|
+
print(f"Answer: {answer}")
|
215
|
+
if str(answer) == "n":
|
216
|
+
print("Game Ending.")
|
217
|
+
self.keep_playing = False
|
218
|
+
time.sleep(0.1)
|
219
|
+
|
220
|
+
|
221
|
+
def game_ended_early(player_number):
|
222
|
+
# Determine who won
|
223
|
+
winner = None
|
224
|
+
for key, value in self.game.players_in_round.items():
|
225
|
+
if value == True:
|
226
|
+
winner = key
|
227
|
+
break
|
228
|
+
|
229
|
+
pot = self.game.pot
|
230
|
+
# print(f"pot: {pot}")
|
231
|
+
# Send showdown information for client to display
|
232
|
+
if (player_number == self.game.dealer):
|
233
|
+
|
234
|
+
print(f"Player {winner} has won!")
|
235
|
+
self.send_global_message(f"All other players have folded. Player {winner} has won {pot} chips!")
|
236
|
+
self.game.give_winnings(winner)
|
237
|
+
|
238
|
+
for player_num , player in self.players.items():
|
239
|
+
if (player_num == winner):
|
240
|
+
player.client_socket.send(str(f"GIVE~{pot}~\n").encode())
|
241
|
+
time.sleep(0.1)
|
242
|
+
player.client_socket.send(str("WIN").encode())
|
243
|
+
else:
|
244
|
+
time.sleep(0.1)
|
245
|
+
player.client_socket.send(str("LOSS").encode())
|
246
|
+
self.game.dealer = (self.game.dealer % self.game.num_players) + 1
|
247
|
+
self.reset_prm_counter()
|
248
|
+
|
249
|
+
|
250
|
+
client_socket = player.client_socket
|
251
|
+
player_number = player.player_number
|
252
|
+
|
253
|
+
client_socket.send(str(player_number).encode())
|
254
|
+
|
255
|
+
# Wait for 2 players before interacting with the client further
|
256
|
+
while self.num_players_connected != self.game.num_players:
|
257
|
+
pass
|
258
|
+
|
259
|
+
|
260
|
+
# With self.lock, .set(), .wait(), .clear() used to lock threads so all players catch up after rounds
|
261
|
+
while self.keep_playing:
|
262
|
+
# Send game start code
|
263
|
+
|
264
|
+
with self.lock:
|
265
|
+
client_socket.send("GAME STARTED".encode())
|
266
|
+
self.prm+=1
|
267
|
+
print(f"PRM: {self.prm}, Num Players: {self.num_players_connected}")
|
268
|
+
if (self.prm==self.num_players_connected):
|
269
|
+
self.reset_prm_counter()
|
270
|
+
self.dealer_check_event.set()
|
271
|
+
self.dealer_check_event.wait()
|
272
|
+
self.dealer_check_event.clear()
|
273
|
+
|
274
|
+
print("Checkpoint 1")
|
275
|
+
|
276
|
+
# Pre-flop round
|
277
|
+
with self.lock:
|
278
|
+
if player_number == self.game.dealer:
|
279
|
+
self.game.deal_hole_cards()
|
280
|
+
|
281
|
+
self.round("Pre-flop")
|
282
|
+
print("Pre-Flop Betting Ended")
|
283
|
+
self.send_global_message("Pre-Flop Betting Ended")
|
284
|
+
self.game.deal_community_cards(3)
|
285
|
+
self.game.reset_bets()
|
286
|
+
self.dealer_check_event.set()
|
287
|
+
|
288
|
+
self.dealer_check_event.wait()
|
289
|
+
self.dealer_check_event.clear()
|
290
|
+
|
291
|
+
print("Checkpoint 2")
|
292
|
+
|
293
|
+
# Flop round
|
294
|
+
if not self.check_for_early_win():
|
295
|
+
with self.lock:
|
296
|
+
if player_number == self.game.dealer:
|
297
|
+
if (not self.game.all_in):
|
298
|
+
self.round("Flop")
|
299
|
+
print("Flop Betting Ended")
|
300
|
+
self.send_global_message("Flop Betting Ended")
|
301
|
+
self.game.deal_community_cards(1)
|
302
|
+
self.game.reset_bets()
|
303
|
+
print(self.game.bets)
|
304
|
+
self.dealer_check_event.set()
|
305
|
+
self.dealer_check_event.wait()
|
306
|
+
self.dealer_check_event.clear()
|
307
|
+
else:
|
308
|
+
game_ended_early(player_number)
|
309
|
+
keep_playing_game(client_socket)
|
310
|
+
continue
|
311
|
+
|
312
|
+
print("Checkpoint 3")
|
313
|
+
|
314
|
+
# Turn round
|
315
|
+
|
316
|
+
if not self.check_for_early_win():
|
317
|
+
with self.lock:
|
318
|
+
if player_number == self.game.dealer:
|
319
|
+
if (not self.game.all_in):
|
320
|
+
self.round("Turn")
|
321
|
+
print("Turn Betting Ended")
|
322
|
+
self.send_global_message("Turn Betting Ended")
|
323
|
+
self.game.deal_community_cards(1)
|
324
|
+
self.game.reset_bets()
|
325
|
+
print(self.game.bets)
|
326
|
+
self.dealer_check_event.set()
|
327
|
+
self.dealer_check_event.wait()
|
328
|
+
self.dealer_check_event.clear()
|
329
|
+
else:
|
330
|
+
game_ended_early(player_number)
|
331
|
+
keep_playing_game(client_socket)
|
332
|
+
continue
|
333
|
+
|
334
|
+
print("Checkpoint 4")
|
335
|
+
|
336
|
+
# River round
|
337
|
+
if not self.check_for_early_win():
|
338
|
+
with self.lock:
|
339
|
+
if player_number == self.game.dealer:
|
340
|
+
if (not self.game.all_in):
|
341
|
+
self.round("River")
|
342
|
+
print("River Betting Ended")
|
343
|
+
self.send_global_message("River Betting Ended")
|
344
|
+
self.game.reset_bets()
|
345
|
+
print(self.game.bets)
|
346
|
+
self.dealer_check_event.set()
|
347
|
+
self.dealer_check_event.wait()
|
348
|
+
self.dealer_check_event.clear()
|
349
|
+
else:
|
350
|
+
game_ended_early(player_number)
|
351
|
+
keep_playing_game(client_socket)
|
352
|
+
continue
|
353
|
+
|
354
|
+
print("Checkpoint 5\n")
|
355
|
+
|
356
|
+
# SHOWDOWN
|
357
|
+
with self.lock:
|
358
|
+
if player_number == self.game.dealer:
|
359
|
+
print("Showdown!")
|
360
|
+
self.game.set_round("Showdown!")
|
361
|
+
self.send_global_message("Showdown!")
|
362
|
+
|
363
|
+
# Determine who won
|
364
|
+
winner = self.game.determine_winner()
|
365
|
+
print(f"Player {winner} has won!")
|
366
|
+
game_state = self.game.get_showdown_game_state()
|
367
|
+
self.game.give_winnings(winner)
|
368
|
+
|
369
|
+
# Send showdown information for client to display
|
370
|
+
self.send_global_message(f"Player {winner} has won {game_state['pot']} chips! with a {game_state['rank']}")
|
371
|
+
for player_num , player in self.players.items():
|
372
|
+
json_data = json.dumps(game_state)
|
373
|
+
print(f"Sending showdown game state to Player {player.player_number}: {json_data}")
|
374
|
+
player.client_socket.send(json_data.encode())
|
375
|
+
if (player_num == winner):
|
376
|
+
player.client_socket.send(str("WIN").encode())
|
377
|
+
else:
|
378
|
+
player.client_socket.send(str("LOSS").encode())
|
379
|
+
self.dealer_check_event.set()
|
380
|
+
|
381
|
+
print("Checkpoint 6")
|
382
|
+
|
383
|
+
self.dealer_check_event.wait()
|
384
|
+
self.dealer_check_event.clear()
|
385
|
+
|
386
|
+
keep_playing_game(client_socket)
|
387
|
+
|
388
|
+
|
389
|
+
# If only one player has chips end the game
|
390
|
+
if self.check_for_early_win():
|
391
|
+
print("Only one player has chips. Game ending now.")
|
392
|
+
self.send_global_message("Only one player has chips. Game ending now.")
|
393
|
+
break
|
394
|
+
|
395
|
+
|
396
|
+
|
397
|
+
# Close connection to client when the game is over
|
398
|
+
try:
|
399
|
+
self.send_global_message("END")
|
400
|
+
finally:
|
401
|
+
print(f"Connection from {client_socket.getpeername()} has been closed.")
|
402
|
+
client_socket.close()
|
403
|
+
self.players_connected = False
|
404
|
+
|
405
|
+
def accept_connections(self):
|
406
|
+
# Repeatedly check for clients that will join
|
407
|
+
while self.players_connected and self.num_players_connected < self.num_players:
|
408
|
+
client_socket, addr = self.server_socket.accept()
|
409
|
+
print(f"Connection from {addr} has been established.")
|
410
|
+
# Add clients to players array for reference later
|
411
|
+
self.num_players_connected += 1
|
412
|
+
player = ServerPlayer(self.num_players_connected, chips=100*self.num_players_connected, client_socket = client_socket)
|
413
|
+
self.players[self.num_players_connected] = player
|
414
|
+
# Start a new threat for each client
|
415
|
+
threading.Thread(target=self.handle_client, args=(player,)).start()
|