Source code for maverick.players.archetypes.grinder
from typing import TYPE_CHECKING
from ...player import Player
from ...enums import ActionType
from ...playeraction import PlayerAction
from ...utils import estimate_holding_strength
if TYPE_CHECKING: # pragma: no cover
from ...game import Game
__all__ = ["GrinderBot"]
[docs]
class GrinderBot(Player):
"""A volume-oriented bot focused on steady expected value.
Uses hand strength evaluation to make disciplined, long-term EV decisions.
Calculates pot odds precisely and makes mathematically sound plays based on
hand equity. Consistent bet sizing and no fancy play syndrome.
- **Key Traits:** Multitabling, consistent lines, bankroll discipline, uses hand equity for EV calculations.
- **Strengths:** Reliable long-term profits, disciplined decision making.
- **Weaknesses:** Predictability, limited creativity.
- **Common At:** Online cash games.
"""
cls_uid = "eae4e67da03d4aed8b90ef4f9d232df1"
[docs]
def decide_action(
self,
*,
game: "Game",
valid_actions: list[ActionType],
min_raise_amount: int,
call_amount: int,
min_bet_amount: int,
) -> PlayerAction:
"""Play solid, consistent poker focused on long-term EV using hand strength."""
# Evaluate hand strength
private_cards = self.state.holding.cards
community_cards = game.state.community_cards
# Get hand equity
if community_cards:
hand_equity = estimate_holding_strength(
private_cards,
community_cards=community_cards,
n_private=game.rules.showdown.hole_cards_required,
n_simulations=600,
n_players=len(game.state.get_players_in_hand()),
)
else:
# Pre-flop estimation
hand_equity = estimate_holding_strength(
private_cards,
n_private=game.rules.showdown.hole_cards_required,
n_simulations=250,
n_players=len(game.state.get_players_in_hand()),
)
# Grinder thresholds - focused on +EV plays
value_hand = hand_equity > 0.60
profitable_hand = hand_equity > 0.45
# Standard raises for value
if ActionType.RAISE in valid_actions and value_hand:
raise_amount = min(min_raise_amount, self.state.stack)
return PlayerAction(
player_uid=self.uid, action_type=ActionType.RAISE, amount=raise_amount
)
# Bet for value with standard sizing
if ActionType.BET in valid_actions and value_hand:
bet_amount = min(min_bet_amount * 2, self.state.stack)
return PlayerAction(
player_uid=self.uid, action_type=ActionType.BET, amount=bet_amount
)
# Call with good pot odds and profitable hands
if ActionType.CALL in valid_actions and profitable_hand:
# Basic pot odds calculation - call if getting 2:1 or better
if call_amount <= self.state.stack and call_amount <= game.state.pot * 0.5:
return PlayerAction(player_uid=self.uid, action_type=ActionType.CALL)
# Check when free
if ActionType.CHECK in valid_actions:
return PlayerAction(player_uid=self.uid, action_type=ActionType.CHECK)
# Fold marginal situations
return PlayerAction(player_uid=self.uid, action_type=ActionType.FOLD)