Euchre Bots (карточная игра)

10

Идея этой задачи проста: создать бота для игры в карточную игру Euchre.

Для тех из вас , кто уже не знает их, я выписал правило к Euchre здесь , как они относятся к этой проблеме.

Я рекомендую использовать python или что-то подобное, но единственное реальное ограничение - это то, что он должен быть совместим с кодом контроллера

Входные данные:

Ваш euchre бот будет получать различные виды ввода в зависимости от текущей фазы игры или раунда. Вообще говоря, вы получите фазу игры в первой строке, за которой следуют запятая и количество очков, которые имеет ваша команда, а затем соответствующие данные в следующих строках.

Хронологически ваш бот получит информацию в следующем порядке:

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

Вывод:

Ваш бот euchre будет иметь разные результаты в зависимости от текущей фазы игры или раунда.

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

Подсчет очков:

Оценка вашего бота - это общее количество игр, в которых он выиграл.

Ваш бот будет играть против любого другого бота, и он всегда будет сотрудничать с собственной копией.

Ноты:

Вот простой шаблон в 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. Там всегда будет 4 полных ответов. Если кто-то пойдет один, то ответ его партнера будет «р» на их ходу.

  2. Я попытался сократить количество избыточных входных данных, чтобы быть более ясным:

    2а. Ваша позиция относительно дилера / лидера и карта, которую сыграл ваш партнер, могут быть определены по количеству предыдущих выходов. Между вами и вашим партнером есть 1 игрок. Например, если в качестве последней строки на вашем ходу вы выбрали «td, 8h, p», вы можете увидеть, что ваш партнер сыграл 8h, а у другой команды есть игрок, который едет один.

  3. Если вам интересно, сделка совершается традиционным способом (в два раунда чередующихся пакетов по 2 и 3 карты), но это не очень важно для вашего бота, так что ...

  4. Если второй игрок решит сделать заказ в фазе козыря, эта фаза продолжится, но его результаты будут в значительной степени проигнорированы. Другими словами, тот, кто заказывает первым, входит в команду Namers, независимо от других результатов.

  5. Ниже приведены значения по умолчанию для различных этапов игры. Если вы не выводите правильный ответ для этого раунда, то ваш ответ будет изменен на следующий.

    Заказ Трампа: р

    Нейминг Трамп: р

    Сбрасывание: (первая карта в вашей руке)

    Одинокий: n

    Ваш ход: (первая легальная карта в вашей руке)

  6. Вот код контроллера для ваших целей тестирования.

    6а. Обратите внимание, что вы можете передать либо 2, либо 4 имени бота, если вы дадите ему 4 бота, то они будут случайным образом сотрудничать, а с 2 они будут иметь свои копии.

    6b. Вам нужен каталог 'bots' в том же каталоге, что и код контроллера, а код вашего бота должен находиться в каталоге bots.

  7. Для тех, кто хочет, чтобы их бот запомнил, какие карты были разыграны, вам предоставляется возможность во время фазы «трюка», которая сообщает вашему боту, какие карты были разыграны. Вы можете записать в файл в каталоге ботов, если этот файл не превышает 1 КБ.

Табло:

Old Stager:  2
Marius:      1
Random 8020: 0
Бобовый стебель
источник
2
Я бы порекомендовал включить образцы ботов, чтобы людям было легче писать свои боты.
Натан Меррилл
3
Опубликуйте это как представление. Однако проблема этого случайного бота заключается в том, что он игнорирует большую часть вводимых вами данных. Люди любят копировать / вставлять (а затем модифицировать) код, поэтому, чем полнее будут ваши первоначальные боты, тем больше заявок (и лучше) вы получите.
Натан Меррилл
1
Правильно ли я предполагаю, что если бот не является последним игроком на терне, у него нет возможности узнать, что было сыграно в последнем ходу?
plannapus
1
@Sleafar хорошо, если бы был способ узнать, что было воспроизведено во время текущего хода, бот мог бы записать это в файл, чтобы отслеживать.
plannapus
1
@NotthatCharles Я обновил правила, чтобы явно разрешить запись в файл
The Beanstalk

Ответы:

2

Marius

Я написал этого бота в R. Я провел несколько тестов с вашим контроллером, и они, кажется, общаются правильно.

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

Я, вероятно, изменю это позже, так как я не реализовал логику «поворота», когда бот защищается, но я публикую это сейчас, чтобы у людей был еще один бот для тестирования.

На данный момент он реализует только очень простые стратегии, такие как ведение с тузом, козырем или любой другой старшей картой; следуя по возможности более высокой карте или разыгрывая карту с наименьшей ценностью, если нет; упорядочение, когда рука имеет высокое значение, и наименование цвета, в котором рука имела бы наибольшее значение; идти одному, когда рука имеет очень высокую ценность. «Значение» каждой карты вычисляется очень просто: значение козырей начинается с 7 для первого валета и уменьшается вдоль масти козыря.

plannapus
источник
1

Старый Stager

Этот бот следует некоторым простым правилам, которые долгое время служили ему хорошо:

  • Интуитивно присваивайте оценку каждой карточке
  • Выберите козырь, если оценка руки достаточно хороша
  • В случае действительно хорошей руки играйте в одиночку
  • Выберите лучшую карту при первой игре
  • Выберите лучшую карту, чем противники, если они выигрывают
  • Выберите худшую карту, если партнер выигрывает или выиграть невозможно

Я увеличил целевой показатель с 10 до 100 для тестирования в контроллере. Результаты все еще очень случайные, но более стабильные, чем раньше.

#!/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
источник
0

Случайный 8020

Простой случайный бот, который пройдет 80% времени. Раскомментируйте последнюю строку, чтобы увидеть (очищенный) ввод и вывод.

#!/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
источник