किंग ऑफ द हिल: स्पीड क्लू AI


24

स्पीड क्लू

Cluedo / Clue एक सम्मोहक कटौती गेमप्ले घटक के साथ एक क्लासिक बोर्ड गेम है। स्पीड क्लू 3-6 खिलाड़ी संस्करण है जो केवल कार्ड का उपयोग करके इस घटक पर जोर देता है। नतीजा यह है कि मानक Cluedo और Speed ​​Clue के बीच एकमात्र अंतर यह है कि खेल में अभी भी प्रत्येक खिलाड़ी कोई भी सुझाव दे सकता है, जिसे वह अपनी बारी पर प्रसन्न करने के बजाय पासा रोल और अन्य खिलाड़ियों के सुझावों की दया पर एक विशिष्ट कमरे तक पहुंचने के लिए इंतजार कर सकता है। यदि आपने पहले कभी भी Cluedo नहीं खेला है, या दो संस्करणों के बीच स्पष्ट अंतर के बारे में निश्चित होना चाहते हैं, तो आप यहां एक पूर्ण गति नियम निर्धारित कर सकते हैं


लक्ष्य

15 मई 2014 00:00 GMT से पहले स्पीड क्लू खेलने के लिए AI प्रोग्राम लिखें और सबमिट करें। उस समय के बाद, मैं सभी कानूनी प्रविष्टियों का उपयोग करके एक टूर्नामेंट चलाऊंगा। टूर्नामेंट में सबसे अधिक गेम जीतने वाला एंट्री चुनौती जीतता है।


ऐ विनिर्देशों

आप अपनी AI को अपने द्वारा चुनी गई किसी भी भाषा में लिख सकते हैं, जो भी तकनीक आप उपयोग करते हैं, इसलिए जब तक वह सर्वर के साथ गेम खेलने के लिए एक टीसीपी / आईपी कनेक्शन पर एप्लिकेशन प्रोटोकॉल का सख्ती से उपयोग करता है। सभी प्रतिबंधों का विस्तृत विवरण यहां पाया जा सकता है


कैसे खेलें

प्रतियोगिता GitHub रिपॉजिटरी को फोर्क करके प्रारंभ करें । entriesअपने StackExchange उपयोगकर्ता नाम का उपयोग करके निर्देशिका नाम के तहत एक निर्देशिका जोड़ें , और उस फ़ोल्डर में अपना कोड विकसित करें। जब आप अपनी प्रविष्टि सबमिट करने के लिए तैयार हों, तो अपने संशोधनों के साथ एक पुल अनुरोध करें, फिर इस साइट पर अपनी प्रविष्टि की घोषणा करने के लिए इन निर्देशों का पालन करें ।

मैंने coreआपको शुरू करने के लिए निर्देशिका में कुछ कोड और JAR प्रदान किए हैं ; सामग्री के लिए किसी न किसी गाइड के लिए मेरी साइट देखें । इसके अलावा, अन्य खिलाड़ी अपनी प्रविष्टियों के अतिरिक्त सहायक कोड जमा कर रहे हैं ताकि आपको उठने और चलने में मदद मिल सके। प्रविष्टियों का पता लगाने के लिए कुछ समय लें, और सबमिट करने से पहले दूसरों की प्रविष्टियों के खिलाफ अपनी प्रविष्टि का परीक्षण करना न भूलें!


परिणाम

Place | User         | AI                 | Result
------+--------------+--------------------+-------------------------------------------------------
    1 | gamecoder    | SpockAI            | 55.75%
    2 | Peter Taylor | InferencePlayer    | 33.06%
    3 | jwg          | CluePaddle         | 20.19%
    4 | Peter Taylor | SimpleCluedoPlayer |  8.34%
    5 | gamecoder    | RandomPlayer       |  1.71%
 ---- | ray          | 01                 | Player "ray-01" [3] sent an invalid accuse message: ""

ऊपर दिए गए परिणामों से पता चलता है कि प्रत्येक योग्य AI के पास 25,200 वैध मैच थे, जिसमें उसने भाग लिया था। कुल 30,000 मैच थे जो परिणामों की ओर गिने गए, और 6,100 या तो जब 01अयोग्य घोषित किए गए थे, तब छूट दी गई थी।

किरण के 01एआई में जाने के लिए एक सम्माननीय उल्लेख की आवश्यकता है । मेरे शुरुआती परीक्षण से पता चला कि यह सबसे मजबूत था, और मुझे उम्मीद थी कि यह प्रतियोगिता जीत जाएगी। हालांकि, यह एक बहुत ही आंतरायिक बग प्रतीत होता है, जहां तक ​​मैं अनुमान लगा सकता हूं, यह सभी संभावित समाधानों को खत्म करने की ओर ले जाता है। टूर्नामेंट ने सभी तीन-खिलाड़ियों के मैचों को समाप्त कर दिया था और चार खिलाड़ियों के मैचों (12,000 खेलों में!) की 01बग का खुलासा होने पर शुरू किया था। यदि मैं केवल 3-प्लेयर मैच स्टैंडिंग पर विचार करता हूं, तो परिणाम इस तरह दिखते हैं:

Place | User         | AI                 | Result
------+--------------+--------------------+--------
    1 | ray          | 01                 | 72.10%
    2 | gamecoder    | SpockAI            | 51.28%
    3 | Peter Taylor | InferencePlayer    | 39.97%
    4 | Peter Taylor | SimpleCluedoPlayer | 17.65%
    5 | jwg          | CluePaddle         | 16.92%
    6 | gamecoder    | RandomPlayer       |  2.08%

मैंने परिणामों पर कुछ डेटा खनन करने की योजना बनाई थी, लेकिन मैं समाप्त हो गया हूं। मुझे प्रतियोगिता के दौरान (पावर फेल्योर, सिस्टम रिबूट) के माध्यम से दौड़ने में तकनीकी दिक्कतें आईं, जिससे आगे बढ़ने के लिए प्रतियोगिता सर्वर को पूरी तरह से पुनर्लेखन करने की आवश्यकता थी। मैं उन सभी परिणाम फ़ाइलों के साथ कोड में सभी परिवर्तनों की टिप्पणी करूंगा और उन सभी फ़ाइलों के साथ उत्पन्न होऊंगा जो किसी के भी हित में हैं। यदि मैं डेटा खनन भी करने का निर्णय लेता हूं, तो मेरे परिणाम भी रिपॉजिटरी में जोड़े जाएंगे।


खेलने के लिए शुक्रिया!


4
क्या आप प्रवेश करने के लिए अपने सर्वर की एक प्रति उपलब्ध करा सकते हैं?
पीटर टेलर

you must accept two port numbers: the first will be the port to which your program will listen, and the second will be the port to which your program will send., दो बंदरगाह क्यों?
हस्त्कर्ण

1
@PeterTaylor, जैसे ही मैंने इसे लिखा है, मैं सर्वर की एक प्रति उपलब्ध कराऊंगा। आपको क्यों लगता है कि मैं एक महीना दे रहा हूं? ;)
सादाकात्सू

@Hasturkun, सर्वर के लिए मैंने जो आर्किटेक्चर की योजना बनाई है, वह यह है कि यह कमांड लाइन के माध्यम से आपकी प्रस्तुतियाँ शुरू करेगा। यह चुनेगा कि कौन सा पोर्ट प्रत्येक प्रोग्राम को संदेश भेजने के लिए उपयोग करेगा ताकि यह आसानी से पहचान सके कि कौन सा प्रोग्राम है (ध्यान दें कि प्रोटोकॉल में कोई पहचानकर्ता शामिल नहीं है)। इसके अलावा, प्रत्येक प्रोग्राम को संदेश भेजने के लिए किस पोर्ट को जानना है ताकि सर्वर वास्तव में संदेश प्राप्त कर सके। ये दो पोर्ट हैं जो प्रत्येक सबमिशन को कमांड लाइन तर्क के रूप में प्राप्त करना चाहिए।
सादाकात्सु

1
केवल नेटवर्किंग प्रोग्राम जो मैंने लिखा है वह यूडीपी का उपयोग करता है। मैंने टीसीपी / आईपी का उपयोग करने का फैसला किया (1) दोनों और (2) के बीच किसी भी अंतर को समझने के लिए उस तकनीक का उपयोग करें जो काम करने के लिए मुझे आवश्यक लॉक-स्टेप प्लेयर अपडेट का समर्थन करता है।
सादकात्सु

जवाबों:


5

एआई 01 - पायथन 3

मुझे अभी तक इसके लिए एक बेहतर नाम नहीं मिला :-P

पहचानकर्ता : रे-एआई 01

प्रौद्योगिकी : पायथन 3

चयनित : हाँ

तर्क :ai01.py identifier port

विवरण : inference द्वारा कार्य करें। जब कार्ड की संख्या जो मालिक को ज्ञात नहीं है, एक सीमा से कम है, तो यह एआई पुनरावर्ती वैश्विक अनुमान द्वारा सभी असंभव समाधानों को समाप्त करना शुरू कर देता है। अन्यथा, यह स्थानीय इंजेक्शन का उपयोग करता है।

#!/usr/bin/env python
import itertools

from speedclue.playerproxy import Player, main
from speedclue.cards import CARDS
from speedclue.protocol import BufMessager

# import crash_on_ipy


class Card:
    def __init__(self, name, type):
        self.name = name
        self.possible_owners = []
        self.owner = None
        self.in_solution = False
        self.disproved_to = set()
        self.type = type

    def __repr__(self):
        return self.name

    def log(self, *args, **kwargs):
        pass

    def set_owner(self, owner):
        assert self.owner is None
        assert self in owner.may_have
        for player in self.possible_owners:
            player.may_have.remove(self)
        self.possible_owners.clear()
        self.owner = owner
        owner.must_have.add(self)
        self.type.rest_count -= 1

    def set_as_solution(self):
        # import pdb; pdb.set_trace()
        assert self.owner is None
        self.type.solution = self
        self.in_solution = True
        for player in self.possible_owners:
            player.may_have.remove(self)
        self.possible_owners.clear()
        self.type.rest_count -= 1

    def __hash__(self):
        return hash(self.name)


class CardType:
    def __init__(self, type_id):
        self.type_id = type_id
        self.cards = [Card(name, self) for name in CARDS[type_id]]
        self.rest_count = len(self.cards)
        self.solution = None


class PlayerInfo:
    def __init__(self, id):
        self.id = id
        self.must_have = set()
        self.may_have = set()
        self.selection_groups = []
        self.n_cards = None

    def __hash__(self):
        return hash(self.id)

    def set_have_not_card(self, card):
        if card in self.may_have:
            self.may_have.remove(card)
            card.possible_owners.remove(self)

    def log(self, *args, **kwargs):
        pass

    def update(self):
        static = False
        updated = False
        while not static:
            static = True
            if len(self.must_have) == self.n_cards:
                if not self.may_have:
                    break
                for card in self.may_have:
                    card.possible_owners.remove(self)
                self.may_have.clear()
                static = False
                updated = True
            if len(self.must_have) + len(self.may_have) == self.n_cards:
                static = False
                updated = True
                for card in list(self.may_have):
                    card.set_owner(self)

            new_groups = []
            for group in self.selection_groups:
                group1 = []
                for card in group:
                    if card in self.must_have:
                        break
                    if card in self.may_have:
                        group1.append(card)
                else:
                    if len(group1) == 1:
                        group1[0].set_owner(self)
                        updated = True
                        static = False
                    elif group1:
                        new_groups.append(group1)
            self.selection_groups = new_groups

            if len(self.must_have) + 1 == self.n_cards:
                # There is only one card remain to be unknown, so this card must
                # be in all selection groups
                cards = self.may_have.copy()
                for group in self.selection_groups:
                    if self.must_have.isdisjoint(group):
                        cards.intersection_update(group)

                for card in self.may_have - cards:
                    static = False
                    updated = True
                    self.set_have_not_card(card)

        # assert self.must_have.isdisjoint(self.may_have)
        # assert len(self.must_have | self.may_have) >= self.n_cards
        return updated


class Suggestion:
    def __init__(self, player, cards, dplayer, dcard):
        self.player = player
        self.cards = cards
        self.dplayer = dplayer
        self.dcard = dcard
        self.disproved = dplayer is not None


class AI01(Player):
    def prepare(self):
        self.set_verbosity(0)

    def reset(self, player_count, player_id, card_names):
        self.log('reset', 'id=', player_id, card_names)
        self.fail_count = 0
        self.suggest_count = 0
        self.card_types = [CardType(i) for i in range(len(CARDS))]
        self.cards = list(itertools.chain(*(ct.cards for ct in self.card_types)))
        for card in self.cards:
            card.log = self.log
        self.card_map = {card.name: card for card in self.cards}
        self.owned_cards = [self.card_map[name] for name in card_names]
        self.players = [PlayerInfo(i) for i in range(player_count)]
        for player in self.players:
            player.log = self.log
        self.player = self.players[player_id]
        for card in self.cards:
            card.possible_owners = list(self.players)
        n_avail_cards = len(self.cards) - len(CARDS)
        for player in self.players:
            player.may_have = set(self.cards)
            player.n_cards = n_avail_cards // player_count \
                + (player.id < n_avail_cards % player_count)
        for card in self.owned_cards:
            card.set_owner(self.player)
        for card in self.cards:
            if card not in self.owned_cards:
                self.player.set_have_not_card(card)
        self.suggestions = []
        self.avail_suggestions = set(itertools.product(*CARDS))
        self.possible_solutions = {
            tuple(self.get_cards_by_names(cards)): 1
            for cards in self.avail_suggestions
        }
        self.filter_solutions()

    def filter_solutions(self):
        new_solutions = {}
        # assert self.possible_solutions
        join = next(iter(self.possible_solutions))
        for sol in self.possible_solutions:
            for card, type in zip(sol, self.card_types):
                if card.owner or type.solution and card is not type.solution:
                    # This candidate can not be a solution because it has a
                    # card that has owner or this type is solved.
                    break
            else:
                count = self.check_solution(sol)
                if count:
                    new_solutions[sol] = count
                    join = tuple(((x is y) and x) for x, y in zip(join, sol))
        self.possible_solutions = new_solutions
        updated = False
        for card in join:
            if card and not card.in_solution:
                card.set_as_solution()
                updated = True
                self.log('found new target', card, 'in', join)

        # self.dump()
        return updated

    def check_solution(self, solution):
        """
        This must be called after each player is updated.
        """
        players = self.players
        avail_cards = set(card for card in self.cards if card.possible_owners)
        avail_cards -= set(solution)
        if len(avail_cards) >= 10:
            return 1
        count = 0

        def resolve_player(i, avail_cards):
            nonlocal count
            if i == len(players):
                count += 1
                return
            player = players[i]
            n_take = player.n_cards - len(player.must_have)
            cards = avail_cards & player.may_have
            for choice in map(set, itertools.combinations(cards, n_take)):
                player_cards = player.must_have | choice
                for group in player.selection_groups:
                    if player_cards.isdisjoint(group):
                        # Invalid choice
                        break
                else:
                    resolve_player(i + 1, avail_cards - choice)

        resolve_player(0, avail_cards)
        return count

    def suggest1(self):
        choices = []
        for type in self.card_types:
            choices.append([])
            if type.solution:
                choices[-1].extend(self.player.must_have & set(type.cards))
            else:
                choices[-1].extend(sorted(
                    (card for card in type.cards if card.owner is None),
                    key=lambda card: len(card.possible_owners)))

        for sgi in sorted(itertools.product(*map(lambda x:range(len(x)), choices)),
                key=sum):
            sg = tuple(choices[i][j].name for i, j in enumerate(sgi))
            if sg in self.avail_suggestions:
                self.avail_suggestions.remove(sg)
                break
        else:
            sg = self.avail_suggestions.pop()
            self.fail_count += 1
            self.log('fail')
        self.suggest_count += 1
        return sg

    def suggest(self):
        sg = []
        for type in self.card_types:
            card = min((card for card in type.cards if card.owner is None),
                key=lambda card: len(card.possible_owners))
            sg.append(card.name)
        sg = tuple(sg)

        if sg not in self.avail_suggestions:
            sg = self.avail_suggestions.pop()
        else:
            self.avail_suggestions.remove(sg)
        return sg

    def suggestion(self, player_id, cards, disprove_player_id=None, card=None):
        sg = Suggestion(
            self.players[player_id],
            self.get_cards_by_names(cards),
            self.players[disprove_player_id] if disprove_player_id is not None else None,
            self.card_map[card] if card else None,
        )
        self.suggestions.append(sg)
        # Iter through the non-disproving players and update their may_have
        end_id = sg.dplayer.id if sg.disproved else sg.player.id
        for player in self.iter_players(sg.player.id + 1, end_id):
            if player is self.player:
                continue
            for card in sg.cards:
                player.set_have_not_card(card)
        if sg.disproved:
            # The disproving player has sg.dcard
            if sg.dcard:
                if sg.dcard.owner is None:
                    sg.dcard.set_owner(sg.dplayer)
            else:
                # Add a selection group to the disproving player
                sg.dplayer.selection_groups.append(sg.cards)
            self.possible_solutions.pop(tuple(sg.cards), None)

        self.update()

    def update(self):
        static = False
        while not static:
            static = True
            for card in self.cards:
                if card.owner is not None or card.in_solution:
                    continue
                if len(card.possible_owners) == 0 and card.type.solution is None:
                    # In solution
                    card.set_as_solution()
                    static = False

            for type in self.card_types:
                if type.solution is not None:
                    continue
                if type.rest_count == 1:
                    card = next(card for card in type.cards if card.owner is None)
                    card.set_as_solution()
                    static = False

            for player in self.players:
                if player is self.player:
                    continue
                if player.update():
                    static = False

            if self.filter_solutions():
                static = False

    def iter_players(self, start_id, end_id):
        n = len(self.players)
        for i in range(start_id, start_id + n):
            if i % n == end_id:
                break
            yield self.players[i % n]

    def accuse(self):
        if all(type.solution for type in self.card_types):
            return [type.solution.name for type in self.card_types]
        possible_solutions = self.possible_solutions
        if len(possible_solutions) == 1:
            return next(possible_solutions.values())
        # most_possible = max(self.possible_solutions, key=self.possible_solutions.get)
        # total = sum(self.possible_solutions.values())
        # # self.log('rate:', self.possible_solutions[most_possible] / total)
        # if self.possible_solutions[most_possible] > 0.7 * total:
        #     self.log('guess', most_possible)
        #     return [card.name for card in most_possible]
        return None

    def disprove(self, suggest_player_id, cards):
        cards = self.get_cards_by_names(cards)
        sg_player = self.players[suggest_player_id]
        cards = [card for card in cards if card in self.owned_cards]
        for card in cards:
            if sg_player in card.disproved_to:
                return card.name
        return max(cards, key=lambda c: len(c.disproved_to)).name

    def accusation(self, player_id, cards, is_win):
        if not is_win:
            cards = tuple(self.get_cards_by_names(cards))
            self.possible_solutions.pop(cards, None)
            # player = self.players[player_id]
            # for card in cards:
            #     player.set_have_not_card(card)
            # player.update()
        else:
            self.log('fail rate:', self.fail_count / (1e-8 + self.suggest_count))
            self.log('fail count:', self.fail_count, 'suggest count:', self.suggest_count)

    def get_cards_by_names(self, names):
        return [self.card_map[name] for name in names]

    def dump(self):
        self.log()
        for player in self.players:
            self.log('player:', player.id, player.n_cards,
                sorted(player.must_have, key=lambda x: x.name),
                sorted(player.may_have, key=lambda x: x.name),
                '\n    ',
                player.selection_groups)
        self.log('current:', [type.solution for type in self.card_types])
        self.log('possible_solutions:', len(self.possible_solutions))
        for sol, count in self.possible_solutions.items():
            self.log('  ', sol, count)
        self.log('id|', end='')

        def end():
            return ' | ' if card.name in [g[-1] for g in CARDS] else '|'

        for card in self.cards:
            self.log(card.name, end=end())
        self.log()
        for player in self.players:
            self.log(' *'[player.id == self.player.id] + str(player.id), end='|')
            for card in self.cards:
                self.log(
                    ' ' + 'xo'[player in card.possible_owners or player is card.owner],
                    end=end())
            self.log()


if __name__ == '__main__':
    main(AI01, BufMessager)

एआई कोड यहां पाया जा सकता है


क्या आप अपने AI के साथ पुल अनुरोध कर सकते हैं? मैं इसे रेपो प्रतियोगिता में लाना चाहता हूं।
सादाकात्सु

@ गेमकोडर मैंने एक मजबूत AI01 बनाया है और एक पुल अनुरोध भेजा है।
रे

1
जैसा कि वर्तमान में चीजें खड़ी हैं, आपका 01 सबसे मजबूत है। मेरे द्वारा चलाए जा रहे परीक्षणों में, यह लगातार ~ 67% मैच जीतता है जिसमें यह प्रतिस्पर्धा करता है। मुझे उम्मीद है कि प्रतियोगिता समाप्त होने से पहले हम कुछ ठोस प्रविष्टियाँ देखेंगे जो इसे चुनौती दे सकते हैं।
सादाकात्सु

मेरी जाँच करें SpockAI। यह काफी अच्छा प्रदर्शन करता है 01। मुझे नहीं पता कि यह प्रतियोगिता जीत पाएगी या नहीं, लेकिन मुझे खुशी है कि आपकी जीत की संख्या कम हुई है; )
सादाकत्सु

@ गेमकोडर वास्तव में, मैंने नए नियमों के अनुसार कई दिनों पहले अपने एआई को अपडेट किया है। मुझे आपकी नई प्रविष्टि देखकर खुशी हुई। यह अच्छा प्रदर्शन करने लगता है लेकिन मैंने इसे अक्षम होने के कारण कई बार परीक्षण नहीं किया। हो सकता है कि आप इसे और तेज़ कर सकें ताकि हमें परखने में आसानी हो।
रे

4

SimpleCluedoPlayer.java

यह वर्ग उपयोग करता है AbstractCluedoPlayer, जो सभी I / O को संभालता है और तर्क को एक सरल टाइप इंटरफेस के साथ काम करने देता है। पूरी बात गितुब पर है

यह यादृच्छिक खिलाड़ी को उच्च संभावना के साथ हराता है (सबसे खराब स्थिति में यह 15 सुझाव लेता है, जबकि यादृच्छिक खिलाड़ी औसतन 162 लेता है), लेकिन यह आसानी से पीटा जाएगा। मैं इसे रोल रोलिंग पाने के लिए पेश करता हूं।

package org.cheddarmonk.cluedoai;

import java.io.IOException;
import java.io.PrintStream;
import java.net.UnknownHostException;
import java.util.*;

/**
 * A simple player which doesn't try to make inferences from partial information.
 * It merely tries to maximise the information gain by always making suggestions involving cards which
 * it does not know to be possessed by a player, and to minimise information leakage by recording who
 * has seen which of its own cards.
 */
public class SimpleCluedoPlayer extends AbstractCluedoPlayer {
    private Map<CardType, Set<Card>> unseenCards;
    private Map<Card, Integer> shownBitmask;
    private Random rnd = new Random();

    public SimpleCluedoPlayer(String identifier, int serverPort) throws UnknownHostException, IOException {
        super(identifier, serverPort);
    }

    @Override
    protected void handleReset() {
        unseenCards = new HashMap<CardType, Set<Card>>();
        for (Map.Entry<CardType, Set<Card>> e : Card.byType.entrySet()) {
            unseenCards.put(e.getKey(), new HashSet<Card>(e.getValue()));
        }

        shownBitmask = new HashMap<Card, Integer>();
        for (Card myCard : myHand()) {
            shownBitmask.put(myCard, 0);
            unseenCards.get(myCard.type).remove(myCard);
        }
    }

    @Override
    protected Suggestion makeSuggestion() {
        return new Suggestion(
            selectRandomUnseen(CardType.SUSPECT),
            selectRandomUnseen(CardType.WEAPON),
            selectRandomUnseen(CardType.ROOM));
    }

    private Card selectRandomUnseen(CardType type) {
        Set<Card> candidates = unseenCards.get(type);
        Iterator<Card> it = candidates.iterator();
        for (int idx = rnd.nextInt(candidates.size()); idx > 0; idx--) {
            it.next();
        }
        return it.next();
    }

    @Override
    protected Card disproveSuggestion(int suggestingPlayerIndex, Suggestion suggestion) {
        Card[] byNumShown = new Card[playerCount()];
        Set<Card> hand = myHand();
        int bit = 1 << suggestingPlayerIndex;
        for (Card candidate : suggestion.cards()) {
            if (!hand.contains(candidate)) continue;

            int bitmask = shownBitmask.get(candidate);
            if ((bitmask & bit) == bit) return candidate;
            byNumShown[Integer.bitCount(bitmask)] = candidate;
        }

        for (int i = byNumShown.length - 1; i >= 0; i--) {
            if (byNumShown[i] != null) return byNumShown[i];
        }

        throw new IllegalStateException("Unreachable");
    }

    @Override
    protected void handleSuggestionResponse(Suggestion suggestion, int disprovingPlayerIndex, Card shown) {
        if (shown != null) unseenCards.get(shown.type).remove(shown);
        else {
            // This player never makes a suggestion with cards from its own hand, so we're ready to accuse.
            unseenCards.put(CardType.SUSPECT, Collections.singleton(suggestion.suspect));
            unseenCards.put(CardType.WEAPON, Collections.singleton(suggestion.weapon));
            unseenCards.put(CardType.ROOM, Collections.singleton(suggestion.room));
        }
    }

    @Override
    protected void recordSuggestionResponse(int suggestingPlayerIndex, Suggestion suggestion, Card shown) {
        shownBitmask.put(shown, shownBitmask.get(shown) | (1 << suggestingPlayerIndex));
    }

    @Override
    protected void recordSuggestionResponse(int suggestingPlayerIndex, Suggestion suggestion, int disprovingPlayerIndex) {
        // Do nothing.
    }

    @Override
    protected Suggestion makeAccusation() {
        Set<Card> suspects = unseenCards.get(CardType.SUSPECT);
        Set<Card> weapons = unseenCards.get(CardType.WEAPON);
        Set<Card> rooms = unseenCards.get(CardType.ROOM);
        if (suspects.size() * weapons.size() * rooms.size()  == 1) {
            return new Suggestion(suspects.iterator().next(), weapons.iterator().next(), rooms.iterator().next());
        }

        return null;
    }

    @Override
    protected void recordAccusation(int accusingPlayer, Suggestion accusation, boolean correct) {
        // Do nothing.
    }

    //*********************** Public Static Interface ************************//
    public static void main(String[] args) throws Exception {
        try {
            System.setOut(new PrintStream("/tmp/speed-cluedo-player" + args[0]+".log"));
            new SimpleCluedoPlayer(args[0], Integer.parseInt(args[1])).run();
        } catch (Throwable th) {
            th.printStackTrace(System.out);
        }
    }
}

बहुत अच्छा, साफ कोड। मुझे संदेह है कि टेस्ट सर्वर या रैंडम खिलाड़ी को देखकर किसी को भी ऐसा लगता है। मुझे यह भी लगता है कि आप इस ^ _ ^
sadakatsu

4

SpockAI

पहचानकर्ता: gamecoder-SpockAI

रेपो एंट्री: यहां क्लिक करें

चयनित: हाँ

प्रौद्योगिकी: जावा 7 पर आधारितcom.sadakatsu.clue.jar

तर्क: {identifier} portNumber [logOutput: true|false]

विवरण:

SpockAIएक स्पीड क्लू प्लेयर जिसे Knowledgeमैंने लिखा था, एक वर्ग पर बनाया गया था। Knowledgeवर्ग के लिए सभी संभव कहा गया है कि खेल अब तक क्या हुआ है को देखते हुए किया जा सकता था प्रतिनिधित्व करता है। यह सेट के रूप में खेल के समाधान और खिलाड़ियों के संभावित हाथों का प्रतिनिधित्व करता है, और जब तक कुछ सीखा जाता है, तब तक इन सेटों को कम करने के लिए पुनरावृत्ति कटौती का उपयोग करता है। SpockAIइस वर्ग का उपयोग यह निर्धारित करने के लिए किया जाता है कि कौन से सुझाव सबसे उपयोगी सबसे खराब परिणाम देने के लिए आश्वस्त हैं और बेतरतीब ढंग से इसके सुझावों में से एक का चयन करता है। जब उसे किसी सुझाव को अस्वीकार करने की आवश्यकता होती है, तो वह या तो एक कार्ड दिखाने की कोशिश करता है जिसे उसने पहले ही सुझाव दिया है कि वह AI को दिखाता है या उसे उस श्रेणी का कार्ड दिखाता है जिसके लिए उसने संभावनाओं को कम से कम किया है। यह केवल एक आरोप लगाता है जब वह समाधान जानता है।

सबसे अच्छा सुझाव निर्धारित करने के लिए मैंने जो अनुमान लगाया, वह इस प्रकार है। एक सुझाव से सभी जानकारी प्राप्त होने के बाद, संभव समाधान और संभव खिलाड़ी के हाथ सेट कम हो गए होंगे (जब तक कि सुझाव से कोई नई जानकारी नहीं मिलती)। सैद्धांतिक रूप से, सबसे अच्छा सुझाव वह है जो सबसे अधिक संभव समाधानों की संख्या को कम करता है। एक टाई के मामले में, मेरा मानना ​​है कि एक सुझाव जो खिलाड़ियों के लिए संभावित हाथों की संख्या को कम करता है, बेहतर है। इसलिए, प्रत्येक सुझाव के लिए, मैं हर संभव परिणाम की कोशिश करता हूं जिससे ज्ञान में विरोधाभास न हो। जिसके परिणाम में समाधान / हाथ की गणना में सबसे कम सुधार हुआ है, यह माना जाता है कि परिणाम में सुझाव होगा। फिर मैं सभी सुझावों के परिणामों की तुलना करता हूं, और उनमें से जो सबसे अच्छा परिणाम होता है उसे चुनता हूं। इस तरह, मैं इष्टतम सबसे खराब स्थिति की जानकारी प्राप्त करने की गारंटी देता हूं।

मैं संभावित समाधानों और संभावित खिलाड़ी के हाथों के एक क्रूर बल संयोजन विश्लेषण को और SpockAIभी मजबूत बनाने पर विचार कर रहा हूं , लेकिन चूंकि SpockAIपहले से ही सबसे धीमी, सबसे संसाधन-गहन प्रविष्टि है, मैं शायद इसे छोड़ दूंगा।

अस्वीकरण:

मैंने हफ्तों पहले इस प्रतियोगिता के लिए AI जारी करने का इरादा किया था। जैसा कि यह खड़ा है, मैं पिछले हफ्ते शुक्रवार तक अपने एआई लिखना शुरू नहीं कर पाया था, और मैं अपने कोड में हास्यास्पद कीड़े ढूंढता रहा। इस वजह SpockAIसे, समय सीमा से पहले मुझे काम करने का एकमात्र तरीका एक बड़े थ्रेड पूल का उपयोग करना था। अंतिम परिणाम यह है कि (वर्तमान में) SpockAI + 90% CPU उपयोग और 2GB + मेमोरी उपयोग (हालांकि मैं इसके लिए कचरा कलेक्टर को दोष देता हूं) को हिट कर सकता हूं। मैं SpockAIप्रतियोगिता में भाग लेने का इरादा रखता हूं , लेकिन अगर दूसरों को लगता है कि यह नियमों का उल्लंघन है , तो मुझे "विजेता" का खिताब देना चाहिए, दूसरे स्थान पर SpockAIजीतना चाहिए । यदि आपको ऐसा लगता है, तो कृपया इस उत्तर पर उस प्रभाव के लिए एक टिप्पणी छोड़ दें।


3

InferencePlayer.java

Github पर पूर्ण कोड (नोट: यह AbstractCluedoPlayerमेरे पहले के समान उपयोग करता है SimpleCluedoPlayer)।

इस खिलाड़ी का असली मूल इसका PlayerInformationवर्ग है (यहाँ थोड़ा छंटनी की गई है):

private static class PlayerInformation {
    final PlayerInformation[] context;
    final int playerId;
    final int handSize;
    Set<Integer> clauses = new HashSet<Integer>();
    Set<Card> knownHand = new HashSet<Card>();
    int possibleCards;
    boolean needsUpdate = false;

    public PlayerInformation(PlayerInformation[] context, int playerId, boolean isMe, int handSize, Set<Card> myHand) {
        this.context = context;
        this.playerId = playerId;
        this.handSize = handSize;
        if (isMe) {
            knownHand.addAll(myHand);
            possibleCards = 0;
            for (Card card : knownHand) {
                int cardMask = idsByCard.get(card);
                clauses.add(cardMask);
                possibleCards |= cardMask;
            }
        }
        else {
            possibleCards = allCardsMask;
            for (Card card : myHand) {
                possibleCards &= ~idsByCard.get(card);
            }

            if (playerId == -1) {
                // Not really a player: this represents knowledge about the solution.
                // The solution contains one of each type of card.
                clauses.add(suspectsMask & possibleCards);
                clauses.add(weaponsMask & possibleCards);
                clauses.add(roomsMask & possibleCards);
            }
        }
    }

    public void hasCard(Card card) {
        if (knownHand.add(card)) {
            // This is new information.
            needsUpdate = true;
            clauses.add(idsByCard.get(card));

            // Inform the other PlayerInformation instances that their player doesn't have the card.
            int mask = idsByCard.get(card);
            for (PlayerInformation pi : context) {
                if (pi != this) pi.excludeMask(mask);
            }

            if (knownHand.size() == handSize) {
                possibleCards = mask(knownHand);
            }
        }
    }

    public void excludeMask(int mask) {
        if (knownHand.size() == handSize) return; // We can't benefit from any new information.

        if ((mask & possibleCards) != 0) {
            // The fact that we have none of the cards in the mask contains some new information.
            needsUpdate = true;
            possibleCards &= ~mask;
        }
    }

    public void disprovedSuggestion(Suggestion suggestion) {
        if (knownHand.size() == handSize) return; // We can't benefit from any new information.

        // Exclude cards which we know the player doesn't have.
        needsUpdate = clauses.add(mask(suggestion.cards()) & possibleCards);
    }

    public void passedSuggestion(Suggestion suggestion) {
        if (knownHand.size() == handSize) return; // We can't benefit from any new information.

        excludeMask(mask(suggestion.cards()));
    }

    public boolean update() {
        if (!needsUpdate) return false;

        needsUpdate = false;

        // Minimise the clauses, step 1: exclude cards which the player definitely doesn't have.
        Set<Integer> newClauses = new HashSet<Integer>();
        for (int clause : clauses) {
            newClauses.add(clause & possibleCards);
        }
        clauses = newClauses;

        if (clauses.contains(0)) throw new IllegalStateException();

        // Minimise the clauses, step 2: where one clause is a superset of another, discard the less specific one.
        Set<Integer> toEliminate = new HashSet<Integer>();
        for (int clause1 : clauses) {
            for (int clause2 : clauses) {
                if (clause1 != clause2 && (clause1 & clause2) == clause1) {
                    toEliminate.add(clause2);
                }
            }
        }
        clauses.removeAll(toEliminate);

        // Every single-card clause is a known card: update knownHand if necessary.
        for (int clause : clauses) {
            if (((clause - 1) & clause) == 0) {
                Card singleCard = cardsById.get(clause);
                hasCard(cardsById.get(clause));
            }
        }

        // Every disjoint set of clauses of size equal to handSize excludes all cards not in the union of that set.
        Set<Integer> disjoint = new HashSet<Integer>(clauses);
        for (int n = 2; n <= handSize; n++) {
            Set<Integer> nextDisjoint = new HashSet<Integer>();
            for (int clause : clauses) {
                for (int set : disjoint) {
                    if ((set & clause) == 0) nextDisjoint.add(set | clause);
                }
            }
            disjoint = nextDisjoint;
        }

        for (int set : disjoint) excludeMask(~set);

        return true;
    }
}

यह उन सुझावों की जानकारी को एकीकृत करता है, जो खिलाड़ी ने नहीं किया था (यह दर्शाता है कि वे उन कार्डों में से कोई भी नहीं रखते हैं), ऐसे सुझाव जो उन्होंने अस्वीकृत किए थे (यह दर्शाता है कि वे उन कार्डों में से कम से कम एक को पकड़ते हैं), और कार्ड जिनका स्थान निश्चित है। फिर यह कुछ मूल नियमों को लागू करता है जो उस जानकारी को अपने सार में कॉम्पैक्ट करते हैं।

मुझे नहीं लगता कि प्राप्त करने के लिए कोई और निर्धारित जानकारी है (झूठे आरोपों के अलावा, जिसे मैं बहुत परेशान होने के लिए दुर्लभ मान रहा हूं), हालांकि मैंने कुछ अनदेखी की होगी। वहाँ है कि खिलाड़ी एक्स कार्ड Y है अनुमान संभावनाओं को एक और अधिक परिष्कृत खिलाड़ी के लिए क्षमता ...

दूसरा क्षेत्र जो संभवत: महत्वपूर्ण सुधार को स्वीकार करता है, यह निर्णय लेने में है कि कौन सा सुझाव देना है। मैं काफी क्लिंक भारी-भारी दृष्टिकोण का उपयोग करके सूचना लाभ को अधिकतम करने का प्रयास करता हूं, लेकिन विभिन्न काल्पनिक डिस्ट्रॉफ़ से प्राप्त ज्ञान के सापेक्ष गुणों का मूल्यांकन करने में बहुत अधिक औचित्यपूर्ण न्यायोचित है। हालाँकि, मैं उत्तराधिकारियों को तब तक साधने की कोशिश नहीं करूंगा जब तक कि कोई अन्य किसी योग्य प्रतिद्वंद्वी को पोस्ट न कर दे।


मुझे चलाने के लिए आपका SimpleCluedoPlayer या आपका InferencePlayer नहीं मिल सकता है। मैंने चींटी को "SpeedClueContest / प्रविष्टियों / peter_taylor /" निर्देशिका में दौड़ाया और सफलतापूर्वक JAR उत्पन्न किया। मैंने इन JAR को सापेक्ष और निरपेक्ष पथों की कोशिश की है, उन्हें उस क्रम में "पहचानकर्ता" और "पोर्टनंबर" पास कर रहा हूं, लेकिन टेस्टस्वर प्रत्येक के लिए "पहचानकर्ता जीवित" संदेश की प्रतीक्षा कर रहा है। मैंने देखा और "/tmp/speed-cluedo-player"+identifier+".log" नहीं ढूँढ सका। क्या मैंने किसी तरह इस प्रक्रिया को गड़बड़ कर दिया है?
सादकात्सु

@ गेमकोडर, मुझे शायद हार्ड-कोड नहीं चाहिए /tmp। यह एक साधारण पैच होना चाहिए; मैं जल्द ही इस पर गौर करूंगा।
पीटर टेलर

1
आपका फिक्स काम करता है; मैंने इसे रेपो में मिला दिया है। अब मैं InferencePlayer के माध्यम से पढ़ने के लिए ध्यान से यह सुनिश्चित करने के लिए तैयार हूं कि इसके और लॉजिकिकल के बीच पर्याप्त अंतर हैं> मैंने ___ <
sadakatsu

यहाँ आपका प्रतिद्वंदी आता है :-)
रे

@ रे, उत्कृष्ट। मैं आपके एआई को अलग करने की कोशिश करूँगा और देखूंगा कि यह किसी बिंदु पर खदान से कैसे अलग है: सरसरी नज़र से, यह एक समान विश्लेषण का उपयोग करता है।
पीटर टेलर

2

CluePaddle (ClueStick / ClueBat / ClueByFour) - C #

मैंने ClueBot लिखा है, एक C # क्लाइंट जिसके लिए यह AI, और विभिन्न AI को लागू करने के लिए सीधा है, जिसमें CluePaddle नामक सबसे गंभीर प्रयास शामिल है। कोड https://github.com/jwg4/SpeedClueContest/tree/clue_paddle पर है, जिसमें एक पुल अनुरोध इसे अपस्ट्रीम में मर्ज करने के लिए शुरू हुआ।

ClueStick एक प्रूफ-ऑफ-कॉन्सेप्ट है, जो मूल रूप से सिर्फ अनुमान लगाता है और जो कुछ भी होता है उसे अनदेखा कर देता है। ClueBat एक और मूर्खतापूर्ण AI है, सिवाय इसके कि यह ClueStick में एक दोष का फायदा उठाने की कोशिश करता है ताकि वह गलत आरोप लगा सके। ClueByFour एक उचित एआई है जिसमें यह उचित सुझाव देता है और दूसरों द्वारा दिखाए गए कार्ड को याद करता है।

CluePaddle सबसे बुद्धिमान है। यह पता लगाने की कोशिश करता है कि किसके पास क्या न केवल क्या डिस्पर्स की पेशकश की गई है, बल्कि यह भी आधारित है कि कौन से खिलाड़ी किसी दिए गए सुझाव के डिसप्रूफ की पेशकश नहीं करते हैं। यह ध्यान नहीं रखता है कि प्रत्येक खिलाड़ी के पास कितने कार्ड हैं लेकिन यह तय किया जाना है। इसमें काफी लंबी कक्षाएं शामिल हैं इसलिए मैं पूरे कोड को यहां पोस्ट नहीं करूंगा, लेकिन निम्नलिखित विधि एक स्वाद देती है।

public void Suggestion(int suggester, MurderSet suggestion, int? disprover, Card disproof)
{
  List<int> nonDisprovers = NonDisprovers(suggester, disprover).ToList();

  foreach (var player in nonDisprovers)
  {
    m_cardTracker.DoesntHaveAnyOf(player, suggestion);
  }

  if (disprover != null && disproof == null)
  {
    // We know who disproved it but not what they showed.
    Debug.Assert(disprover != m_i, "The disprover should see the disproof");
    Debug.Assert(suggester != m_i, "The suggester should see the disproof");
    m_cardTracker.DoesntHaveAllOf(suggester, suggestion);
    m_cardTracker.HasOneOf((int)disprover, suggestion);
  }

  if (disproof != null)
  {
    // We know who disproved it and what they showed.
    Debug.Assert(disprover != null, "disproof is not null but disprover is null");
    m_cardTracker.DoesHave((int)disprover, disproof.Value);
  }
}

यदि 4 एक दूसरे के खिलाफ खेलते हैं, तो CluePaddle अब तक का सबसे गेम जीतता है, ClueByFour के साथ दूसरा और अन्य दो क्षेत्रों में।

केवल क्लूपैडल एक प्रतिस्पर्धी प्रविष्टि (अब तक) है। उपयोग:

CluePaddle.exe identifier port

यदि कोई अन्य व्यक्ति C # AI बनाना चाहता है, तो बस सॉल्वैंट्स में एक ConsoleApplication प्रोजेक्ट बनाएं, IClueAIइंटरफ़ेस को एक क्लास में लागू करें , और उसके बाद अपना Programव्युत्पन्न करें ProgramTemplateऔर अन्य प्रोजेक्ट्स के लिए जो करें उसे कॉपी करें Main()। इकाई परीक्षण के लिए एकमात्र निर्भरता NUnit है और आप आसानी से कोड से सभी परीक्षण हटा सकते हैं (लेकिन, बस NUnit स्थापित नहीं करें)।


मैंने आपके एआई को संकलित करने और उन्हें प्रतियोगितासर्वर (जल्द ही पोस्ट किए जाने वाले) में परीक्षण करने की कोशिश की है। यह CluePaddleपरियोजना संकलित नहीं करती है, यह दावा करते हुए कि NUnitअन्य परियोजनाएं संकलित होने के बावजूद स्थापित नहीं हैं। जो परीक्षण के दौरान स्टालिंग को समाप्त करते हैं, और जावा कनेक्शन रीसेट त्रुटि की रिपोर्ट करता है। क्या आप मुझे यह निर्धारित करने में मदद कर सकते हैं कि मैंने कुछ गलत किया है?
सादाकासु

सुधार: ClueStickकेवल एआई है जो स्टाल करता है जब मैं इसे शुरू करने की कोशिश करता हूं। अन्य दो ट्रायल टूर्नामेंट में प्रतिस्पर्धा करते हैं और अंततः एक ही उल्लंघन के लिए अयोग्य हो जाते हैं। ClueByFourएक अनिर्दिष्ट सुझाव को दोहराने में विफल रहने के लिए अयोग्य हो जाता है, यह तब आरोप के रूप में होता है जब यह कार्डों में से किसी को पकड़ नहीं पाता है। ClueBatआरोप लगाने के लिए अयोग्य हो जाता है जिसके पास कार्ड हैं या तो उसे दिखाया गया है या उसके हाथ में है। कृपया अनुपालन सुनिश्चित करने के लिए संशोधित AI प्रतिबंध देखें
सादाकात्सु

@ गेमकोडर क्या आपके पास NUnit स्थापित है? यदि आप इसे स्थापित नहीं कर सकते हैं तो मैं सशर्त रूप से इकाई परीक्षण कोड हटा सकता हूं। क्लूपैड मेरी वास्तविक प्रविष्टि है - मैं अन्य दो द्वारा उल्लंघन के बारे में बहुत चिंतित नहीं हूं क्योंकि वे वास्तव में जीतने के लिए नहीं खेल रहे हैं।
jwg

मेरे पास कुछ अपडेट भी हैं CluePaddle। मैं बाद में इनके लिए एक पुल अनुरोध करूंगा।
jwg

मैंने NUnit इंस्टॉल किया। मैं आपकी अन्य परियोजनाओं के संदर्भों का उपयोग करके MSVS में इसके नाम स्थान का पता लगाने में सक्षम था। मैं आपके पुल अनुरोध का इंतजार करूंगा।
सादकात्सु
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.