Euchre Bots (Kartenspiel)

10

Die Idee dieser Herausforderung ist einfach: Erstellen Sie einen Bot, um das Kartenspiel Euchre zu spielen.

Für diejenigen , die sie nicht wissen bereits, ich habe die Regeln Euchre geschrieben hier , da sie auf diese Herausforderung betreffen.

Ich empfehle die Verwendung von Python oder ähnlichem, aber die einzige wirkliche Einschränkung besteht darin, dass es mit dem Controller-Code kompatibel sein muss

Eingang:

Ihr Euchre-Bot erhält je nach aktueller Phase des Spiels oder der Runde unterschiedliche Eingaben. Im Allgemeinen erhalten Sie die Spielphase in der ersten Zeile, gefolgt von einem Komma und der Anzahl der Punkte, die Ihr Team hat, sowie den relevanten Daten in den folgenden Zeilen.

Chronologisch wird Ihr Bot in der folgenden Reihenfolge eingegeben:

Ordering Trump:
    js,ah,qc,ts,jc  // the cards in your hand
    2               // number of points your team has
    0               // number of tricks your team has taken
    ordering        // the phase of the game
    th              // the turned up card
    p,p             // each previous player’s decision

Naming Trump:
    js,ah,qc,ts,jc  // the cards in your hand
    2               // number of points your team has
    0               // number of tricks your team has taken
    naming          // the phase of the game
    p               // each previous player’s decision

Dealer Discarding:
    js,ah,qc,ts,jc  // the cards in your hand
    2               // number of points your team has
    0               // number of tricks your team has taken
    discard         // the phase of the game
    th              // the card you will pick up

Going alone:
    js,ah,qc,ts,jc  // the cards in your hand
    2               // number of points your team has
    0               // number of tricks your team has taken
    alone           // the phase of the game
    h               // the trump suit
    n,n             // each previous player’s decision

Your turn:
    js,ah,qc,ts,jc  // the cards in your hand
    2               // number of points your team has
    0               // number of tricks your team has taken
    turn            // the phase of the game
    h               // the trump suit
    td,8h,p         // each previous player’s card

Trick data:
                    // the cards in your hand (none, since this happens at the end of a trick)
    2               // number of points your team has
    1               // number of tricks your team has taken
    trick           // the phase of the game
    0               // the index of the following list that is your card
    js,tc,4d,js     // the cards played during the trick in the order they were played

Ausgabe:

Ihr Euchre-Bot hat je nach aktueller Phase des Spiels oder der Runde unterschiedliche Ausgaben.

Ordering Trump:
    p   //for pass
    OR
    o   //for order up

Naming Trump:
    p           //for pass
    OR ANY OF
    c,s,h,d     //the suit you want to name

Going alone:
    n   // no
    OR
    y   // yes

Your turn:
    js  //the card you want to play

Wertung:

Die Punktzahl Ihres Bots ist die Gesamtzahl der Spiele, die er gewinnt.

Ihr Bot spielt gegen jeden anderen Bot und ist immer mit einer Kopie von sich selbst verbunden.

Anmerkungen:

Hier ist eine einfache Vorlage in Python2.7:

#!/usr/bin/python2.7
import sys

data = sys.stdin.readlines()

hand = data[0].strip().split(',')   # Hand as a list of strings
points = int(data[1])       # Number of points
tricks = int(data[2])       # Number of tricks

out = ''

if data[3] == 'ordering':
    card = data[4]              # The upturn card
    prev = data[5].strip().split(',')   # The previous player's decisions as a list
    # Ordering logic
    out =       # 'o' or 'p'
elif data[3] == 'naming':
    prev = data[4].strip().split(',')   # The previous player's decisions as a list
    # Naming logic
    out =       # 'p', 'h', 's', 'c', or 'd'
elif data[3] == 'discard':
    card = data[4]              # The card you'll take
    # Discarding logic
    out =       # The card you want to discard
elif data[3] == 'alone':
    trump = data[4]             # The trump suit
    prev = data[5].strip().split(',')   # The previous player's decisions as a list
    # Alone logic
    out =       # 'y' for yes, 'n' for no
elif data[3] == 'turn':
    trump = data[4]             # The trump suit
    prev = data[5].strip().split(',')
    # Turn logic
    out =       # The card you want to play
elif data[3] == 'trick':
    trump = data[5]
    cards = data[6].strip().split(',')
    my_card = cards[int(data[4])]
    # Data logic

print(out)
  1. Es wird immer 4 Antworten geben. Wenn jemand alleine geht, lautet die Antwort seines Partners "p", wenn er an der Reihe ist.

  2. Ich habe versucht, die Menge der redundanten Eingaben zu reduzieren, um besonders klar zu sein:

    2a. Sowohl Ihre Position relativ zum Dealer / Leader als auch die Karte, die Ihr Partner gespielt hat, können durch die Anzahl der vorherigen Ausgaben bestimmt werden. Es gibt 1 Spieler zwischen Ihnen und Ihrem Partner. Wenn Sie beispielsweise "td, 8h, p" als letzte Zeile in Ihrem Zug erhalten, können Sie sehen, dass Ihr Partner eine 8h gespielt hat und das andere Team einen Spieler hat, der alleine geht.

  3. Wenn Sie neugierig sind, erfolgt der Deal auf traditionelle Weise (in zwei Runden abwechselnd Pakete mit 2 und 3 Karten), aber das ist für Ihren Bot nicht wirklich relevant, also ...

  4. Wenn der zweite Spieler beschließt, in der Trumpfphase zu bestellen, wird diese Phase fortgesetzt, aber ihre Ausgaben werden so gut wie ignoriert. Mit anderen Worten, wer zuerst bestellt, ist im Namers-Team, unabhängig von anderen Ausgaben.

  5. Im Folgenden sind die Standardeinstellungen für die verschiedenen Spielphasen aufgeführt. Wenn Sie für diese Runde keine gültige Antwort ausgeben, wird Ihre Antwort in die folgende geändert.

    Trump bestellen: p

    Trump benennen: p

    Abwerfen: (die erste Karte auf deiner Hand)

    Alleine gehen: n

    Du bist dran: (die erste legale Karte in deiner Hand)

  6. Hier ist der Controller-Code für Ihre Testzwecke.

    6a. Beachten Sie, dass Sie entweder 2 oder 4 Bot-Namen eingeben können. Wenn Sie 4 Bots vergeben, werden diese zufällig zusammengeführt, und bei 2 werden sie mit Kopien von sich selbst zusammengeführt.

    6b. Sie benötigen ein 'Bots'-Verzeichnis im selben Verzeichnis wie der Controller-Code, und Ihr Bot-Code muss sich im Bots-Verzeichnis befinden.

  7. Für diejenigen, die möchten, dass sich ihr Bot daran erinnert, welche Karten gespielt wurden, haben Sie während der "Trick" -Phase die Möglichkeit, Ihrem Bot mitzuteilen, welche Karten gespielt wurden. Sie können in eine Datei im Bots-Verzeichnis schreiben, solange diese Datei 1 KB nicht überschreitet.

Anzeigetafel:

Old Stager:  2
Marius:      1
Random 8020: 0
Die Bohnenstange
quelle
2
Ich würde empfehlen, Beispiel-Bots einzuschließen, um es den Leuten zu erleichtern, ihre Bots zu schreiben.
Nathan Merrill
3
Poste es als Einreichung. Das Problem mit diesem zufälligen Bot ist jedoch, dass er den Großteil der Eingaben, die Sie ihm geben, ignoriert. Die Leute lieben es, Code zu kopieren / einzufügen (und dann zu ändern). Je umfassender Ihre anfänglichen Bots sind, desto mehr Einsendungen (und bessere Einsendungen) erhalten Sie.
Nathan Merrill
1
Kann ich zu Recht davon ausgehen, dass der Bot, wenn er nicht der letzte Spieler der Runde ist, nicht wissen kann, was in der letzten Runde gespielt wurde?
Planapus
1
@Sleafar gut, wenn es eine Möglichkeit gab zu wissen, was während der aktuellen Runde gespielt wurde, konnte der Bot es in eine Datei schreiben, um den Überblick zu behalten.
Planapus
1
@NotthatCharles Ich habe die Regeln aktualisiert, um das Schreiben in eine Datei explizit zu ermöglichen
The Beanstalk

Antworten:

2

Marius

Ich habe diesen Bot in R geschrieben. Ich habe einige Tests mit Ihrem Controller durchgeführt und sie scheinen korrekt zu kommunizieren.

#!/usr/bin/Rscript
options(warn=-1)
infile = file("stdin")
open(infile)
input = readLines(infile,5)
hand = strsplit(input[1],",")[[1]]
phase = input[4]
if(!phase%in%c("discard","naming")) input = c(input,readLines(infile,1))
other_o = c("a","k","q","j","t","9")
alone = "n"
ord = "p"
trumpify = function(color){
    tr_suit = switch(color,
            "c" = c("c","s",rep("c",5)),
            "s" = c("s","c",rep("s",5)),
            "h" = c("h","d",rep("h",5)),
            "d" = c("d","h",rep("d",5)))
    paste(c("j","j","a","k","q","t","9"),tr_suit,sep="")
    }

if(phase%in%c("ordering","alone")){
    flip = input[5]
    if(phase=="ordering") trump = trumpify(substr(flip,2,2))
    if(phase=="alone") trump = trumpify(flip)
    hand_value = sum((7:1)[trump%in%c(hand,flip)])
    if(hand_value>13) ord = "o"
    if(hand_value>18) alone = "y"
    if(phase=="alone") cat(alone)
    if(phase=="ordering") cat(ord)
    }

if(phase=="naming"){
    name = "p"
    colors = unique(substr(hand,2,2))
    col_values = sapply(colors,function(x)sum((7:1)[trumpify(x)%in%hand]))
    if(any(col_values>13)){name = colors[which.max(col_values)]}
    cat(name)
    }

if(phase=="discard"){
    flip = input[5]
    new_hand = c(hand,flip)
    trump = trumpify(substr(flip,2,2))
    discardables = new_hand[!new_hand%in%trump]
    if(length(discardables)){
        val = sapply(substr(discardables,1,1),function(x)(6:1)[other_o==x])
        d = discardables[which.min(val)]
    }else{d = tail(trump[trump%in%new_hand],1)}
    cat(d)
    }

if(phase=="turn"){
    trump = trumpify(input[5])
    fold = strsplit(gsub("[[:punct:]]","",input[6]),",")[[1]]
    if(length(fold)&!any(is.na(fold))){
        fold_c = substr(fold[1],2,2)
        f_suit = if(fold_c!=input[5]){paste(other_o,fold_c,sep="")}else{trump}
        l = length(f_suit)
        current = (l:1)[f_suit%in%fold]
        if(any(hand%in%f_suit)){
            playable = hand[hand%in%f_suit]
            val = sapply(playable,function(x)(l:1)[f_suit==x])
            if(all(max(val)>current)){
                play = playable[which.max(val)]
            }else{play = playable[which.min(val)]}
        }else if(any(hand%in%trump)){
            playable = hand[hand%in%trump]
            val = sapply(playable,function(x)(7:1)[trump==x])
            if(!any(fold%in%trump)){
                play = playable[which.min(val)]
            }else{
                trumped = fold[fold%in%trump]
                val_o = max((7:1)[trump%in%trumped])
                play = ifelse(any(val>val_o), playable[which.min(val[val>val_o])], playable[which.min(val)])
            }
        }else{
            val = sapply(substr(hand,1,1),function(x)(6:1)[other_o==x])
            play = hand[which.min(val)]
            }
    }else{
        col = c("c","s","h","d")
        non_tr = col[col!=input[5]]
        aces = paste("a",non_tr,sep="")
        if(any(hand%in%aces)){
            play = hand[hand%in%aces][1]
        }else if(any(hand%in%trump)){
            playable = hand[hand%in%trump]
            val = sapply(playable,function(x)(7:1)[trump==x])
            play = playable[which.max(val)]
        }else{
            val = sapply(substr(hand,1,1),function(x)(6:1)[other_o==x])
            play = hand[which.max(val)]
        }
    }
    cat(play)   
}

Ich werde es wahrscheinlich später ändern, da ich keine "Turn" -Logik implementiert habe, wenn der Bot verteidigt, aber ich poste es jetzt, damit die Leute einen anderen Bot haben, gegen den sie testen können.

Im Moment implementiert es nur sehr grundlegende Strategien wie das Führen mit einem Ass, einem Trumpf oder einer anderen hohen Karte. wenn möglich mit einer höheren Karte folgen oder wenn nicht mit der Karte mit dem niedrigsten Wert spielen; Bestellen, wenn die Hand einen hohen Wert hat, und Benennen der Farbe, in der die Hand den höchsten Wert gehabt hätte; alleine gehen, wenn die Hand einen sehr hohen Wert hat. Der "Wert" jeder Karte wird sehr einfach berechnet: Der Wert der Trümpfe beginnt bei 7 für den ersten Wagenheber und nimmt entlang der Trumpffarbe ab.

Planapus
quelle
1

Alter Hase

Dieser Bot folgt einigen einfachen Regeln, die ihm lange Zeit gute Dienste geleistet haben:

  • Weisen Sie jeder Karte intuitiv eine Punktzahl zu
  • Wählen Sie den Trumpf, wenn die Handnote gut genug ist
  • Im Falle eines wirklich guten Handspiels alleine
  • Wählen Sie die beste Karte, wenn Sie zuerst spielen
  • Wähle eine bessere Karte als die Gegner, wenn sie gewinnen
  • Wählen Sie die schlechteste Karte, wenn der Partner gewinnt oder wenn ein Gewinn nicht möglich ist

Ich habe die Zielpunktzahl für Tests im Controller von 10 auf 100 erhöht. Die Ergebnisse sind immer noch sehr zufällig, aber viel stabiler als zuvor.

#!/usr/bin/python2.7
from __future__ import print_function
import sys, re, math

base = 1.2
playThreshold = 27.0
aloneThreshold = 36.0
sameColor = { 'd' : 'h', 'h' : 'd', 's' : 'c', 'c' : 's' , '' : '', 'n' : 'n' }
cardValue = { 'p' : 0, '9' : 1, 't' : 2, 'j' : 3, 'q' : 4, 'k' : 5, 'a' : 6 }

class Card(object):
    def __init__(self, name, trump):
        self.name = name
        self.value = cardValue[name[0:1]]
        self.suit = name[1:2]
        self.trump = False
        self.updateScore(trump)
    def updateScore(self, trump):
        self.score = self.value
        if self.suit == trump:
            self.trump = True
            self.score += 6
        if self.value == 3:
            if self.suit == trump:
                self.score = 14
            if self.suit == sameColor[trump]:
                self.trump = True
                self.score = 13

class Cards(object):
    def __init__(self, cards, trump):
        self.list = []
        self.score = 0.0
        if cards:
            for c in cards.split(','):
                self.append(Card(c, trump))
    def append(self, card):
        self.list.append(card)
        self.score += math.pow(base, card.score)
    def updateScore(self, trump):
        self.score = 0.0
        for card in self.list:
            card.updateScore(trump)
            self.score += math.pow(base, card.score)
    def best(self):
        card = self.list[0]
        for i in self.list[1:]:
            if i.score > card.score:
                card = i
        return card
    def worst(self):
        card = self.list[0]
        for i in self.list[1:]:
            if i.score < card.score:
                card = i
        return card
    def better(self, ref):
        card = None
        for i in self.list:
            if i.score > ref.score and (card is None or i.score < card.score):
                card = i
        return card

def ordering(hand, card, decisions):
    if len(decisions) == 3:
        hand.append(card)
    return 'o' if hand.score > playThreshold else 'p'

def naming(hand):
    result = 'p'
    score = playThreshold
    for trump in ['d', 'h', 's', 'c']:
        hand.updateScore(trump)
        if hand.score > score:
            result = trump
            score = hand.score
    return result

def turn(hand, decisions):
    bestIndex = -1
    for i, d in enumerate(decisions.list):
        if d.suit:
            bestIndex = i
            break
    if bestIndex == -1:
        return hand.best()
    else:
        suit = decisions.list[bestIndex].suit
        for i in range(2, len(decisions.list)):
            if (decisions.list[i].suit == suit or decisions.list[i].trump) and decisions.list[i].score > decisions.list[bestIndex].score:
                bestIndex = i
        matching = Cards('', '')
        for card in hand.list:
            if card.suit == suit:
                matching.append(card)
        if not matching.list:
            if bestIndex == len(decisions.list) - 2:
                return hand.worst()
            for card in hand.list:
                if card.trump:
                    matching.append(card)
            if not matching.list:
                return hand.worst()
        if bestIndex == len(decisions.list) - 2:
            return matching.worst()
        card = matching.better(decisions.list[bestIndex])
        if card:
            return card
        return matching.worst()

output = ''
input = re.split('\n', re.sub(r'[^a-z0-9,\n]+', '', sys.stdin.read()))

if input[3] == 'ordering':
    output = ordering(Cards(input[0], input[4][1:2]), Card(input[4], input[4][1:2]), input[5].split(','))
elif input[3] == 'naming':
    output = naming(Cards(input[0], 'n'))
elif input[3] == 'discard':
    output = Cards(input[0], input[4][1:2]).worst().name
elif input[3] == 'alone':
    output = 'y' if Cards(input[0], input[4]).score > aloneThreshold else 'n'
elif input[3] == 'turn':
    output = turn(Cards(input[0], input[4]), Cards(input[5], input[4])).name

print(output)
Sleafar
quelle
0

Zufällig 8020

Ein einfacher zufälliger Bot, der 80% der Zeit vergeht. Kommentieren Sie die letzte Zeile aus, um die (gelöschten) Ein- und Ausgaben anzuzeigen.

#!/usr/bin/python2.7
from __future__ import print_function
import sys, re, random

output = ''
input = re.split('\n', re.sub(r'[^a-z0-9,\n]+', '', sys.stdin.read()))
hand = input[0].split(',')

if input[3] == 'ordering':
    output = random.choice(['p', 'p', 'p', 'p', 'o'])
elif input[3] == 'naming':
    output = random.choice(['p', 'p', 'p', 'p', random.choice(hand)[1:2]])
elif input[3] == 'discard':
    output = random.choice(hand)
elif input[3] == 'alone':
    output = random.choice(['n', 'n', 'n', 'n', 'y'])
elif input[3] == 'turn':
    output =  random.choice(hand)
    if input[5]:
        suited = filter(lambda x: input[5][1:2] in x, hand)
        if suited:
            output = random.choice(suited)

print(output)
#print(input, " --> ", output, file=sys.stderr)
Sleafar
quelle