Konkurs BlackJack KOTH

13

Maczuga

Ponieważ świetnie się bawiłem, pracując nad oryginalnym wyzwaniem KOTH, chciałem wymyślić inne. Dla mnie frajdą z tych wyzwań AI jest udoskonalenie stosunkowo prostego bota, który subtelnie gra w bardzo prostą grę. Ze względu na probabilistyczny charakter gier karcianych uważam, że blackjack może być interesującą grą KOTH, podobnie jak TPD.

Wszystkie zasady wywodzą się z opisu BlackJacka z butami na tej stronie

Zasady dotyczące kart i talii

  • Boty grają przy stołach czterech (4) konkurentów i jednego (1) dealera
  • Jeden (1) but (tasowana talia) jest wspólny dla wszystkich graczy i krupiera, dopóki nie zostanie wyczerpany, w którym to momencie zostanie dodana nowa losowo tasowana talia i gra będzie kontynuowana. Boty NIE SĄ (obecnie) POWIADOMIANE o dodaniu tej nowej talii. Takie powiadomienie może zostać dodane, jeśli brak tej funkcji powoduje dostateczne cierpienie / kłopoty.
  • Istnieje wpisowe w wysokości 10 na rundę, a karty są bezpłatne
  • Idealna / idealna ręka ma wynik 21
  • Wszystkie karty twarzy mają wartość 10
  • Wszystkie karty numeryczne są warte swojej liczby
  • Asy są warte 11 lub 1. Zostanie to rozwiązane automatycznie przez ramę, a nie przez boty.
  • Zgodnie z zasadami wszystkie karty graczy są odkryte i są widoczne. Jedna z kart krupiera jest odkryta, a druga odkryta.

Punktacja

  • Wyniki powyżej 21, które używają asa jako 11, zmuszają asa do zmniejszenia wartości do 1
  • wyniki powyżej 21, których nie można zmusić poniżej progu 21, „pokonują” bota

Sprzedawca

  • Krupier losuje, dopóki nie odpadnie lub nie przekroczy wyniku 17, w którym to momencie jest zmuszony wstać

Zakłady i żetony

  • Na początku każdej rundy naliczane jest wpisowe 10, więc jest minimalna stawka 10, a minimalna stawka 1. UWAGA - zakład jest wartością bezwzględną argumentu zakładu, więc nie przejmuj się próbować ujemne zakłady.
  • Boty, których nie stać na wpisowe, są usuwane z konkursu
  • Podczas stawiania zakładów boty nie mogą stawiać więcej niż żetony, które mają
  • Jeśli zakład jest możliwy, zakład żetonów jest natychmiast usuwany z bota i dodawany do stawki
  • Wygranie zakładu daje botowi 2x zakład żetonów. Ponieważ jednak zakład jest odejmowany od żetonów bota, bot nawet się łamie, a następnie wygrywa 1x zakład.
  • Boty wygrywają zakłady tylko wtedy, gdy ich wynik jest wyższy niż u krupiera

Podział rozgrywki

Jedna ręka

  1. Na początku gry każdy gracz otrzymuje iteracyjnie jedną kartę i odejmuje od swoich żetonów opłatę wpisową w wysokości 10 USD / minimalny zakład.
  2. Dealer losuje
  3. Dokonane zostaje drugie podanie, a wszyscy gracze otrzymują kolejną kartę.
  4. Dealer losuje
  5. Następnie (w tej samej kolejności, w jakiej zostały zadane) każdy bot jest wykonywany zgodnie z opisem w sekcji „Interfejs programisty” i musi wykonać ruch lub stać. Zakłady są uważane za ruch. UWAGA, ŻE ZAKŁADY NIE WPŁYWAJĄ NA ZDOLNOŚĆ BOTÓW DO WYKONYWANIA INNYCH RUCHÓW. Bardzo możliwe jest obstawianie, a następnie losowanie karty. Możliwe jest dobranie wielu kart i postawienie zakładu przed postawieniem.
  6. Gdy wszystkie boty się zepsują lub staną, krupier gra do progu 17
  7. Wyniki botów są następnie porównywane z wynikami krupiera, zakłady są wygrane i przegrane

Jedna runda

Uważa się, że stanowi pięć (5) rąk. Pomiędzy rozdaniami lista uczestników jest sortowana w celu usunięcia graczy, a następnie przetwarzana dalej, aby zapewnić, że wszystkie boty zagrają taką samą liczbę rozdań (pod warunkiem, że liczba zgłoszeń nie będzie równomiernie rozłożona na stoły z czterema botami) ).

Interfejs programisty i legalne ruchy

Jak udokumentowano w pliku CardShark:

#   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.

Jak (teraz) udokumentowano w pliku Cards:

#       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":   '#'

Kod źródłowy systemu oceniania jest TUTAJ

Przykładowe boty

Lim 17

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

Języki wprowadzania

Obecnie obsługiwane są Java, c / c ++, Python i Lisp. Dołożymy starań, aby dołączyć zgłoszenia w innych językach, ale pamiętaj, że ostateczny konkurs zostanie przeprowadzony na Linux-ie.

Wybór zwycięzcy

Zwycięzcą zostałby autor bota, który konsekwentnie gromadził najwięcej żetonów w ciągu jeszcze określonej liczby stołów i rund. Zwycięzca zostanie ogłoszony 3 czerwca, ale ogłoszenie może zostać opóźnione, jeśli nadal będą nadsyłane zgłoszenia. Konkurs przedłużony na czas nieokreślony.

arrdem
źródło
Pytanie: czy widoczne karty obejmują karty w ręce gracza?
dmckee --- były moderator kociak
Drugie pytanie: czy wiemy, ile rozdano kart, których nie widzimy?
dmckee --- były moderator kociak
Odpowiedź na nr 1 - tak; Odpowiedź na nr 2 - w sposobie implementacji tego silnika nie ma ukrytych kart. karty widoczne to każda karta, która została rozdana z każdego buta zużytego podczas bieżącej rundy. Zwroty widocznych kart nie są usuwane na nowych butach (ponieważ część starego buta prawdopodobnie nadal jest w grze), ale są usuwane po zakończeniu rundy. Jest to wybór architektury, który podjąłem dla uproszczenia, który można zmienić, jeśli uznasz, że brak ukrytych kart jest problematyczny.
arrdem
Aktualizacja: sprawdź link do reguł. Silnik implementuje teraz ukryte karty, ale obecnie jedyną ukrytą kartą jest jedna z podstawowych kart rozdającego.
arrdem
W jaki sposób boty mogą odróżnić, która widoczna karta jest krupierem?
cthom06

Odpowiedzi:

3

BlackJackDavey

Nudne, staromodne ok. Powinien kompilować pod ANSI lub 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;
}

Strategia tutaj jest udokumentowana w komentarzach, ale jest bardzo prosta. Dodatkowe zakłady są zawierane tylko w dwóch przypadkach (niewystarczająca stawka na następną rundę lub podwojenie stawki) i może to wymagać zmiany.

Ta gra różni się od przewodników oferowanych graczom w kasynie tym, że nie ma konkretnych informacji na temat karty pokazującej krupiera (czy moglibyśmy uznać, że jest to ostatni wpis visible?), Więc niektóre z magicznych liczb są przypuszczeniami.

Może wymagać skromnego zamieszania w zależności od odpowiedzi na dwa pytania w komentarzach.

Imię z gry, moje imię i stara ballada ludowa .

dmckee --- były kot moderator
źródło
Dziesięć kart jest reprezentowanych przez znak T. Zaktualizuje post w konkursie o listę.
arrdem
3

Zakład liniowy

#!/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"

Ten bot jest modyfikacją strategii 17. Ten bot losuje, dopóki nie przekroczy wyniku 15 (f), a następnie obstawia żetony int (c * (score / 21)). W ten sposób bot będzie stawiać agresywnie tam, gdzie to możliwe.

arrdem
źródło