흰 코끼리 교환


11

7 월의 크리스마스이기 때문에 가상의 흰 코끼리 선물 교환보다 축하하는 더 좋은 방법!

이 King of the Hill 도전을 위해서는 화이트 엘리펀트 교환 시뮬레이션 에서 플레이하는 봇을 만들어야합니다 .

게임 규칙

  • 게임은 여러 차례에 걸쳐 진행되며, 각 라운드는 가변 회전 수로 구성됩니다.
  • 라운드 설정 : 게임에 플레이어가있는 수만큼의 선물이 있으며, 각각의 값은 [0 ... 1) 범위 내에서 무작위로 균일하게 평가되며,이 값은 현재가 열릴 때까지 알 수 없습니다. 플레이어는 대기열에서 무작위 순서로 배치됩니다. 첫 번째 플레이어는 대기열의 전면에서 튀어 나옵니다.
  • 플레이어의 턴인 경우, 선물을 열거 나 다른 플레이어의 현재를 훔쳐서 도난당한 플레이어에게 차례를 넘길 수 있습니다.
    • 각 선물은 3 번까지 도난 당할 수 있습니다.
    • 당신은 당신에게서 훔친 플레이어로부터 훔칠 수 없습니다.
    • 각 플레이어는 한 번에 하나의 선물 만 가질 수 있습니다.
  • 선물이 열리면 큐의 앞쪽에서 튀어 나온 다음 플레이어로 진행합니다. 아직 턴을하지 않은 다음 차례의 플레이어가됩니다.
  • 라운드 엔드 : 모든 선물이 열리면 라운드가 끝나고 각 플레이어가 보유한 현재 값이 해당 플레이어의 점수에 추가됩니다. 각 플레이어는 현재 선물을 가지고 있지 않고 새로운 순서가 시작되고 새로운 라운드가 시작됩니다.
  • 게임 종료 : 적어도 하나 명의 선수가 득점에있을 때 게임이 종료됩니다 100 승리는 선물의 합이 높은 선수에게 수여와 함께, 500 점.

코딩

모든 제출물은 Python 3.7과 호환되어야합니다. 에서 직접 상속되는 클래스를 작성해야합니다 WhiteElephantBot. 예를 들어 :

class FooBot(WhiteElephantBot):
    # Your implementation here

bot 클래스에서를 호출해야하는 __init__메소드 (한 개의 인수를 취함)를 제공 할 수 있습니다 . 클래스 에는 다음 순서로 다음과 같은 인수를 예상 하는 메소드 가 있어야합니다 .namesuper().__init__(name)take_turn

  • players: 아직 선물이없는 모든 플레이어의 플레이어 이름 목록입니다.
  • presents: 플레이어 이름을 해당 플레이어가 보유한 현재 값과 현재 도난 횟수가 포함 된 2 개의 튜플에 매핑하는 사전입니다. 여기에는 현재 선물을 들고있는 다른 플레이어 만 포함됩니다.
  • just_stole: 마지막으로 취한 행동이 도둑질 이었다면, 방금 훔친 플레이어의 이름이됩니다. 그렇지 않으면 None입니다.

각 인수는 변경 불가능하거나 새로운 객체이므로이를 변경하면 게임에 영향을 미치지 않습니다. 원하는 경우 모든 주장의 사본을 보관할 수 있습니다.

에 대한 예제 값 presents:

{
    'Alice':   (0.35, 0),
    'Bob':     (0.81, 2),
    'Charlie': (0.57, 1)
}

당신의 take_turn방법은 당신이 훔치거나 None선물을 열 플레이어의 이름을 반환해야합니다 . 예외가 발생 str하거나 또는 이외의 것을 반환 하거나 None도용 할 수없는 플레이어의 이름이 있으면 기본적으로 선물을 엽니 다.

생성자는 각 라운드의 시작 부분에서 호출되므로 라운드에서 라운드까지의 상태를 기억할 수 없습니다.

에서 상속을 받으면 선물을 받아 훔칠 수있는 플레이어 이름 목록을 반환하는 방법에 WhiteElephantBot액세스 할 수 있습니다.steal_targetsjust_stole

스크립트가 필요한 모든 모듈은 항목 상단에 가져와야합니다.

테스트 드라이버

테스트 드라이버는 여기 에서 찾을 수 있습니다 . from white_elephant import WhiteElephantBot게시 된 답변 에 포함 할 필요는 없지만 로컬 모듈이 그렇게해야합니다.

기준 경쟁사

  • 랜덤 : 새로운 선물을 열거 나 훔칠 지 여부를 무작위로 선택합니다.
  • 욕심 : 도난 당할 수있는 가장 소중한 선물을 훔칩니다. 선물을 도난 당할 수 없으면 선물을여십시오.
  • Nice : 항상 새로운 선물을 엽니 다. 도둑질하지 마십시오.

추가 규칙

  • 모든 예외를 잡는 것은 귀하의 책임입니다. 수업에서 예외를 잡지 못하면 실격 처리됩니다. 또한 KeyboardInterrupts를 놓치지 마십시오.
  • 게임간에 상태를 저장할 수 없다는 것을 우회하기 위해 파일이나 다른 방법을 사용하지 마십시오. 예를 들어 신경 네트워크 상태를 실행 중 파일에 저장할 수 없습니다.
  • 봇은 클래스 코드 및 관련 상수 내에 자체적으로 포함되어야합니다.
  • 표준 라이브러리 가져 오기만 사용할 수 있습니다.
  • 엄격한 성능 요구 사항은 없습니다. 합리적이고 신중해야합니다. 성능이 문제가되면 제한 시간을 추가 할 권리가 있습니다.
  • 한 사람당 한 항목. 하나 이상의 출품작을 제출하면 봇이 함께 작동하지 않을 수 있습니다. 지금은 1 인당 여러 항목을 허용 할 예정이지만 문제가 될 경우 나중에 다시 금지 할 수 있습니다.
  • 이것은 종료 날짜가없는 공개 경쟁입니다. 중요한 변경 사항이있을 때 가능하면 언제든지 다시 실행됩니다.

EDIT1 : 순위가 더 일관되도록 우승 점수를 100에서 500으로 변경했습니다. 테스트 드라이버에는 새로운 버그 수정이 있으며 승리 점수 변경 사항도 반영합니다.

EDIT2 : 필요한 가져 오기에 대한 설명이 명확합니다.


리더 보드 (2018 년 8 월 8 일 현재)

  1. 샘플 봇 (500.093)
  2. LastMinuteBot (486.163)
  3. 로빈 후드 (463.160)
  4. OddTodd (448.825)
  5. 욕심 봇 (438.520)
  6. SecondPlaceBot (430.598)
  7. 임계 값 봇 (390.480)
  8. 도박꾼 (313.362)
  9. 니스 봇 (275.536)
  10. 랜덤 봇 (256.172)
  11. 선한 사마리아인 (136.298)

연속으로 여러 도둑이있을 수 있습니까? 내가 게임을 할 때, 보통 연속으로 2 개의 훔치기가 제한되며, 3 번째 사람이 열어야합니다. 이렇게하면 같은 선물이 한 차례에 두 번 이상 도난 당하지 않습니다.
mbomb007

@ mbomb007 예. 체인 도둑질은 특정 선물이 도둑질에 면역이되는 다른 규칙을 제외하고는 무제한입니다. 각 선물은 3 회만 도난 당할 수 있으며 방금 훔친 플레이어에게서 도용 할 수 없습니다.
Beefster

선물을 훔친 다음 가지고 있던 원래 물건을 다시 훔칠 수 있습니까?
Outgolfer Erik

@EriktheOutgolfer : 그렇습니다. 현재 도난당한 직후에는 다시 도둑질 할 수 없습니다.
Beefster

1
양키 스왑!? 다음은 공유 생일 파티?
ngm

답변:


3

LastMinuteBot

(파이썬을 거의 알지 못하기 때문에 코드의 골격에 대한 @Mnemonic 덕분에 크게 감사드립니다.)

class LastMinuteBot(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):
        targets = self.steal_targets(presents, just_stole)
        if len(targets) <= 1:
            return None

        target = None

        # If most of the presents are already distributed, try to steal an 
        #  un-restealable gift of high value
        if len(presents) > (len(players) + len(presents)) * 0.75:
            at_threshold = [t for t in targets if presents[t][1]==2 and presents[t][0]>=0.8]
            if at_threshold:
                target = max(at_threshold, key=lambda x: presents[x][0])

        # Otherwise, take the best available
        if not target:
            target = max(targets, key=lambda x: presents[x][0])

        return target if presents[target][0] > 0.5 else None

선물을 세 번 이상 도난 당할 수 없다는 사실을 이용하십시오. 고가의 선물을 발견하고 대부분의 선물이 열렸다면 세 번째로 자신을 훔치십시오.


간단하지만의 beautifull
r_j

2

홀수 토드

class OddTodd(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):

        targets = self.steal_targets(presents, just_stole)

        # if none to steal, pick present
        if len(targets) <= 1:
            return None

        # steals the best gift that he can, as long as he's the 1st/3rd steal
        targets = [t for t in targets if presents[t][1] % 2 == 0]
        if targets:
            return max(targets, key=lambda x:presents[x][0])

        else:
            return None

그가 할 수있는 최고의 선물을 훔치지 만 선물을 훔친 두 번째 사람이되고 싶지는 않습니다. 선물을 훔친 경우 다시받을 수 없기 때문입니다.


11 행의 구문 오류입니다 . 목록 이해에서 ==대신이 필요합니다 =.
Beefster

고마워요! 파이썬을 많이 사용하지 마십시오.
brian_t

1

SecondPlaceBot

class SecondPlaceBot(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):
        targets = self.steal_targets(presents, just_stole)
        if len(targets) <= 1:
            return None

        # If most of the presents are already distributed, take the second best.
        if len(presents) > (len(players) + len(presents)) * 0.8:
            target = sorted(targets, key=lambda x: presents[x][0])[-2]
        # Otherwise, take the best and hope someone steals it later.
        else:
            target = max(targets, key=lambda x: presents[x][0])

        return target if presents[target][0] > 0.5 else None

모두가 가장 소중한 선물을 위해 싸울 것입니다. 다음으로 최고의 선물은 거의 훌륭하지만 도난 당할 가능성은 훨씬 적습니다.


1

임계 값봇

import random

class ThresholdBot(WhiteElephantBot):
    def __init__(self, name):
        self.name = name
        # Choose a minimum value to be happy.
        self.goal = 1 - random.random() ** 2

    def take_turn(self, players, presents, just_stole):
        # Find who has a gift that's sufficiently valuable.
        targets = self.steal_targets(presents, just_stole)
        targets = [x for x in targets if presents[x][0] >= self.goal]
        targets = sorted(targets, key=lambda x: presents[x][0])

        if not targets:
            return None

        # Choose a target (biased toward the best gifts).
        weighted = []
        for i, target in enumerate(targets, 1):
            weighted += [target] * i ** 2
        return random.choice(weighted)

우리는 정말로 최고의 선물을 받는 것에 관심이없고 , 단지 좋은 무언가 입니다. 도둑질 할만한 것이있는 한 그렇게 할 것입니다.


1

샘플 봇

import random

class SampleBot(WhiteElephantBot):
    def rollout(self, values, counts, just_stole, next_move):
        targets = set()
        move_chosen = False
        for i, (v, n) in enumerate(zip(values, counts)):
            if v and n < 3 and i != just_stole and i != 0:
                targets.add(i)
        for i in range(len(values)):
            if values[i]:
                break
            while True:
                if not targets:
                    break
                if move_chosen:
                    j = max(targets, key=lambda i: values[i])
                    if values[j] < 0.5:
                        break
                else:
                    move_chosen = True
                    if next_move is None:
                        break
                    j = next_move
                values[i] = values[j]
                counts[i] = counts[j] + 1
                values[j] = 0
                counts[j] = 0
                if just_stole is not None and counts[just_stole] < 3:
                    targets.add(just_stole)
                if j in targets:
                    targets.remove(j)
                just_stole = i
                i = j
            values[i] = random.random()
            for player in (just_stole, i):
                if player is not None and values[player] and counts[player] < 3:
                    targets.add(player)
        return values[0]
    def take_turn(self, players, presents, just_stole, n_rollouts=2000):
        names = [self.name] + players + list(presents.keys())
        values = [presents[name][0] if name in presents else None for name in names]
        counts = [presents[name][1] if name in presents else 0 for name in names]
        if just_stole is not None:
            just_stole = names.index(just_stole)
        targets = [None]
        for i, (v, n) in enumerate(zip(values, counts)):
            if v and n < 3 and i != just_stole and i != 0:
                targets.append(i)
        if len(targets) == 1:
            return targets[0]
        scores = [0. for _ in targets]
        n = n_rollouts // len(targets)
        for i, target in enumerate(targets):
            for _ in range(n):
                scores[i] += self.rollout(list(values), list(counts), just_stole, target) / float(n)
        target_index = targets[scores.index(max(scores))]
        if target_index is None:
            return None
        return names[target_index]

각 플레이어가 탐욕스럽게 행동하는 2000 개의 시뮬레이션을 실행하고 최상의 행동을 선택합니다.


이 봇은 정확히 무엇을합니까?
Beefster

@Beefster 각 플레이어가 탐욕스럽게 행동하는 2000 개의 랜덤 게임을 실행하고 최고 평균 최종 점수로 이동을 선택합니다.
user1502040

이름 오류. 무작위로 가져와야합니다.
Beefster

1

로빈 후드

class RobinHood(WhiteElephantBot):       
    def take_turn(self, players, presents, just_stole):
        #get the possible steal targets
        targets = self.steal_targets(presents, just_stole)
        #who stole his gift?
        targets = [x for x in targets if presents[x][1] > 0]
        #sort by value
        targets = sorted(targets, key=lambda x: presents[x][0])        
        #only steal back if it's worth it        
        targets = [x for x in targets if presents[x][0] > 0.5]

        if len(targets)>0:
           return targets.pop()

선물을 얻지 못한 부자들을 훔치십시오


들여 쓰기 오류가 있습니다.
Beefster

0

좋은 사마리아인

class GoodSamaritan(WhiteElephantBot):     
    def take_turn(self, players, presents, just_stole):  
        targets = self.steal_targets(presents, just_stole)

         #if only one player has a gift, don't steal it!
        if len(presents)<=1 or len(targets)==0:
             return None
        else:       
             #Steal the worst present  
             return min(targets, key=lambda x: presents[x][0])

운이 좋은 사람들에게 행운을 빕니다


0

노름꾼

class Gambler(WhiteElephantBot):
    def take_turn(self, players, presents, just_stole):        
        #get the possible steal targets
        targets = self.steal_targets(presents, just_stole)        

        #last player 
        if len(players)==0:
            #lets gamble! Try and get the highest score
            return None

        #If you are not last, steal the best gift that can be restolen so maybe you can become the last player
        targets = [t for t in targets if presents[t][1]<2 ]
        if targets:
            return max(targets, key=lambda x: presents[x][0])   

도박꾼은 중독되어 마지막 플레이어가 되려고 시도한 다음 다른 모든 플레이어를 이길 새로운 선물로 도박을합니다.


0

Top3Bot

class Top3Bot(WhiteElephantBot):
    def __init__(self, name):
        super().__init__(name)
        self.firstturn = True

    def take_turn(self, players, presents, just_stole):
        if self.firstturn:
            num_presents = len(players) + len(presents) + 1
            self.value_limit = (num_presents - 3) / num_presents
            self.firstturn = False

        targets = self.steal_targets(presents, just_stole)

        if players:
            targets += None

        return max(
            targets,
            key=lambda name: self.steal_ranking(name, presents, len(players))
        )


    def steal_ranking(self, name, presents, presents_remaining):
        if name is None:
            return (0, 0)

        present_value = presents[name][0]
        num_steals = presents[name][1]
        if present_value >= self.value_limit:
            if num_steals == 2:
                return (5, present_value)
            elif  num_steals == 0:
                return (4, -presemt_value)
            elif num_steals == 1 and presents_remaining == 0:
                return (3, -present_value)
            else:
                return (-1, present_value)
        else:
            if num_steals < 2:
                return (2, present_value)
            else:
                return (-2, present_value)

이 봇은 가능한 최고의 선물을 얻으려고하지 않지만> = (n-3) / n 인 선물을 얻으려고합니다. 여기서 n은 선물 수입니다. 대부분의 경우, 많은 가치가있는 선물이있을 것이며, Top3Bot은 이들 중 하나에 손을 대려고 노력할 것이지만, 그가 얻는 사람 중 누구를 실제로 신경 쓰지는 않습니다.


당신 __init__self주장 이 빠져 있습니다
Beefster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.