13
\$\begingroup\$

BlackJack

As I had a blast working on the original KOTH challenge, I wanted to come up with another one. For me, the fun of these AI challenges is in refining a comparatively simple bot which plays a very simple game subtly. Due to the probabilistic nature of card games, I think that blackjack could be an interesting KOTH game just like TPD.

All the rules are derived from this website's description of BlackJack with shoes

Rules Regarding Cards & the Deck

  • Bots play at tables of four (4) competitors and one (1) dealer
  • One (1) shoe (a shuffled deck) is shared by all players and the dealer until it is exhausted, at which point a new randomly shuffled deck will be added and play will continue. The bots ARE NOT (at present) NOTIFIED of the addition of this new deck. Such notification may be added if lack of this feature causes sufficient distress/trouble.
  • There is a buy-in of 10 per round, and cards are free
  • Perfect/ideal hand has a score of 21
  • All face cards have a value of 10
  • All numeric cards are worth their number
  • Aces are worth 11 or 1. this will be dealt with automatically by the framework, not the bots.
  • As per the rules, all players' cards are dealt face-up and are visible. One of the dealer's cards is face-down and the other is face-up.

Scoring

  • Scores in excess of 21 which use an ace as 11 force the ace to reduce in value to 1
  • scores in excess of 21 which cannot be coerced below the threshold of 21 "bust" the bot

The Dealer

  • The dealer draws until he busts, or excedes a score of 17 at which point he is forced to stand

Betting and Chips

  • At the start of each round, a buy-in of 10 is charged, so there is a minimum stake of 10, and a minimum bet of 1. NOTE - the bet is the absolute value of the bet argument, so don't bother trying negative bets.
  • Bots which cannot afford the buy-in are removed from the contest
  • When making bets, bots cannot bet more than the chips they have
  • If the bet is possible, the chips bet are emmidiately removed from the bot and added to the stake
  • Winning a bet gives the bot 2x chips bet. However because the bet is subtracted from the bot's chips, the bot breaks even and then wins 1x the bet.
  • Bots win bets only if their score is greater than that of the dealer

Gameplay Breakdown

One Hand

  1. When the game starts, each player is iteratively dealt one card, and has the $10 buy-in fee/minimum bet subtracted from their chips.
  2. The dealer draws
  3. A second pass is made, and another card is dealt to all players.
  4. The dealer draws
  5. Then (in the same order as they were dealt to) each bot is executed as described in the "Programmer's Interface" section and must make a move or stand. Betting is considered a move. NOTE THAT BETTING DOES NOT AFFECT BOTS' ABILITY TO MAKE FURTHER MOVES. It is very possible to bet and then draw a card, and it is possible to draw multiple cards and them bet before standing.
  6. When all the bots have busted or stood, the dealer plays to its threshold of 17
  7. The scores of the bots are then compared to that of the dealer, bets are won and lost

One Round

Is considered to constitute five (5) hands. In-between hands, the list of contestants is sorted to remove players and then further processed to ensure that all the bots play the same number of hands (a provision for the fact that the number of entries will not devide evenly among four-bot tables).

Programmer's Interface and Legal Moves

As documented in the CardShark file:

#   DOCUMENTATION
#       INPUT SPECIFICATION
#          $ ./foo.bar <hand-score> <hand> <visible cards> <stake> <chips>
#          <hand-score>     is the present integer value of the player's hand.
#          <hand>           is a space-free string of the characters [1-9],A,J,Q,K
#          <visible cards>  every dealt card on the table. when new shoes are brought
#                           into play, cards drawn therefrom are simply added to this list
#                           NOTE: the first TWO (2) cards in this list belong to the dealer.
#                             one however will be "hidden" by a "#". the other is visible.
#                           !!! THE LIST IS CLEARED AT THE END OF HANDS, NOT SHOES !!!
#          <stake>          the  number of chips which the bot has bet this hand
#          <chips>          the number of chips which the bot has
#       SAMPLE INPUT
#          $ ./foo.bar 21 KJA KQKJA3592A 25 145
#
#       OUTPUT SPECIFICATION
#          "H"|"S"|"D"|"B"  (no quotes in output)
#          "H"              HIT - deal a card
#          "S"              STAND - the dealer's turn
#          "D"              DOUBLEDOWN - double the bet, take one card. FIRST MOVE ONLY
#          "B 15"           BET - raises the bot's stakes by $15.

As (now) documented in the Cards file:

#       class CARD
#           card is a container for representing paper playing cards in
#           otherwise fairly functional programming.
#           letter()
#               gets the letter used to identify the card in a string  
#               LETTER MAPPINGS  
#                   Ace     :   'A'
#                   Two     :   '2'
#                   Three   :   '3'
#                   Four    :   '4'
#                   Five    :   '5'
#                   Six     :   '6'
#                   Seven   :   '7'
#                   Eight   :   '8'
#                   Nine    :   '9'
#                   Ten     :   'T'
#                   Jack    :   'J'
#                   Queen   :   'Q'
#                   King    :   'K'
#                   "Hidden":   '#'

The source code for the scoring system is HERE

Sample Bots

Lim 17

#!/usr/bin/env python
import sys
s = sys.argv
if int(s[1]) < 17:
    print "H"
else:
    print "S"

Entry Languages

At present, Java, c/c++, Python and Lisp are supported. A reasonable effort will be made to include submissions in other languages, but remember that the final contest will be run on a Linux box.

Winner Selection

The winner would be the author of the bot which consistently accrued the most chips over a yet-to-be determined number of tables and rounds. The winner will be announced on June 3rd, but the announcement may be delayed if there are still submissions coming in. Contest extended indefinitely.

\$\endgroup\$
  • \$\begingroup\$ Question: does visible-cards include those in the players own hand? \$\endgroup\$ – dmckee May 28 '11 at 1:10
  • \$\begingroup\$ Second question: do we know how many cards have been dealt that we can't see? \$\endgroup\$ – dmckee May 28 '11 at 1:23
  • \$\begingroup\$ Answer to #1 - yes; Answer to #2 - the way this engine is implemented, there are no hidden cards. visible-cards is every card which has been dealt from every shoe consumed during the current round. visible-cards returns is cleared not on new shoes (because part of the old shoe is probably still in play) but is instead cleared on round termination. This is an architecture choice I made for simplicity, which can be revised if you find the lack of hidden cards problematic. \$\endgroup\$ – arrdem May 28 '11 at 2:26
  • \$\begingroup\$ Update: check the rules link. The engine now implements hidden cards, but the only hidden card at present is one of the dealer's base cards. \$\endgroup\$ – arrdem May 28 '11 at 3:38
  • \$\begingroup\$ How can bots distinguish which visible card is the dealers? \$\endgroup\$ – cthom06 Jun 2 '11 at 19:47
3
\$\begingroup\$

BlackJackDavey

Boring, old fashioned c. Should compiler under ANSI or c99.

/* BlackJackDavey
 *
 * A entry for
 * http://codegolf.stackexchange.com/questions/2698/a-blackjack-koth-contest
 * copyright 2011 
 *
 * Currently expects a slightly extended version of the spec. Two
 * expected changes:
 * - Tens will be represented as 'T'
 * - The visible card string will include '#' for those cards whose
 *     *backs* we can see (slight improvement in card counting technique)
 * 
 * No disaster if neither feature is present, just sligtly degraded
 * performance.
 */
#include <stdio.h>
#include <string.h>

/* A full deck has a total value of 4*( (11*5) + (3*10) + ace ) where
 * ace is 11 or according to our need.
 **/
int fullWeight(const int current){
  int ace = (current>10) ? 1 : 11;
  return 4 * ( 11*5 + 3*10 + ace);
}
/* Return the value of a particular card in the context of our
 * current score
 */
int cardWeight(const char c, const int current){
 switch (c) {
 case '1': case '2': case '3': case '4': case '5':
 case '6': case '7': case '8': case '9':
   return (c - '0');
 case 'T': case 'J': case 'Q': case 'K':
   return 10;
 case 'A':
   return current>10 ? 1 : 11;
 }
 return 0;
}
/* returns the mean card *value* to be expected from the deck 
 *
 * Works by computing the currently unknown value and diviing by the
 * number of remaining cards 
 */
float weight(const char*known, const int current){
  int weight = fullWeight(current);
  int count=52;
  int uCount=0;
  const char*p=known;
  while (*p != '\0') {
    if (*p == '#') { /* Here '#' is a stand in for the back of a card */
      uCount++;
    } else {
      weight -= cardWeight(*p,current);
    }
    count--;
    p++;
    if ( count==0 && *p != '\0') {
      count += 52;
      weight += fullWeight(current);
    }
  }
  return (1.0 * weight)/(count+uCount);
}


int main(int argc, char*argv[]){
  int score=atoi(argv[1]);
  const char*hand=argv[2];
  const char*visible=argv[3];
  int stake=atoi(argv[4]);
  int chips=atoi(argv[5]);

  /* If current stake is less than 10, bet all the rest because a loss
     does not leave us enough to continue */
  if (chips < 10 && chips > 0) {
    printf("B %d\n",chips);
    return 0;
  }
  /* First round stategy differs from the rest of the game */
  if (strlen(hand)==2 && stake==10) {
    switch(score){
    case 10:
    case 11: /* Double down on particularly strong hands */
      if (chips >= 10) {
    printf("D\n");
    return 0;
      }
      break;
    default:
      break;
    };
  }
  /* In future rounds or when first round spcialls don't apply it is
     all about maximizing chance of getting a high score */
  if ((score + weight(visible,score)) <= 21) {
    /* if the oods are good for getting away with it, hit */
    printf("H\n");
    return 0;
  }
  /* Here odd are we bust if we hit, but if we are too low, the dealer
     probably makes it.*/
  printf("%c\n", score>14 ? 'S' : 'H');
  return 0;
}

The strategy here is documented in the comments, but is very straight ahead. Additional bets are made in only two cases (not enough stake for the next round, or double down), and this may need to change.

The game differs some from the guides offered for casino gamblers in that there is no specific information about the dealer's showing card (or can we could on that being the last entry in visible?), so some of the magic numbers are guesses.

May need some modest diddling depending on the answer to two questions in the comments.

Name from the game, my first name, and the old folk ballad.

\$\endgroup\$
  • \$\begingroup\$ The ten card IS represented by the T character. Will update the contest post with the list. \$\endgroup\$ – arrdem May 28 '11 at 2:31
3
\$\begingroup\$

Linear Bet

#!/usr/bin/env python
from __future__ import division
import sys
s = sys.argv

c=150    # chip modifier
f=15     # stand score

if int(s[1]) < f:
    print "H"
else:
    if int(s[4]) == 10:
        print "B", (int(s[1])/21)*c
    else:
        print "S"

This bot is a modification of the 17 strategy. This bot draws until it exceeds a score of 15 (f) and then bets int(c*(score/21)) chips. This way the bot will bet aggressively wherever possible.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.