def test__call(self): players = self.__setup_blind_players() self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 9)) self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 10)) self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 11)) self.eq(5, ActionChecker.need_amount_for_action(players[0], 10)) self.eq(0, ActionChecker.need_amount_for_action(players[1], 10))
def test_check(self): players = self.__setup_clean_players() self.false( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 0)) self.eq(0, ActionChecker.need_amount_for_action(players[0], 0)) self.eq(0, ActionChecker.need_amount_for_action(players[1], 0))
def test__raise(self): players = self.__setup_blind_players() self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 14)) self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 15)) self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 16)) self.eq(10, ActionChecker.need_amount_for_action(players[0], 15)) self.eq(5, ActionChecker.need_amount_for_action(players[1], 15))
def test__raise(self): players = self.__setup_blind_players() self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 14)) self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 15)) self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 17.5)) self.eq(10, ActionChecker.need_amount_for_action(players[0], 15)) self.eq(5, ActionChecker.need_amount_for_action(players[1], 15))
def __update_state_by_action(self, state, action, bet_amount, bot_info=None): table = state["table"] action, bet_amount = ActionChecker.correct_action(\ table.seats.players, state["next_player"], state["small_blind_amount"], action, bet_amount) next_player = table.seats.players[state["next_player"]] if ActionChecker.is_allin(next_player, action, bet_amount): next_player.pay_info.update_to_allin() return self.__accept_action(state, action, bet_amount, bot_info=bot_info)
def __update_state_by_action(self, state, action, bet_amount): table = state['table'] action, bet_amount = ActionChecker.correct_action( table.seats.players, state['next_player'], state['small_blind_amount'], action, bet_amount) next_player = table.seats.players[state['next_player']] if ActionChecker.is_allin(next_player, action, bet_amount): next_player.pay_info.update_to_allin() return self.__accept_action(state, action, bet_amount)
def test__short_of_money(self): players = self.__setup_blind_players() players[0].collect_bet(88) # p1 stack is $7 self.false( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, "call", 10)) self.true( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, "call", 15))
def test_big_blind_allin_call(self): players = self.__setup_blind_players() players[0].add_action_history(Const.Action.RAISE, 100, 95) self.false( ActionChecker._ActionChecker__is_illegal(players, 1, 2.5, "call", 100)) players[1].collect_bet(1) self.true( ActionChecker._ActionChecker__is_illegal(players, 1, 2.5, "call", 100))
def test_legal_actions_when_short_of_money(self): players = self.__setup_blind_players() players[0].stack = 9 legal_actions = ActionChecker.legal_actions(players, 0, 2.5) self.eq({'action': 'fold', 'amount': 0}, legal_actions[0]) self.eq({'action': 'call', 'amount': 10, 'paid': 5}, legal_actions[1]) self.eq({'action': 'raise', 'amount': {'min': 14, 'max': 14}, 'paid': 5}, legal_actions[2])
def test_legal_actions_when_short_of_money(self): players = self.__setup_blind_players() players[0].stack = 9 legal_actions = ActionChecker.legal_actions(players, 0, 2.5) self.eq({"action":"fold", "amount":0}, legal_actions[0]) self.eq({"action":"call", "amount":10}, legal_actions[1]) self.eq({"action":"raise", "amount": { "min":-1, "max":-1} }, legal_actions[2])
def test_generate_possible_actions(self): state = self.task.generate_initial_state() actions = self.task.generate_possible_actions(state) self.size(6, actions) self.eq({"name": "fold", "action": "fold", "amount": 0}, actions[0]) self.eq({"name": "call", "action": "call", "amount": 50}, actions[1]) self.eq({ "name": "min_raise", "action": "raise", "amount": 75 }, actions[2]) self.eq({ "name": "double_raise", "action": "raise", "amount": 150 }, actions[3]) self.eq({ "name": "triple_raise", "action": "raise", "amount": 225 }, actions[4]) self.eq({ "name": "max_raise", "action": "raise", "amount": 10000 }, actions[5]) # Check if generated actions are valid correct = lambda act, amount: ActionChecker.correct_action( state["table"].seats.players, 2, 25, act, amount) for action in [a for a in actions if not a["action"] == "fold"]: self.neq("fold", correct(action["action"], action["amount"])[0])
def test_correct_action_on_allin_call(self): players = self.__setup_clean_players() players[0].add_action_history(Const.Action.RAISE, 50, 50) players[1].add_action_history(Const.Action.BIG_BLIND, sb_amount=5) players[1].stack = 30 action, bet_amount = ActionChecker.correct_action(players, 1, 2.5, 'call', 50) self.eq('call', action) self.eq(40, bet_amount)
def get_valid_moves(amount_left, num_raises, street, pot_amount): raise_amount, raise_limit = ActionChecker.round_raise_amount( SMALL_BLIND, street_as_int(street)) if (num_raises >= MAX_RAISES or pot_amount >= raise_limit or # TODO: check this condition amount_left < raise_amount * 2): # raise_amount * 2 is generalisation, can still raise with less than that in reality return ["call", "fold"] return ["raise", "call", "fold"]
def perform(self, action, game_state, adversary_state): # raise amount and limit is constant for betting round, can move following call elsewhere to reduce # unnecessary calls raise_amount, raise_limit = ActionChecker.round_raise_amount( SMALL_BLIND, street_as_int(game_state.street)) call_amount = adversary_state.amount_bet - self.amount_bet # you can only call by the amount you have left call_amount = min(call_amount, self.amount_left) if action == "fold": # a fold ends the round self.valid_actions = [] adversary_state.valid_actions = [] self.has_folded = True self.has_played = True elif action == "call": # only need to put in money if opponent has more in pot than you if call_amount > 0: assert (self.amount_left >= call_amount) self.amount_left -= call_amount self.amount_bet += call_amount game_state.pot_amount += call_amount self.has_played = True # a call ends the betting round if the opponent has played if (adversary_state.has_played): self.valid_actions = [] adversary_state.valid_actions = [] elif action == "raise": if call_amount > 0: assert (self.amount_left >= raise_amount + call_amount) self.amount_left -= raise_amount + call_amount self.amount_bet += raise_amount + call_amount game_state.pot_amount += raise_amount + call_amount else: self.amount_left -= raise_amount self.amount_bet += raise_amount game_state.pot_amount += raise_amount self.raises_made += 1 self.has_played = True # remove raise if insufficient cash to raise later or if opp cannot match raise if "raise" in self.valid_actions and ( self.raises_made >= MAX_RAISES or game_state.pot_amount >= raise_limit or # TODO: check this condition self.amount_left < raise_amount * 2 or adversary_state.amount_left < raise_amount): self.valid_actions.remove("raise") # remove call if unable to match opponent raise if "call" in self.valid_actions and self.amount_left < raise_amount: self.valid_actions.remove("call") # remove fold if only move left is fold if len(self.valid_actions) == 1 and self.valid_actions[0] == "fold": self.valid_actions.remove("fold")
def __update_state_by_action(self, state, action): table = state["table"] current_amount = ActionChecker.agree_amount( state["table"].seats.players) bet = ActionChecker.round_raise_amount(state["small_blind_amount"], state["street"]) if action == "raise": amount = current_amount + bet[0] elif action == "call": amount = current_amount elif action == "fold": amount = 0 else: amount = 0 action, bet_amount = ActionChecker.correct_action(\ table.seats.players, state["next_player"], state["small_blind_amount"], action, amount) next_player = table.seats.players[state["next_player"]] if ActionChecker.is_allin(next_player, action, bet_amount): next_player.pay_info.update_to_allin() return self.__accept_action(state, action, bet_amount), amount
def _visualize_action_log(self, task, value_function, experience): state, action, _next_state, _reward = experience players = state["table"].seats.players me = [p for p in players if p.uuid == "uuid-0"][0] me_pos = players.index(me) sb_amount = state["small_blind_amount"] valid_actions = ActionChecker.legal_actions(players, me_pos, sb_amount) hole = [str(card) for card in me.hole_card] round_state = DataEncoder.encode_round_state(state) visualized_state = visualize_declare_action(valid_actions, hole, round_state) action_log = "Agent took action [ %s: %s (%s) ] at round %d" % ( action["action"], action["amount"], action["name"], state["round_count"]) actions = task.generate_possible_actions(state) act_vals = [ value_function.predict_value(state, act) for act in actions ] act_names = [act["name"] for act in actions] action_value_log = " => %s" % zip(act_names, act_vals) if self.show_weights: weights_log = ["** weights and features in detail **"] features_title = value_function.delegate.generate_features_title() features = value_function.delegate.construct_poker_features( "dummy", "dummy", round_state, me.uuid, hole, value_function.delegate.handicappers, blind_structure) w_for_acts = value_function.delegate.model.get_weights()[0].T bias = value_function.delegate.model.layers[0].b.get_value() weights_log.append("features : %s" % features) weights_log.append("bias : %s" % bias) for act in actions: weights_log.append("") act_val = act_vals[actions.index(act)] weights = w_for_acts[actions.index(act)].tolist() linear_comb = [ (f * w, f, w, features_title[idx]) for idx, (f, w) in enumerate(zip(features, weights)) ] linear_comb.append((bias[actions.index(act)], 0, 0, "bias")) linear_comb = sorted(linear_comb, key=lambda item: abs(item[0]))[::-1] display_items = [] for item in linear_comb: display_items.append(item) if abs(act_val - sum([tpl[0] for tpl in display_items])) < 0.01: break weights_log.append("linear combination for %s :" % act["name"]) for comb in display_items: weights_log.append(" %s" % str(comb)) action_value_log += "\n" + "\n".join(weights_log) return "\n".join([visualized_state, action_log, action_value_log])
def test_legal_actions(self): players = self.__setup_blind_players() legal_actions = ActionChecker.legal_actions(players, 0, 2.5) self.eq({"action": "fold", "amount": 0}, legal_actions[0]) self.eq({"action": "call", "amount": 10}, legal_actions[1]) self.eq({ "action": "raise", "amount": { "min": 15, "max": 100 } }, legal_actions[2])
def test_legal_actions_when_short_of_money(self): players = self.__setup_blind_players() players[0].stack = 9 legal_actions = ActionChecker.legal_actions(players, 0, 2.5) self.eq({"action": "fold", "amount": 0}, legal_actions[0]) self.eq({"action": "call", "amount": 10}, legal_actions[1]) self.eq({ "action": "raise", "amount": { "min": -1, "max": -1 } }, legal_actions[2])
def _get_allowed_action(self): allowed_action_list = ActionChecker().legal_actions( self.state['table'].seats.players, self.state['next_player'], self.state['small_blind_amount']) action_list = build_action_list( self.state['table'].seats.players[self.state['next_player']].stack, self.state['table'].seats.players) res = [ i for i, action in enumerate(action_list) for allowed_action in allowed_action_list if compare_action(action, allowed_action) ] return list(set(res))
def build_ask_message(self, player_pos, state): players = state["table"].seats.players player = players[player_pos] hole_card = DataEncoder.encode_player(player, holecard=True)["hole_card"] valid_actions = ActionChecker.legal_actions(players, player_pos, state["small_blind_amount"]) message = { "message_type" : self.ASK_MESSAGE, "hole_card": hole_card, "valid_actions": valid_actions, "round_state": DataEncoder.encode_round_state(state), "action_histories": DataEncoder.encode_action_histories(state["table"]) } return self.__build_ask_message(message)
def __accept_action(self, state, action, bet_amount, bot_info=None): player = state["table"].seats.players[state["next_player"]] if action == 'call': self.__chip_transaction(player, bet_amount) player.add_action_history(Const.Action.CALL, bet_amount, bot_info=bot_info) elif action == 'raise': self.__chip_transaction(player, bet_amount) add_amount = bet_amount - ActionChecker.agree_amount(state["table"].seats.players) player.add_action_history(Const.Action.RAISE, bet_amount, add_amount, bot_info=bot_info) elif action == 'fold': player.add_action_history(Const.Action.FOLD, bot_info=bot_info) player.pay_info.update_to_fold() else: raise ValueError("Unexpected action %s received" % action) return state
def build_ask_message(self, player_pos, state): players = state['table'].seats.players player = players[player_pos] hole_card = DataEncoder.encode_player(player, holecard=True)['hole_card'] valid_actions = ActionChecker.legal_actions( players, player_pos, state['small_blind_amount']) message = { 'message_type': self.ASK_MESSAGE, 'hole_card': hole_card, 'valid_actions': valid_actions, 'round_state': DataEncoder.encode_round_state(state), 'action_histories': DataEncoder.encode_action_histories(state['table']) } return self.__build_ask_message(message)
def build_ask_message(self, player_pos, state): players = state["table"].seats.players player = players[player_pos] hole_card = DataEncoder.encode_player(player, holecard=True)["hole_card"] valid_actions = ActionChecker.legal_actions( players, player_pos, state["small_blind_amount"]) message = { "message_type": self.ASK_MESSAGE, "hole_card": hole_card, "valid_actions": valid_actions, "round_state": DataEncoder.encode_round_state(state), "action_histories": DataEncoder.encode_action_histories(state["table"]) } return self.__build_ask_message(message)
def test_correct_correct_action_on_call_regression(self): players = self.__setup_clean_players() for player, stack in zip(players, [130, 70]): player.stack = stack players[0].collect_bet(5) players[0].pay_info.update_by_pay(5) players[0].add_action_history(Const.Action.SMALL_BLIND, sb_amount=5) players[1].collect_bet(10) players[1].pay_info.update_by_pay(10) players[1].add_action_history(Const.Action.BIG_BLIND, sb_amount=5) players[0].collect_bet(55) players[0].pay_info.update_by_pay(55) players[0].add_action_history(Const.Action.RAISE, 60, 55) action, bet_amount = ActionChecker.correct_action(players, 1, 2.5, 'call', 60) self.eq('call', action) self.eq(60, bet_amount)
def test_correct_correct_action_on_call_regression(self): players = self.__setup_clean_players() for player, stack in zip(players,[130, 70]): player.stack = stack players[0].collect_bet(5) players[0].pay_info.update_by_pay(5) players[0].add_action_history(Const.Action.SMALL_BLIND, sb_amount=5) players[1].collect_bet(10) players[1].pay_info.update_by_pay(10) players[1].add_action_history(Const.Action.BIG_BLIND, sb_amount=5) players[0].collect_bet(55) players[0].pay_info.update_by_pay(55) players[0].add_action_history(Const.Action.RAISE, 60, 55) action, bet_amount = ActionChecker.correct_action(players, 1, 2.5, 'call', 60) self.eq('call', action) self.eq(60, bet_amount)
def __accept_action(self, state, action, bet_amount): player = state["table"].seats.players[state["next_player"]] if action == 'call': self.__chip_transaction(player, bet_amount) player.add_action_history(Const.Action.CALL, bet_amount) print("@@@@@player : ", player.name, "@@@@@\n") print("@@@@@bet_amount : ", bet_amount, "@@@@@\n") led.led(led, player.name, bet_amount) elif action == 'raise': self.__chip_transaction(player, bet_amount) add_amount = bet_amount - ActionChecker.agree_amount(state["table"].seats.players) player.add_action_history(Const.Action.RAISE, bet_amount, add_amount) print("@@@@@player : ", player.name, "@@@@@\n") print("@@@@@bet_amount : ", bet_amount, "@@@@@\n") #bet_amount 만큼 칩을 뱉어야 함 print("@@@@@add_amount : ", add_amount, "@@@@@\n") led.led(led, player.name, bet_amount) elif action == 'fold': player.add_action_history(Const.Action.FOLD) player.pay_info.update_to_fold() else: raise ValueError("Unexpected action %s received" % action) return state
def test_legal_raise(self): players = self.__setup_clean_players() self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 5))
def test_too_small_raise(self): players = self.__setup_clean_players() self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 4))
def test__fold(self): players = self.__setup_blind_players() self.false( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'fold'))
def test_check(self): players = self.__setup_clean_players() self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 0)) self.eq(0, ActionChecker.need_amount_for_action(players[0], 0)) self.eq(0, ActionChecker.need_amount_for_action(players[1], 0))
def test_allin_check_on_raise(self): player = self.__setup_clean_players()[0] self.false(ActionChecker.is_allin(player, "raise", 99)) self.true(ActionChecker.is_allin(player, "raise", 100)) self.false(ActionChecker.is_allin(player, "raise", 101))
def test_small_blind_allin_raise(self): players = self.__setup_blind_players() self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, "raise", 100))
def generate_legal_actions(players, player_position, sb_amount): return ActionChecker.legal_actions(players, player_position, sb_amount)
def test_allin_check_on_call(self): player = self.__setup_clean_players()[0] self.false(ActionChecker.is_allin(player, "call", 99)) self.true(ActionChecker.is_allin(player, "call", 100)) self.true(ActionChecker.is_allin(player, "call", 101))
def test_call(self): players = self.__setup_clean_players() self.true( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 10))
def test_correct_action_when_legal(self): players = self.__setup_clean_players() action, bet_amount = ActionChecker.correct_action( players, 0, 2.5, 'raise', 100) self.eq('raise', action) self.eq(100, bet_amount)
def test_small_blind_allin_raise(self): players = self.__setup_blind_players() self.false( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, "raise", 100))
def test_need_amount_after_ante(self): # situation => SB=$5 (players[0]), BB=$10 (players[1]), ANTE=$3 players = [Player("uuid", 100, name="name") for _ in range(3)] for player in players: player.collect_bet(3) player.add_action_history(Const.Action.ANTE, 3) player.pay_info.update_by_pay(3) players[0].collect_bet(5) players[0].add_action_history(Const.Action.SMALL_BLIND, sb_amount=5) players[0].pay_info.update_by_pay(5) players[1].collect_bet(10) players[1].add_action_history(Const.Action.BIG_BLIND, sb_amount=5) players[1].pay_info.update_by_pay(10) def set_stack(stacks, ps): for stack, p in zip(stacks, ps): p.stack = stack set_stack([7, 7, 7], players) self.eq(("call", 10), ActionChecker.correct_action(players, 0, 5, "call", 10)) self.eq(("call", 10), ActionChecker.correct_action(players, 1, 5, "call", 10)) self.eq(("call", 7), ActionChecker.correct_action(players, 2, 5, "call", 10)) self.true(ActionChecker.is_allin(players[2], "call", 8)) self.false(ActionChecker.is_allin(players[2], "raise", 10)) self.eq(5, ActionChecker.need_amount_for_action(players[0], 10)) self.eq(0, ActionChecker.need_amount_for_action(players[1], 10)) self.eq(10, ActionChecker.need_amount_for_action(players[2], 10)) set_stack([12, 12, 12], players) actions = ActionChecker.legal_actions(players, 2, 5) self.eq(-1, actions[2]["amount"]["max"]) set_stack([10, 5, 12], players) self.eq(("raise", 15), ActionChecker.correct_action(players, 0, 5, "raise", 15)) self.eq(("raise", 15), ActionChecker.correct_action(players, 1, 5, "raise", 15)) self.eq(("fold", 0), ActionChecker.correct_action(players, 2, 5, "raise", 15))
def test__fold(self): players = self.__setup_blind_players() self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'fold'))
def is_legal_action(players, player_position, sb_amount, action, amount=None): return ActionChecker._is_legal( players, player_position, sb_amount, action, amount)
def test_correct_action_when_legal(self): players = self.__setup_clean_players() action, bet_amount = ActionChecker.correct_action(players, 0, 2.5, 'call', 0) self.eq('call', action) self.eq(0, bet_amount)
def test__short_of_money(self): players = self.__setup_blind_players() players[0].collect_bet(88) # p1 stack is $7 self.false(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, "call", 10)) self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, "call", 15))
def test_legal_actions(self): players = self.__setup_blind_players() legal_actions = ActionChecker.legal_actions(players, 0, 2.5) self.eq({"action":"fold", "amount":0}, legal_actions[0]) self.eq({"action":"call", "amount":10}, legal_actions[1]) self.eq({"action":"raise", "amount": { "min":15, "max":100} }, legal_actions[2])
def test_big_blind_allin_call(self): players = self.__setup_blind_players() players[0].add_action_history(Const.Action.RAISE, 100, 95) self.false(ActionChecker._ActionChecker__is_illegal(players, 1, 2.5, "call", 100)) players[1].collect_bet(1) self.true(ActionChecker._ActionChecker__is_illegal(players, 1, 2.5, "call", 100))
def test_call(self): players = self.__setup_clean_players() self.true(ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'call', 10))
def test_allin_check_on_fold(self): player = self.__setup_clean_players()[0] self.false(ActionChecker.is_allin(player, "fold", 0))
def test_need_amount_after_ante(self): # situation => SB=$5 (players[0]), BB=$10 (players[1]), ANTE=$3 players = [Player("uuid", 100, name="name") for _ in range(3)] for player in players: player.collect_bet(3) player.add_action_history(Const.Action.ANTE, 3) player.pay_info.update_by_pay(3) players[0].collect_bet(5) players[0].add_action_history(Const.Action.SMALL_BLIND, sb_amount=5) players[0].pay_info.update_by_pay(5) players[1].collect_bet(10) players[1].add_action_history(Const.Action.BIG_BLIND, sb_amount=5) players[1].pay_info.update_by_pay(10) def set_stack(stacks, ps): for stack, p in zip(stacks, ps): p.stack = stack set_stack([7,7,7], players) self.eq(("call", 10), ActionChecker.correct_action(players, 0, 5, "call", 10)) self.eq(("call", 10), ActionChecker.correct_action(players, 1, 5, "call", 10)) self.eq(("call", 7), ActionChecker.correct_action(players, 2, 5, "call", 10)) self.true(ActionChecker.is_allin(players[2], "call", 8)) self.false(ActionChecker.is_allin(players[2], "raise", 10)) self.eq(5, ActionChecker.need_amount_for_action(players[0], 10)) self.eq(0, ActionChecker.need_amount_for_action(players[1], 10)) self.eq(10, ActionChecker.need_amount_for_action(players[2], 10)) set_stack([12,12,12], players) actions = ActionChecker.legal_actions(players, 2, 5) self.eq(-1, actions[2]["amount"]["max"]) set_stack([10,5,12], players) self.eq(("raise", 15), ActionChecker.correct_action(players, 0, 5, "raise", 15)) self.eq(("raise", 15), ActionChecker.correct_action(players, 1, 5, "raise", 15)) self.eq(("fold", 0), ActionChecker.correct_action(players, 2, 5, "raise", 15))
def test_too_small_raise(self): players = self.__setup_clean_players() self.true( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 4))
def test_correct_action_on_allin_raise(self): players = self.__setup_clean_players() action, bet_amount = ActionChecker.correct_action(players, 0, 2.5, 'raise', 100) self.eq('raise', action) self.eq(100, bet_amount)
def test_correct_illegal_call(self): players = self.__setup_clean_players() action, bet_amount = ActionChecker.correct_action(players, 0, 2.5, 'call', 10) self.eq('fold', action) self.eq(0, bet_amount)
def test_legal_raise(self): players = self.__setup_clean_players() self.false( ActionChecker._ActionChecker__is_illegal(players, 0, 2.5, 'raise', 5))