Euchre bots (jogo de cartas)

10

A ideia deste desafio é simples: crie um bot para jogar o jogo de cartas Euchre.

Para aqueles de vocês que ainda não os conhecem, escrevi as regras para Euchre aqui no que se refere a esse desafio.

Eu recomendo usar python ou algo semelhante, mas a única restrição real é que ele deve ser compatível com o código do controlador

Entrada:

Seu bot de euchre receberá diferentes tipos de entrada, dependendo da fase atual do jogo ou rodada. De um modo geral, você terá a fase do jogo na primeira linha, seguida por uma vírgula e o número de pontos que sua equipe possui e, em seguida, os dados relevantes nas linhas a seguir.

Cronologicamente, seu bot receberá informações na seguinte ordem:

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

Resultado:

O seu euchre bot terá saídas diferentes, dependendo da fase atual do jogo ou rodada.

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

Pontuação:

A pontuação do seu bot é o número total de jogos que ele vence.

Seu bot jogará contra todos os outros bot e sempre será associado a uma cópia de si mesmo.

Notas:

Aqui está um modelo simples em 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. Sempre haverá 4 respostas no total. Se alguém for sozinho, a resposta do parceiro será "p" por sua vez.

  2. Tentei reduzir a quantidade de entrada redundante, para ficar mais claro:

    2a Sua posição em relação ao dealer / líder e a carta que seu parceiro jogou podem ser determinadas pelo número de saídas anteriores. Há 1 jogador entre você e seu parceiro. Por exemplo, se você receber "td, 8h, p" como a última linha do seu turno, poderá ver que seu parceiro jogou 8h e o outro time tem um jogador que está indo sozinho.

  3. Se você estiver curioso, o acordo é feito da maneira tradicional (em duas rodadas, pacotes alternados de 2 e 3 cartões), mas isso não é realmente relevante para o seu bot, então ...

  4. Se o segundo jogador decidir fazer o pedido na fase de trunfo, essa fase continuará, mas suas saídas serão praticamente ignoradas. Em outras palavras, quem encomendar primeiro estará na equipe do Namers, independentemente de qualquer outra saída.

  5. A seguir, são apresentados os padrões para as várias fases do jogo. Se você não der uma resposta válida para essa rodada, sua resposta será alterada para o que está abaixo.

    Ordenação Trump: p

    Nomeando Trump: p

    Descartando: (a primeira carta na sua mão)

    Sozinho: n

    Sua vez: (a primeira carta legal na sua mão)

  6. Aqui está o código do controlador para fins de teste.

    6a Observe que você pode passar 2 ou 4 nomes de bots, se você der 4 bots, eles serão parceiros aleatoriamente e, com 2, eles serão parceiros com cópias de si mesmos.

    6b Você precisa de um diretório 'bots' no mesmo diretório que o código do controlador, e seu código bot deve estar no diretório bots.

  7. Para aqueles que querem que o bot se lembre de quais cartas foram jogadas, você terá a oportunidade durante a fase de "truques", que informa ao bot quais cartas foram jogadas. Você pode gravar em um arquivo no diretório de bots, desde que esse arquivo não exceda 1kb.

Placar:

Old Stager:  2
Marius:      1
Random 8020: 0
The Beanstalk
fonte
2
Eu recomendaria a inclusão de bots de amostra para facilitar a escrita dos bots.
Nathan Merrill
3
Publique como um envio. No entanto, o problema com esse bot aleatório é que ele ignora a maioria da entrada que você está fornecendo. As pessoas gostam de copiar / colar (depois modificar) o código; portanto, quanto mais abrangentes forem seus bots iniciais, mais envios (e melhores envios) você receberá.
Nathan Merrill
11
Estou certo em assumir que, a menos que o bot seja o último jogador do turno, ele não tem como saber o que foi jogado no último turno?
plannapus
11
@ Sleafar bem, se houvesse uma maneira de saber o que foi jogado durante o turno atual, o bot poderia gravá-lo em um arquivo, a fim de acompanhar.
plannapus
11
@NotthatCharles Eu atualizei as regras para permitir explicitamente escrita para um arquivo
O Beanstalk

Respostas:

2

Marius

Eu escrevi esse bot na R. Fiz alguns testes com seu controlador e eles parecem se comunicar corretamente.

#!/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)   
}

Provavelmente vou modificá-lo mais tarde, pois não implementei uma lógica de "virada" para quando o bot está defendendo, mas estou publicando agora para que as pessoas tenham outro bot para testar.

Por enquanto, apenas implementa estratégias muito básicas, como liderar com um ás, um trunfo ou qualquer outra carta alta; seguindo com uma carta mais alta quando possível ou jogando a carta com o menor valor, se não; ordenar quando a mão tem alto valor e nomear a cor na qual a mão teria o maior valor; indo sozinho quando a mão tem um valor muito alto. O "valor" de cada carta é calculado com muita simplicidade: o valor de trunfos começa em 7 para o primeiro valete e diminui ao longo do naipe de trunfos.

plannapus
fonte
1

Old Stager

Este bot segue algumas regras simples que o serviram bem por um longo tempo:

  • Atribuir intuitivamente uma pontuação a cada cartão
  • Escolha o trunfo se a pontuação da mão é boa o suficiente
  • No caso de uma mão realmente boa jogar sozinho
  • Escolha a melhor carta ao jogar primeiro
  • Escolha uma carta melhor que a dos oponentes se eles estiverem ganhando
  • Escolha a pior carta se o parceiro estiver ganhando ou se não for possível ganhar

Aumentei a pontuação alvo de 10 para 100 para teste no controlador. Os resultados ainda são muito aleatórios, mas muito mais estáveis ​​do que antes.

#!/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
fonte
0

Random 8020

Um bot aleatório simples, que passará 80% do tempo. Remova o comentário da última linha para ver as entradas e saídas (limpas).

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