แลกเปลี่ยนช้างเผือก


11

เป็นวันคริสต์มาสในเดือนกรกฎาคมดังนั้นจะมีวิธีใดที่ดีไปกว่าการเฉลิมฉลองการแลกเปลี่ยนของขวัญช้างเผือกเสมือนจริง!

สำหรับการท้าทาย King of the Hill นี้คุณต้องสร้างบอทที่เล่นในการจำลองการแลกเปลี่ยนช้างเผือกพยายามที่จะได้รับของขวัญที่มีค่าสูงสุดเท่าที่จะทำได้

กฎของเกม

  • เกมจะเล่นในหลายรอบแต่ละรอบประกอบด้วยจำนวนรอบที่แตกต่างกัน
  • การตั้งค่ารอบ : จะมีของขวัญให้มากที่สุดเท่าที่มีผู้เล่นในเกมแต่ละค่าจะมีการสุ่มอย่างสม่ำเสมอในช่วง [0 ... 1) ด้วยค่านี้จะไม่เป็นที่รู้จักจนกว่าจะมีการเปิด "ปัจจุบัน" ผู้เล่นจะถูกสุ่มวางในคิว ผู้เล่นคนแรกจะถูกตอกจากด้านหน้าคิว
  • เมื่อถึงคราวของผู้เล่นพวกเขาอาจจะเปิดของขวัญหรือขโมยของขวัญของผู้เล่นคนอื่นผ่านหันไปหาผู้เล่นที่ถูกขโมยในปัจจุบัน
    • ของขวัญแต่ละชิ้นอาจถูกขโมยได้สูงสุด 3 ครั้ง
    • คุณไม่สามารถขโมยจากผู้เล่นที่ขโมยมาจากคุณ
    • ผู้เล่นแต่ละคนสามารถมีเพียงหนึ่งครั้งในปัจจุบัน
  • หลังจากเปิดของขวัญเล่นไปยังผู้เล่นคนต่อไปที่โผล่ออกมาจากด้านหน้าของคิว นี่จะเป็นผู้เล่นคนถัดไปตามลำดับที่ยังไม่ได้เทิร์น
  • รอบสุดท้าย : เมื่อเปิดของขวัญทั้งหมดแล้วรอบนั้นจะสิ้นสุดลงและมูลค่าของของขวัญที่ผู้เล่นแต่ละคนถืออยู่จะถูกเพิ่มเข้าไปในคะแนนของผู้เล่นนั้น รอบใหม่เริ่มต้นขึ้นโดยผู้เล่นแต่ละคนจะไม่มีของขวัญและผู้เล่นสั่งสับ
  • Game End : เกมจะจบลงเมื่อผู้เล่นอย่างน้อยหนึ่งคนมีคะแนน100 500 คะแนนโดยชัยชนะจะมอบให้กับผู้เล่นที่มีมูลค่ารวมสูงสุดของของขวัญ

การเข้ารหัส

การส่งทั้งหมดควรเข้ากันได้กับ Python 3.7 WhiteElephantBotคุณต้องเขียนชั้นเรียนที่สืบทอดโดยตรงจาก ตัวอย่างเช่น

class FooBot(WhiteElephantBot):
    # Your implementation here

คุณอาจจะให้__init__วิธีการ (ซึ่งจะใช้เวลาหนึ่งอาร์กิวเมนต์name) super().__init__(name)ในชั้นเรียนบอทของคุณซึ่งจะต้องเรียก ชั้นเรียนของคุณจะต้องมีtake_turnวิธีการที่คาดหวังข้อโต้แย้งต่อไปนี้ในลำดับนี้:

  • players: รายชื่อผู้เล่นตามลำดับของผู้เล่นทั้งหมดที่ยังไม่มีของขวัญ
  • presents: พจนานุกรมที่แมปชื่อผู้เล่นกับ 2-tuples ที่มีมูลค่าปัจจุบันที่ผู้เล่นคนนั้นถือครองและจำนวนครั้งที่มีการขโมย ซึ่งจะรวมถึงผู้เล่นอื่น ๆ ที่กำลังถือของขวัญอยู่ในปัจจุบัน
  • just_stole: หากการกระทำล่าสุดถูกขโมยไปจะเป็นชื่อของผู้เล่นที่ขโมยมา Noneถ้าไม่ได้ก็จะเป็น

แต่ละข้อโต้แย้งจะไม่เปลี่ยนรูปหรือวัตถุใหม่เพื่อให้กลายพันธุ์ใด ๆ ของพวกเขาจะไม่ส่งผลกระทบต่อเกม คุณสามารถเก็บสำเนาของข้อโต้แย้งใด ๆ หากคุณต้องการ

ค่าตัวอย่างสำหรับpresents:

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

take_turnวิธีการของคุณควรคืนชื่อผู้เล่นที่คุณต้องการขโมยจากหรือNoneเพื่อเปิดของขวัญ หากมีข้อยกเว้นให้ส่งคืนสิ่งอื่นที่ไม่ใช่strหรือNoneหรือชื่อของผู้เล่นที่คุณไม่สามารถขโมยได้คุณจะเปิดของขวัญตามค่าเริ่มต้น

ตัวสร้างของคุณจะถูกเรียกที่จุดเริ่มต้นของแต่ละรอบดังนั้นคุณจะไม่จำสถานะจากรอบต่อรอบ

โดยการสืบทอดจากWhiteElephantBotคุณจะสามารถเข้าถึงsteal_targetsวิธีการที่จะนำเสนอ dict และjust_stoleและส่งกลับรายชื่อผู้เล่นที่คุณสามารถขโมยได้

โมดูลใด ๆ ที่คุณต้องการสคริปต์จะต้องนำเข้าที่ด้านบนของรายการของคุณ

ทดสอบไดร์เวอร์

ขับทดสอบที่สามารถพบได้ที่นี่ คุณไม่จำเป็นต้องรวมfrom white_elephant import WhiteElephantBotไว้ในคำตอบที่โพสต์ของคุณอย่างไรก็ตามโมดูลท้องถิ่นจะต้องทำเช่นนั้น

คู่แข่งหลัก

  • สุ่ม : สุ่มเลือกว่าจะเปิดของขวัญใหม่หรือขโมยพร้อมกับเป้าหมายขโมยที่เลือกแบบสุ่ม
  • โลภ : ขโมยของกำนัลที่มีค่าที่สุดที่สามารถถูกขโมยได้ หากไม่สามารถขโมยของขวัญได้ให้เปิดของกำนัล
  • นิสัยดี : เปิดของขวัญใหม่เสมอ ไม่เคยขโมย

กฎเพิ่มเติม

  • มันเป็นความรับผิดชอบของคุณในการตรวจจับข้อยกเว้นทั้งหมด หากคลาสของคุณไม่สามารถตรวจพบข้อยกเว้นคลาสนั้นจะถูกตัดสิทธิ์ นอกจากนี้โปรดอย่าจับ KeyboardInterrupts
  • อย่าใช้ไฟล์หรือวิธีการอื่นเพื่อหลีกเลี่ยงการไม่สามารถบันทึกสถานะระหว่างเกมได้ ตัวอย่างเช่นคุณไม่สามารถบันทึกสถานะเครือข่ายนิวรัลลงในไฟล์ที่เรียกใช้งานได้
  • บอทของคุณจะต้องอยู่ในรหัสชั้นเรียนและค่าคงที่ที่เกี่ยวข้อง
  • คุณสามารถใช้การนำเข้าไลบรารีมาตรฐานเท่านั้น
  • ไม่มีข้อกำหนดด้านประสิทธิภาพที่เข้มงวด มีเหตุผลและรอบคอบ หากประสิทธิภาพกลายเป็นปัญหาฉันขอสงวนสิทธิ์ในการเพิ่มการ จำกัด เวลา
  • หนึ่งรายการต่อคน หากคุณส่งมากกว่าหนึ่งรายการบอทของคุณอาจไม่ทำงานร่วมกัน ตอนนี้ฉันจะอนุญาตให้เข้าร่วมหลายรายการต่อคนแม้ว่าฉันจะทำการตรวจสอบอีกครั้งในภายหลังหากเป็นปัญหา
  • นี่เป็นการแข่งขันแบบเปิดที่ไม่มีวันสิ้นสุดที่แตกต่างกัน มันจะเริ่มใหม่ทุกครั้งที่ฉันสามารถทำได้เมื่อมีการเปลี่ยนแปลงที่สำคัญ

แก้ไข 1: เปลี่ยนคะแนนผู้ชนะจาก 100 เป็น 500 เพื่อให้การจัดอันดับสอดคล้องกันมากขึ้น โปรแกรมควบคุมการทดสอบมีการแก้ไขข้อบกพร่องใหม่และยังสะท้อนถึงการเปลี่ยนแปลงคะแนนชนะ

EDIT2: ชี้แจงบันทึกการนำเข้าที่ต้องการ


ลีดเดอร์บอร์ด (จนถึง 8 ส.ค. 2561)

  1. SampleBot (500.093)
  2. LastMinuteBot (486.163)
  3. RobinHood (463.160)
  4. OddTodd (448.825)
  5. GreedyBot (438.520)
  6. SecondPlaceBot (430.598)
  7. ThresholdBot (390.480)
  8. นักพนัน (313.362)
  9. NiceBot (275.536)
  10. RandomBot (256.172)
  11. GoodSamaritan (136.298)

สามารถขโมยได้จำนวนเท่าใดในแถว? เมื่อฉันเล่นมักจะมีการ จำกัด จำนวนการขโมย 2 ครั้งต่อแถวหรือบางสิ่งและบุคคลที่สามจากนั้นจะต้องเปิดหนึ่งรายการ นี่เป็นการป้องกันไม่ให้ของขวัญเดียวกันถูกขโมยมากกว่าหนึ่งครั้งต่อเทิร์น
mbomb007

@ mbomb007 ใช่ การขโมยโซ่จะไม่ จำกัด ยกเว้นกฎอื่น ๆ ที่ทำให้ของขวัญบางอย่างมีภูมิคุ้มกันต่อการขโมย: ของขวัญแต่ละชิ้นจะถูกขโมยได้ 3 ครั้งเท่านั้นและคุณไม่สามารถขโมยจากผู้เล่นที่ขโมยมาจากคุณได้
Beefster

คุณสามารถขโมยของกำนัลแล้วขโมยของที่คุณมีอยู่เดิมได้หรือไม่?
Erik the Outgolfer

@EriktheOutgolfer: ใช่ตราบใดที่มีการเลี้ยวอีกครั้งระหว่างนั้น คุณไม่สามารถขโมยซ้ำได้ทันทีหลังจากที่ของขวัญของคุณถูกขโมย
Beefster

1
แลกเปลี่ยนชาวอเมริกัน! ถัดไปเป็นปาร์ตี้วันเกิดที่แชร์
ngm

คำตอบ:


3

LastMinuteBot

(ด้วยความขอบคุณอย่างมากต่อ @Mnemonic สำหรับโครงกระดูกของรหัสเนื่องจากฉันแทบจะไม่รู้จัก Python)

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

ใช้ประโยชน์จากความจริงที่ว่าของขวัญไม่สามารถถูกขโมยได้เกินกว่าสามครั้งทำอย่างที่สามขโมยตัวคุณเองถ้าคุณพบว่ามีของกำนัลมูลค่าสูงและของขวัญส่วนใหญ่เปิดแล้ว


เรียบง่าย แต่สวยงาม
r_j

2

Odd Todd

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

คงที่ขอบคุณ! อย่าใช้ Python มาก
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

ThresholdBot

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

SampleBot

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

Robinhood

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

GoodSamaritan

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.