Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
691 changes: 691 additions & 0 deletions Improved Bot without RL 1.23.2026.py

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions cc_py_bot_v1/commands.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"build": [],
"run": ["python3", "player.py"]
}
123 changes: 123 additions & 0 deletions cc_py_bot_v1/player.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'''
Not-So-Simple example pokerbot, written in Python.
'''
from skeleton.actions import FoldAction, CallAction, CheckAction, RaiseAction, DiscardAction
from skeleton.states import GameState, TerminalState, RoundState
from skeleton.states import NUM_ROUNDS, STARTING_STACK, BIG_BLIND, SMALL_BLIND
from skeleton.bot import Bot
from skeleton.runner import parse_args, run_bot

import random


class Player(Bot):
'''
A pokerbot.
'''

def __init__(self):
'''
Called when a new game starts. Called exactly once.

Arguments:
Nothing.

Returns:
Nothing.
'''
x = 3
pass

def handle_new_round(self, game_state, round_state, active):
'''
Called when a new round starts. Called NUM_ROUNDS times.

Arguments:
game_state: the GameState object.
round_state: the RoundState object.
active: your player's index.

Returns:
Nothing.
'''
my_bankroll = game_state.bankroll # the total number of chips you've gained or lost from the beginning of the game to the start of this round
# the total number of seconds your bot has left to play this game
game_clock = game_state.game_clock
round_num = game_state.round_num # the round number from 1 to NUM_ROUNDS
my_cards = round_state.hands[active] # your cards
big_blind = bool(active) # True if you are the big blind
pass

def handle_round_over(self, game_state, terminal_state, active):
'''
Called when a round ends. Called NUM_ROUNDS times.

Arguments:
game_state: the GameState object.
terminal_state: the TerminalState object.
active: your player's index.

Returns:
Nothing.
'''
my_delta = terminal_state.deltas[active] # your bankroll change from this round
previous_state = terminal_state.previous_state # RoundState before payoffs
street = previous_state.street # 0,2,3,4,5,6 representing when this round ended
my_cards = previous_state.hands[active] # your cards
# opponent's cards or [] if not revealed
opp_cards = previous_state.hands[1-active]
pass

def get_action(self, game_state, round_state, active):
'''
Where the magic happens - your code should implement this function.
Called any time the engine needs an action from your bot.

Arguments:
game_state: the GameState object.
round_state: the RoundState object.
active: your player's index.

Returns:
Your action.
'''
legal_actions = round_state.legal_actions() # the actions you are allowed to take
# 0, 3, 4, or 5 representing pre-flop, flop, turn, or river respectively
street = round_state.street
my_cards = round_state.hands[active] # your cards
board_cards = round_state.board # the board cards
# the number of chips you have contributed to the pot this round of betting
my_pip = round_state.pips[active]
# the number of chips your opponent has contributed to the pot this round of betting
opp_pip = round_state.pips[1-active]
# the number of chips you have remaining
my_stack = round_state.stacks[active]
# the number of chips your opponent has remaining
opp_stack = round_state.stacks[1-active]
continue_cost = opp_pip - my_pip # the number of chips needed to stay in the pot
# the number of chips you have contributed to the pot
my_contribution = STARTING_STACK - my_stack
# the number of chips your opponent has contributed to the pot
opp_contribution = STARTING_STACK - opp_stack

# Only use DiscardAction if it's in legal_actions (which already checks street)
# legal_actions() returns DiscardAction only when street is 2 or 3
if DiscardAction in legal_actions:
# Always discards the first card in the bot's hand
return DiscardAction(0)
if RaiseAction in legal_actions:
# the smallest and largest numbers of chips for a legal bet/raise
min_raise, max_raise = round_state.raise_bounds()
min_cost = min_raise - my_pip # the cost of a minimum bet/raise
max_cost = max_raise - my_pip # the cost of a maximum bet/raise
if random.random() < 0.5:
return RaiseAction(min_raise)
if CheckAction in legal_actions: # check-call
return CheckAction()
if random.random() < 0.25:
return FoldAction()
return CallAction()


if __name__ == '__main__':
run_bot(Player(), parse_args())
11 changes: 11 additions & 0 deletions cc_py_bot_v1/skeleton/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'''
The actions that the player is allowed to take.
'''
from collections import namedtuple

FoldAction = namedtuple('FoldAction', [])
CallAction = namedtuple('CallAction', [])
CheckAction = namedtuple('CheckAction', [])
DiscardAction = namedtuple('DiscardAction', ['card'])## Card should be the index of the card in your hand [0,1,2]
# we coalesce BetAction and RaiseAction for convenience
RaiseAction = namedtuple('RaiseAction', ['amount'])
52 changes: 52 additions & 0 deletions cc_py_bot_v1/skeleton/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'''
This file contains the base class that you should implement for your pokerbot.
'''


class Bot():
'''
The base class for a pokerbot.
'''

def handle_new_round(self, game_state, round_state, active):
'''
Called when a new round starts. Called NUM_ROUNDS times.

Arguments:
game_state: the GameState object.
round_state: the RoundState object.
active: your player's index.

Returns:
Nothing.
'''
raise NotImplementedError('handle_new_round')

def handle_round_over(self, game_state, terminal_state, active):
'''
Called when a round ends. Called NUM_ROUNDS times.

Arguments:
game_state: the GameState object.
terminal_state: the TerminalState object.
active: your player's index.

Returns:
Nothing.
'''
raise NotImplementedError('handle_round_over')

def get_action(self, game_state, round_state, active):
'''
Where the magic happens - your code should implement this function.
Called any time the engine needs an action from your bot.

Arguments:
game_state: the GameState object.
round_state: the RoundState object.
active: your player's index.

Returns:
Your action.
'''
raise NotImplementedError('get_action')
147 changes: 147 additions & 0 deletions cc_py_bot_v1/skeleton/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
'''
The infrastructure for interacting with the engine.
'''
import argparse
import socket
from .actions import FoldAction, CallAction, CheckAction, RaiseAction, DiscardAction
from .states import GameState, TerminalState, RoundState
from .states import STARTING_STACK, BIG_BLIND, SMALL_BLIND
from .bot import Bot


class Runner():
'''
Interacts with the engine.
'''

def __init__(self, pokerbot, socketfile):
self.pokerbot = pokerbot
self.socketfile = socketfile

def receive(self):
'''
Generator for incoming messages from the engine.
'''
while True:
packet = self.socketfile.readline().strip().split(' ')
if not packet:
break
yield packet

def send(self, action):
'''
Encodes an action and sends it to the engine.
'''
if isinstance(action, FoldAction):
code = 'F'
elif isinstance(action, CallAction):
code = 'C'
elif isinstance(action, CheckAction):
code = 'K'
elif isinstance(action, DiscardAction):
code = 'D' + str(action.card)## action.card is the index of the action card in the player's hand
else: # isinstance(action, RaiseAction)
code = 'R' + str(action.amount)
self.socketfile.write(code + '\n')
self.socketfile.flush()

def run(self):
'''
Reconstructs the game tree based on the action history received from the engine.
'''
game_state = GameState(0, 0., 1)
round_state = None
active = 0
round_flag = True
for packet in self.receive():
for clause in packet:
if clause[0] == 'T':
game_state = GameState(game_state.bankroll, float(clause[1:]), game_state.round_num)
elif clause[0] == 'P':
active = int(float(clause[1:]))
elif clause[0] == 'H':
hands = [[], []]

hands[active] = clause[1:].split(',')
pips = [SMALL_BLIND, BIG_BLIND]
stacks = [STARTING_STACK - SMALL_BLIND, STARTING_STACK - BIG_BLIND]
round_state = RoundState(0, 0, pips, stacks, hands, [], None)
elif clause[0] == 'G':
# 'G' clause indicates game/round start - just update the round_state without changing values
round_state = RoundState(round_state.button, round_state.street, round_state.pips, round_state.stacks,
round_state.hands, round_state.board, round_state.previous_state)
if round_flag:
self.pokerbot.handle_new_round(game_state, round_state, active)
round_flag = False
elif clause[0] == 'F':
round_state = round_state.proceed(FoldAction())
elif clause[0] == 'C':
round_state = round_state.proceed(CallAction())
elif clause[0] == 'K':
round_state = round_state.proceed(CheckAction())
elif clause[0] == 'D':
if isinstance(round_state, RoundState):
round_state = round_state.proceed(DiscardAction(int(clause[1:])))
else:
pass
elif clause[0] == 'R':
round_state = round_state.proceed(RaiseAction(int(float(clause[1:]))))
elif clause[0] == 'B':
# 'B' clause contains the board cards for the current street
# The street should already be correct from previous proceed() calls
# Just update the board with the cards from the engine
board_cards = clause[1:].split(',') if len(clause) > 1 else []
round_state = RoundState(round_state.button, round_state.street, round_state.pips, round_state.stacks,
round_state.hands, board_cards, round_state.previous_state)
elif clause[0] == 'O':
# backtrack
round_state = round_state.previous_state
revised_hands = list(round_state.hands)
revised_hands[1-active] = clause[1:].split(',')
# rebuild history
round_state = RoundState(round_state.button, round_state.street, round_state.pips, round_state.stacks,
revised_hands, round_state.board, round_state.previous_state)
round_state = TerminalState([0, 0], round_state)
elif clause[0] == 'A':
assert isinstance(round_state, TerminalState)
delta = int(float(clause[1:]))
deltas = [-delta, -delta]
deltas[active] = delta
round_state = TerminalState(deltas, round_state.previous_state)
self.pokerbot.handle_round_over(game_state, round_state, active)
game_state = GameState(game_state.bankroll + delta, game_state.game_clock, game_state.round_num)
round_flag = True
elif clause[0] == 'Q':
return
if round_flag or isinstance(round_state, TerminalState): # ack the engine
self.send(CheckAction())
else:
##assert active == round_state.button % 2
action = self.pokerbot.get_action(game_state, round_state, active)
self.send(action)


def parse_args():
'''
Parses arguments corresponding to socket connection information.
'''
parser = argparse.ArgumentParser(prog='python3 player.py')
parser.add_argument('--host', type=str, default='localhost', help='Host to connect to, defaults to localhost')
parser.add_argument('port', type=int, help='Port on host to connect to')
return parser.parse_args()

def run_bot(pokerbot, args):
'''
Runs the pokerbot.
'''
assert isinstance(pokerbot, Bot)
try:
sock = socket.create_connection((args.host, args.port))
except OSError:
print('Could not connect to {}:{}'.format(args.host, args.port))
return
socketfile = sock.makefile('rw')
runner = Runner(pokerbot, socketfile)
runner.run()
socketfile.close()
sock.close()
Loading