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.
@@ -0,0 +1,236 @@
1
+ # client.py
2
+ import sys
3
+ # Import deck from parent directory
4
+ import socket
5
+ import json
6
+ from .poker_host import Player
7
+ from .deck import Card
8
+
9
+
10
+ """
11
+ Limp Agent:
12
+ This agent participates based on the concept of "limping" through games. Essentially, the agent will simply match bets so that it can stay in the game but not increase bets to try and gain an advantage.
13
+ In the poker community, this is quite looked down upon. It kills the excitement of the game and becomes "predictable" and drags on games where players with bad hands should fold.
14
+ This is a perfect default agent for our purposes! It's strategically weak, extremely easy to implement, and if bugs are thrown I know that it's not because of internal logic.
15
+ Decision Making Logic:
16
+ The decision making function will return 1 every time to call every hand.
17
+ """
18
+ class LimpAgent:
19
+ def __init__(self):
20
+ # initialize cliennt socket with localhost ip address on port 5555
21
+ self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
22
+ self.host = "127.0.0.1"
23
+ self.port = 5555
24
+ self.client_socket.connect((self.host, self.port))
25
+ self.player_number = int(self.client_socket.recv(1024).decode())
26
+ self.player = Player(self.player_number)
27
+ self.player.chips *= self.player_number
28
+ print(f"You are Player {self.player_number}")
29
+
30
+ # Client messages Server
31
+ def send_message(self, message):
32
+ message = str(message)
33
+ self.client_socket.send(message.encode())
34
+
35
+ # Server messages Client
36
+ def receive_data(self):
37
+ data = self.client_socket.recv(1024).decode()
38
+ try:
39
+ return json.loads(data)
40
+ except json.JSONDecodeError:
41
+ return data
42
+
43
+ # Logic to play the game
44
+ def play_game(self):
45
+ def is_int(value):
46
+ try:
47
+ int(value)
48
+ return True
49
+ except ValueError:
50
+ return False
51
+ def is_str(value):
52
+ try:
53
+ str(value)
54
+ return True
55
+ except ValueError:
56
+ return False
57
+
58
+ while True:
59
+ game_state = self.receive_data()
60
+ # Wait for start code
61
+ if isinstance(game_state, str) and game_state == "GAME STARTED":
62
+ print("Game has started!")
63
+
64
+
65
+ # Big Blind
66
+ elif isinstance(game_state, str) and ("BIG" in game_state):
67
+ self.player.chips -= 2
68
+ # TODO: Handle when player cannot afford big blind
69
+ if (self.player.chips < 0):
70
+ self.player.chips = 0
71
+
72
+ # Small Blind
73
+ elif isinstance(game_state, str) and ("SMALL" in game_state):
74
+ self.player.chips -= 1
75
+
76
+ # Server gives chips back to player
77
+ elif isinstance(game_state, str) and ("GIVE" in game_state):
78
+ first_tilde_pos = game_state.find("~")
79
+ second_tilde_pos = game_state.find("~", first_tilde_pos + 1)
80
+ # Extract the value between the tilde characters
81
+ if first_tilde_pos != -1 and second_tilde_pos != -1:
82
+ chips = game_state[first_tilde_pos + 1:second_tilde_pos]
83
+ self.player.chips += int(chips)
84
+ print(f"You got {chips} chips back!")
85
+ else:
86
+ print("Tilde characters not found or value not present.")
87
+
88
+ # If the data is the cards make bets
89
+ elif isinstance(game_state, dict):
90
+ self.player.chips = game_state['chips'][str(self.player_number)]
91
+ # print(f"Round: {game_state['round']}")
92
+ if (game_state['round'] == 'Showdown!'):
93
+ self.print_showdown_cards(game_state)
94
+ if self.player.player_number == game_state['winner']:
95
+ self.player.chips += int(game_state['pot'])
96
+ chips_str = "\033[92m{}\033[0m".format(str(self.player.chips))
97
+ print(f"You now have {chips_str} chips!")
98
+ continue
99
+
100
+ # Add cards into players hands if hand is empty
101
+ if len(self.player.hand) == 0:
102
+ for card_str in game_state["player_hand"]:
103
+ card = Card.from_str(card_str)
104
+ self.player.hand.append(card)
105
+
106
+ print("\nCurrent Cards:")
107
+ self.print_cards(game_state)
108
+ # Active player makes their move
109
+ if game_state['current_player'] == self.player_number:
110
+ # Game Info
111
+ print(f"Current Player: Player {game_state['current_player']}", end=" | ")
112
+ print(f"Current Dealer: Player {game_state['current_dealer']}", end=" | ")
113
+ print(f"Current Pot: {game_state['pot']}")
114
+ chips_str = "\033[92m{}\033[0m".format(str(self.player.chips))
115
+ print(f"You have {chips_str} chips")
116
+ if (game_state['current_bet'] == 0):
117
+ print("What would you like to do?\nType 1 to check, 2 to raise, and 3 to fold")
118
+ if (game_state['current_bet'] > 0):
119
+ print("What would you like to do?\nType 1 to call, 2 to raise, and 3 to fold")
120
+ print("Bets:")
121
+ print("| ",end="")
122
+ for player,bet in game_state['bets'].items():
123
+ if bet < 0:
124
+ bet = 0
125
+ print(f"Player {player}: {bet} chips", end=" | ")
126
+ print(f"Current Bet: {game_state['current_bet']} |")
127
+
128
+ # Get players choice
129
+ action, bet = self.make_decision(game_state=game_state)
130
+ # while ((not is_int(action)) or (int(action) < 1) or (int(action) > 3)):
131
+ # print("Invalid choice.")
132
+ # action = self.make_decision(game_state=game_state)
133
+
134
+ self.send_message(action)
135
+ # TODO: Handle when player cannot bet but tries to anyways
136
+ if int(action) == 2:
137
+
138
+ # Player cannot make bet if the bet is not a number,
139
+ while ((not is_int(bet)) or (int(bet) < 0) or ((int(bet) + game_state['current_bet']) > self.player.chips)):
140
+ print("Invalid bet.")
141
+ raise Exception("Invalid Bet has occurred from automated bot")
142
+ self.player.chips -= int(bet) + game_state['current_bet']
143
+ self.send_message(bet)
144
+
145
+ # Action should always be 1
146
+ if int(action) == 1:
147
+ print("AI Called!!!")
148
+ if game_state['bets'][str(self.player.player_number)] >= 0:
149
+ self.player.chips += game_state['bets'][str(self.player.player_number)]
150
+ self.player.chips -= int(game_state['current_bet'])
151
+
152
+ print()
153
+ else:
154
+ print("Waiting for the other player's move...")
155
+ # If the move is invalid, the player is prompted again
156
+ elif isinstance(game_state, str) and game_state == "INVALID_MOVE":
157
+ print("Invalid move. Please try again.")
158
+ continue
159
+
160
+ elif isinstance(game_state, str) and ("WIN" in game_state):
161
+ print("Congratulations! You won!")
162
+ self.player.hand = []
163
+
164
+ action = input("Do you want to play again? (y/n)")
165
+ action = action.lower()
166
+ while not is_str(action) or (action != "y" and action != "n"):
167
+ print('invalid entry')
168
+ action = input("Do you want to play again? (y/n)")
169
+ action = action.lower()
170
+ self.send_message(action)
171
+
172
+ elif isinstance(game_state, str) and ("LOSS" in game_state):
173
+ print("Sorry, you lost...")
174
+ self.player.hand = []
175
+
176
+ action = input("Do you want to play again? (y/n)")
177
+ action = action.lower()
178
+ while not is_str(action) or (action != "y" and action != "n"):
179
+ print('invalid entry')
180
+ action = input("Do you want to play again? (y/n)")
181
+ action = action.lower()
182
+ self.send_message(action)
183
+
184
+ # Break code when the game ends
185
+ elif isinstance(game_state, str) and ("END" in game_state):
186
+ print("Game ended.")
187
+ break
188
+ elif isinstance(game_state, str):
189
+ print(game_state)
190
+
191
+ # Close client socket
192
+ def close_connection(self):
193
+ self.client_socket.close()
194
+
195
+ @staticmethod
196
+ def make_decision(game_state):
197
+ # Shoutout raiden. You'd be so proud.
198
+ return 1, 0
199
+
200
+
201
+ # Helper methods for formatting
202
+ @staticmethod
203
+ def print_cards(game_state):
204
+ print(f"Your Hand:")
205
+ print("| ", end="")
206
+ for card_str in game_state["player_hand"]:
207
+ card = Card.from_str(card_str)
208
+ print(card.print_card(),end = " | ")
209
+ print("\n---------------------------")
210
+ if (len(game_state['community_cards']) > 0):
211
+ print(f"Community Cards:")
212
+ print("| ", end="")
213
+ for card_str in game_state["community_cards"]:
214
+ card = Card.from_str(card_str)
215
+ print(card.print_card(),end = " | ")
216
+ print()
217
+ @staticmethod
218
+ def print_showdown_cards(game_state):
219
+ for player, hand in game_state['players_hand'].items():
220
+ print(f"Player {player}'s Hand:")
221
+ print("| ", end="")
222
+ for card_str in hand:
223
+ card = Card.from_str(card_str)
224
+ print(card.print_card(),end = " | ")
225
+ print("\n---------------------------")
226
+ if (len(game_state['community_cards']) > 0):
227
+ print(f"Community Cards:")
228
+ print("| ", end="")
229
+ for card_str in game_state["community_cards"]:
230
+ card = Card.from_str(card_str)
231
+ print(card.print_card(),end = " | ")
232
+ print()
233
+
234
+
235
+
236
+
@@ -0,0 +1,340 @@
1
+ import sys
2
+ # Import deck from parent directory
3
+ # sys.path.append("../")
4
+
5
+ from .deck import *
6
+ from collections import Counter
7
+
8
+
9
+ flag = False
10
+ def cprint(str):
11
+ if flag:
12
+ print(str)
13
+ def rank_hand(hand):
14
+ # Helper function to rank the hand
15
+ # Returns a tuple with two values:
16
+ # - The rank of the hand (higher is better)
17
+ # - The value(s) used for tie-breaking if ranks are equal
18
+ def is_subarray(subarray, array):
19
+ # Helper function to check if subarray is a subarray of array
20
+ m, n = len(subarray), len(array)
21
+ i = 0
22
+ while i <= n - m:
23
+ if array[i:i + m] == subarray:
24
+ return True # subarray found
25
+ i += 1
26
+ return False
27
+
28
+ def has_flush(suits):
29
+ # Helper function to check for a flush
30
+ # Returns True if there are 5 or more cards of the same suit, False otherwise
31
+ suit_counts = Counter(suits)
32
+ return any(count >= 5 for count in suit_counts.values())
33
+ def has_consecutive_values(arr):
34
+ n = len(arr)
35
+ if n < 5:
36
+ return False, []
37
+ sorted_arr = sorted(arr)
38
+ for i in range(n - 4):
39
+ sub_array = sorted_arr[i : i + 5]
40
+ # print(sub_array)
41
+ if all((sub_array[j] + 1) % 13 == sub_array[j + 1] % 13 for j in range(4)) or set(sub_array) == {12, 0, 1, 2, 3}:
42
+
43
+ return True, sub_array
44
+ return False, []
45
+ values = [card.value for card in hand]
46
+ suits = [card.suit for card in hand]
47
+
48
+ flush = has_flush(suits)
49
+
50
+
51
+
52
+
53
+
54
+ if is_subarray([10, 11, 12, 13, 14], sorted(values)) and flush:
55
+ # print("Royal Flush!")
56
+ return (10, [14])
57
+
58
+
59
+ straight_values = [value - 2 for value in values]
60
+
61
+
62
+
63
+
64
+ if flush:
65
+ is_straight_flush, returned = has_consecutive_values(straight_values)
66
+ if is_straight_flush:
67
+ # print("Straight Flush!")
68
+ return (9, [[value + 2 for value in returned][4]])
69
+
70
+ # If it's not a straight flush but all suits are the same, it's a flush
71
+ # Note: You should sort the values in descending order, not just reverse
72
+ return (6, sorted(values, reverse=True))
73
+
74
+
75
+ for value, count in Counter(values).items():
76
+ if count == 4:
77
+ # print("Four of a kind!")
78
+ return (8, [value, max(set(values) - {value})])
79
+
80
+ three_of_a_kind = None
81
+ pair = None
82
+ for value, count in Counter(values).items():
83
+ if count == 3:
84
+ three_of_a_kind = value
85
+ elif count == 2:
86
+ pair = value
87
+
88
+ if three_of_a_kind is not None and pair is not None:
89
+ # print("Full House!")
90
+ return (7, [three_of_a_kind, pair])
91
+
92
+
93
+ is_straight, returned = has_consecutive_values(straight_values)
94
+ if (is_straight):
95
+ # print("Straight!")
96
+ return (5, [[value + 2 for value in returned][4]])
97
+
98
+ for value, count in Counter(values).items():
99
+ if count == 3:
100
+ # print("3 of a kind!")
101
+ return (4, [value] + sorted(set(values) - {value}, reverse=True)[:2])
102
+
103
+ pairs = [value for value, count in Counter(values).items() if count == 2]
104
+ if len(pairs) >= 2:
105
+ # print("Two Pair!")
106
+ return (3, sorted(pairs, reverse=True) + sorted(set(values) - set(pairs), reverse=True)[:1])
107
+
108
+ for value, count in Counter(values).items():
109
+ if count == 2:
110
+ # print("One Pair!")
111
+ return (2, [value] + sorted(set(values) - {value}, reverse=True)[:3])
112
+
113
+ # print("High Card!")
114
+ return (1, sorted(values, reverse=True))
115
+
116
+ class Player:
117
+ def __init__(self, player_number, chips=100):
118
+ self.chips = chips
119
+ self.hand = []
120
+ self.player_number = player_number
121
+
122
+ class TexasHoldem:
123
+
124
+ def __init__(self, num_players=2):
125
+ # Initialize Deck, players hands, community cards
126
+ self.deck = Deck()
127
+ self.deck.shuffle()
128
+ self.community_cards = []
129
+ self.player_hands = {player: [] for player in range(1, num_players + 1)}
130
+ self.round = ""
131
+ self.winning_rank = ""
132
+ self.all_in = False
133
+
134
+
135
+ # Initialize players
136
+ self.num_players = num_players
137
+ self.players = {player: Player(player, chips=100*player) for player in range(1, num_players + 1)}
138
+ self.dealer = 1
139
+ self.current_player = (self.dealer % self.num_players) + 1
140
+
141
+ self.players_in_round = {player: True for player in range(1, num_players + 1)}
142
+
143
+ # Initialize bets
144
+ self.cum_bets = {player: 0 for player in range(1, num_players + 1)}
145
+ self.bets = {player: -1 for player in range(1, num_players + 1)}
146
+ self.current_bet = 0
147
+ self.pot = 0
148
+
149
+
150
+ def reset_deck(self):
151
+ self.deck = Deck()
152
+ self.deck.shuffle()
153
+ self.community_cards = []
154
+ self.player_hands = {player: [] for player in range(1, self.num_players + 1)}
155
+ self.set_round("Pre-flop")
156
+ self.winning_hand = ""
157
+ self.all_in = False
158
+ self.players_in_round = {player: self.players[player].chips != 0 for player in range(1, self.num_players + 1)}
159
+
160
+ def reset_players(self):
161
+ for _, player in self.players.items():
162
+ player.hand = []
163
+
164
+ def reset_bets(self):
165
+ self.bets = {player: -1 for player in range(1, self.num_players + 1)}
166
+ self.current_player = (self.dealer % self.num_players) + 1
167
+ self.current_bet = 0
168
+
169
+ def reset_pot(self):
170
+ self.pot = 0
171
+ self.current_bet = 0
172
+ self.bets = {player: -1 for player in range(1, self.num_players + 1)}
173
+
174
+ def set_round(self, round):
175
+ self.round = round
176
+
177
+ def get_game_state(self, player_num):
178
+ """
179
+ Get the game state for a specific player.
180
+
181
+ Parameters:
182
+ - player_num (int): The player number.
183
+
184
+ Returns:
185
+ - dict: The game state for the specified player.
186
+ """
187
+ player_state = {
188
+ "round": self.round,
189
+ "player_hand": [str(card) for card in self.player_hands[player_num]],
190
+ "community_cards": [str(card) for card in self.community_cards],
191
+ "current_bet": self.current_bet,
192
+ "pot": self.pot,
193
+ "current_player": self.current_player,
194
+ "current_dealer": self.dealer,
195
+ "bets": self.bets,
196
+ "cum_bets": self.cum_bets,
197
+ "chips": {player.player_number: player.chips for player in self.players.values() },
198
+ "probabilities": self.draw_probabilities(self.community_cards)
199
+ }
200
+
201
+ return player_state
202
+ def get_showdown_game_state(self):
203
+ """
204
+ Get the game state for a specific player.
205
+
206
+ Parameters:
207
+ - player_num (int): The player number.
208
+
209
+ Returns:
210
+ - dict: The game state for the specified player.
211
+ """
212
+ player_state = {
213
+ "round": self.round,
214
+ "players_hand": {player_num: [str(card) for card in self.player_hands[player_num]] for player_num in self.players.keys()},
215
+ "community_cards": [str(card) for card in self.community_cards],
216
+ "winner": self.determine_winner(),
217
+ "pot": self.pot,
218
+ "rank": self.winning_rank,
219
+ "chips": {player.player_number: player.chips for player in self.players.values() }
220
+ }
221
+
222
+ return player_state
223
+
224
+ def deal_hole_cards(self):
225
+ for _ in range(2):
226
+ for _ , hand in self.player_hands.items():
227
+ hand.append(self.deck.deal_card())
228
+
229
+
230
+ def deal_community_cards(self, num_cards):
231
+ for _ in range(num_cards):
232
+ self.community_cards.append(self.deck.deal_card())
233
+
234
+ def determine_winner(self):
235
+ all_hands = [hand + self.community_cards for hand in self.player_hands.values()]
236
+
237
+ best_hands = [rank_hand(player_hand) for player_hand in all_hands]
238
+
239
+
240
+
241
+ # Sort the best hands by rank and then by the highest card value
242
+ sorted_best_hands = sorted(enumerate(best_hands), key=lambda x: (x[1][0], x[1][1]), reverse=True)
243
+
244
+ # Determine the winner
245
+ winner_key = list(self.player_hands.keys())[sorted_best_hands[0][0]] # Get the key of the first (highest) hand
246
+ winning_hand = all_hands[sorted_best_hands[0][0]]
247
+
248
+ # Update winning_hand_frequency dictionary based on the hand rank
249
+ rank, _ = best_hands[sorted_best_hands[0][0]]
250
+ rank_mapping = {
251
+ 10: "Royal Flush",
252
+ 9: "Straight Flush",
253
+ 8: "Four of a Kind",
254
+ 7: "Full House",
255
+ 6: "Flush",
256
+ 5: "Straight",
257
+ 4: "Three of a Kind",
258
+ 3: "Two Pair",
259
+ 2: "One Pair",
260
+ 1: "High Card",
261
+ }
262
+ print("Winning Hand:" + rank_mapping[rank] + " with rank " + str(rank))
263
+ self.winning_rank = rank_mapping[rank]
264
+ print("| ", end="")
265
+ for i in winning_hand:
266
+ print(i.print_card(),end =" | ")
267
+ print()
268
+
269
+ return winner_key
270
+
271
+ def place_bet(self, player_number, bet_amount):
272
+ self.players[player_number].chips -= bet_amount
273
+ self.pot += bet_amount
274
+ self.bets[player_number] += bet_amount
275
+ self.cum_bets[player_number] += bet_amount
276
+ if (not self.all_in):
277
+ self.current_bet = max(self.bets.values())
278
+
279
+ def give_winnings(self, player_num):
280
+ self.players[player_num].chips += self.pot
281
+ self.pot = 0
282
+
283
+
284
+ def switch_player(self):
285
+ self.current_player = (self.current_player % self.num_players) + 1
286
+
287
+ @staticmethod
288
+ def draw_probabilities(cards):
289
+ probabilities = {
290
+ "Royal Flush": 0,
291
+ "Straight Flush": 0,
292
+ "Four of a Kind": 0,
293
+ "Full House": 0,
294
+ "Flush": 0,
295
+ "Straight": 0,
296
+ "Three of a Kind": 0,
297
+ "Two Pair": 0,
298
+ "One Pair": 0,
299
+ "High Card": 0,
300
+ }
301
+
302
+ rank_mapping = {
303
+ 10: "Royal Flush",
304
+ 9: "Straight Flush",
305
+ 8: "Four of a Kind",
306
+ 7: "Full House",
307
+ 6: "Flush",
308
+ 5: "Straight",
309
+ 4: "Three of a Kind",
310
+ 3: "Two Pair",
311
+ 2: "One Pair",
312
+ 1: "High Card",
313
+ }
314
+ deck = Deck()
315
+ deck.cards = [card for card in deck.cards if all(card.name != other_card.name for other_card in cards)]
316
+ i = 0
317
+ for card1 in deck.cards:
318
+ for card2 in deck.cards:
319
+ if card1 != card2:
320
+ temp_cards = cards + [card1 , card2]
321
+ rank, _ = rank_hand(temp_cards)
322
+ probabilities[rank_mapping[rank]] += 1
323
+ i+=1
324
+
325
+ for key in probabilities:
326
+ probabilities[key] /= i
327
+
328
+ return probabilities
329
+
330
+ def check_bets_matched(self):
331
+ if (max(self.bets.values()) < 0):
332
+ return False
333
+ # Iterate through players still in the round
334
+ for player_num, in_round in self.players_in_round.items():
335
+ if in_round:
336
+ # Check if the player's bet matches the current bet
337
+ if self.bets[player_num] < self.current_bet:
338
+ return False
339
+ # If all players' bets match the current bet, return True
340
+ return True