iczelia
Blackjack KoTH event for Esolangs
| 2026-08-12 20:24 | initial import | iczelia |
| main (2026-08-12 20:24) |
no tags.
make (produces bj, dflat, drandom). Use new-ish clang or gcc../bj --games 2000 --bot flat:./dflat --bot random:./drandom.legal_actions and staking constraints to avoid termination.dflat.c (./dflat): deterministic baseline; always bets 10, declines insurance, prefers DOUBLE if offered, otherwise HIT if available, otherwise STAND; occasional SPLIT (20% chance) when legal.drandom.c (./drandom): random-ish; bets 5-10 uniformly, declines insurance, chooses uniformly among advertised legal play actions.penetration_reshuffle_fraction 0.75).blackjack_payout 1.5). Player blackjack vs dealer blackjack is a push.split_max_hands (4 total hands). Aces may be split once; split aces get one card each and are immediately marked completed (no hit/double/resplit).min_bet and max_bet. Bot must have at least min_bet balance to be seated for a game.min_bet and not terminated are shuffled into a per-game order; seat in requests is this order index.HIT, STAND, optional DOUBLE, optional SPLIT.num_games or no active bots.One line of JSON is sent per decision. Structure (all numeric fields are integers unless noted):
{
"type": "action_request",
"match_id": "demo-match",
"game_id": 12,
"bot_id": "flat_basic",
"seat": 0,
"phase": "bet" | "insurance" | "play",
"st": {
"rules": {
"decks": 6,
"penetration_reshuffle_fraction": 0.75,
"dealer_hits_soft_17": 1,
"blackjack_payout": 1.5,
"double_allowed": "any_two",
"double_after_split": 1,
"split_max_hands": 4,
"resplit_aces": 0,
"hit_split_aces": 0,
"surrender": "none",
"insurance_allowed": 1,
"min_bet": 1,
"max_bet": 100
},
"table": {
"num_seats": 2,
"seat_order": [ { "seat": 0, "bot_id": "flat_basic" }, ... ],
"balances": { "flat_basic": 9990.0, "randomish": 10005.0 },
"active_bots": ["flat_basic", "randomish"],
"terminated_bots": [],
"current_bets": { "flat_basic": 10.0, "randomish": 5.0 },
"hands": [
{ "seat": 0, "hands": [ { "hand_id": "12-0-0", "wager": 10.0, "is_split_hand": 0, "is_completed": 0, "cards": [ { "rank": "A", "suit": "S" }, ... ] } ] },
{ "seat": 1, "hands": [ ... ] }
]
},
"shoe": {
"cards_dealt_in_shoe": 42,
"cards_remaining_in_shoe": 270,
"reshuffle_at_dealt": 234,
"discard_rank_counts": { "A": 3, "2": 5, ... "K": 4 }
},
"history": { "games_completed": 11, "recent_games": [] },
"you": {
"bot_id": "flat_basic",
"seat": 0,
"balance": 9990.0,
"current_bet_total": 10.0,
"current_hands": [ { "hand_id": "12-0-0", "wager": 10.0, "is_split_hand": 0, "is_completed": 0, "cards": [ ... ] } ]
},
"dealer": {
"upcard": { "rank": "K", "suit": "H" },
"hole_card_known": 0,
"cards_revealed": [ { "rank": "K", "suit": "H" } ],
"peek_checked": 1,
"has_blackjack": 0
},
"decision": {
"phase": "bet" | "insurance" | "play",
"hand_id": "12-0-0", // present during play decisions
"legal_actions": ["HIT","STAND","DOUBLE"] // phase-dependent
},
"max_insurance": 5 // present when insurance is offered
},
"legal_actions": ["BET" | "INSURANCE" | "NO_INSURANCE" | ...],
"min_bet": 1, // bet phase only
"max_bet": 100, // bet phase only
"max_insurance": 5, // insurance phase only
"time_budget_ms": 50
}Notes:
hand_id format: <game_id>-<seat>-<hand_index>.legal_actions is duplicated: inside st.decision for context and again top-level for ease of parsing.Bot must emit a single line JSON per request:
{
"type": "action_response",
"match_id": "demo-match",
"game_id": 12,
"bot_id": "flat_basic",
"action": "BET" | "INSURANCE" | "NO_INSURANCE" | "HIT" | "STAND" | "DOUBLE" | "SPLIT",
"amount": 10 // required for BET, optional for INSURANCE, ignored otherwise
}Validation per phase:
action must be BET; amount clamped to [min_bet, max_bet] and to available balance. Missing/invalid/over-range bets terminate the seat.action may be INSURANCE or NO_INSURANCE. If INSURANCE, amount is clamped to [0, max_insurance] and balance; sending 0 or omitting amount is treated as full max_insurance. Over-range/invalid terminates the seat.action must be one of the provided legal actions for that hand. DOUBLE/SPLIT also require sufficient balance at execution time; otherwise the seat is terminated./bin/sh -c <cmd>; stdin/stdout pipes carry JSON lines.time_budget_ms in the request is 50ms, but the engine actually waits up to ~500ms for a full line response. No/late response results in an empty buffer and subsequent termination.action strings, or missing required fields terminate the seat (balance set to 0, hand data cleared) for the rest of the match.