পাহাড়ের রাজা: স্পিড ক্লু এআই


24

স্পিড ক্লু

ক্লিগো / ক্লু একটি আকর্ষণীয় ছাড়ের গেমপ্লে উপাদান সহ একটি ক্লাসিক বোর্ড গেম। স্পিড ক্লু একটি 3-6 প্লেয়ার বৈকল্পিক যা কেবলমাত্র কার্ড ব্যবহার করে এই উপাদানটিকে জোর দেয়। ফলাফলটি হ'ল স্ট্যান্ডার্ড ক্লিউডো এবং স্পিড ক্লুয়ের মধ্যে একমাত্র পার্থক্য হ'ল গেমের প্রতিটি খেলোয়াড় ডাইস রোলস এবং অন্যান্য খেলোয়াড়দের পরামর্শের দয়াতে একটি নির্দিষ্ট ঘরে পৌঁছানোর অপেক্ষা না করে তার পালাটিতে সন্তুষ্ট কোনও পরামর্শ দিতে পারে। আপনি যদি এর আগে কখনও ক্লিগো না খেলেন, বা দুটি সংস্করণের মধ্যে স্পষ্ট পার্থক্য সম্পর্কে নিশ্চিত হতে চান তবে আপনি এখানে একটি সম্পূর্ণ স্পিড ক্লু বিধি সেট করতে পারেন ।


লক্ষ্য

15 মে 2014 00:00 GMT এর আগে স্পিড ক্লু খেলতে একটি এআই প্রোগ্রাম লিখুন এবং জমা দিন। সেই সময়ের পরে, আমি সমস্ত আইনী এন্ট্রি ব্যবহার করে একটি টুর্নামেন্ট চালাব । যার এআই টুর্নামেন্টে সর্বাধিক গেমস জিতবে সেই চ্যালেঞ্জ জিতেছে।


এআই স্পেসিফিকেশন

আপনি সার্ভারের সাথে গেমস খেলতে টিসিপি / আইপি সংযোগের মাধ্যমে অ্যাপ্লিকেশন প্রোটোকলটি কঠোরভাবে ব্যবহার না করে আপনি যে কোনও কৌশলই ব্যবহার করেন না কেন আপনি আপনার এআই লিখতে পারেন choose সমস্ত বিধিনিষেধের বিস্তারিত ব্যাখ্যা এখানে পাওয়া যাবে


কিভাবে খেলতে হবে

গিটহাব সংগ্রহস্থলটি প্রতিযোগিতায় জোর করে শুরু করুন । entriesআপনার স্ট্যাকএক্সচেঞ্জ ব্যবহারকারীর নাম ব্যবহার করে নামের নীচে ডিরেক্টরি যুক্ত করুন এবং সেই ফোল্ডারে আপনার কোডটি বিকাশ করুন। আপনি যখন আপনার এন্ট্রি জমা দেওয়ার জন্য প্রস্তুত হন, আপনার সংশোধনগুলির সাথে একটি টানুন অনুরোধ করুন, তারপরে এই সাইটে আপনার প্রবেশের ঘোষণার জন্য এই নির্দেশাবলী অনুসরণ করুন।

coreআপনাকে শুরু করার জন্য আমি ডিরেক্টরিটিতে কিছু কোড এবং জেআর সরবরাহ করেছি ; উপকরণগুলির জন্য মোটামুটি গাইডের জন্য আমার সাইটটি দেখুন । এছাড়াও, অন্য খেলোয়াড়রা আপনাকে উঠতে এবং দৌড়াতে সহায়তা করতে তাদের এন্ট্রিগুলি ছাড়াও সহায়ক কোড জমা দিচ্ছে। এন্ট্রিগুলি অন্বেষণ করতে কিছুটা সময় নিন, এবং জমা দেওয়ার আগে অন্যের প্রবেশের বিরুদ্ধে আপনার এন্ট্রি পরীক্ষা করতে ভুলবেন না!


ফলাফল

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: ""

উপরের ফলাফলগুলিতে প্রতিটি যোগ্যতাসম্পন্ন এআই এর 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
@ পিটারটেলর, সার্ভারটি লিখিত হওয়ার সাথে সাথেই আমি এটির একটি অনুলিপি সরবরাহ করব। তুমি কেন ভাবছ যে আমি একমাস দিচ্ছি? ;)
সদাকাতসু

@ হস্তুরকুন, সার্ভারের জন্য আমি যে আর্কিটেকচারটি পরিকল্পনা করেছি তা হ'ল এটি আপনার আদেশ জমা দেওয়া শুরু করবে কমান্ড লাইনের মাধ্যমে। এটি প্রতিটি বার্তাটি বার্তাগুলি প্রেরণের জন্য কোন বন্দর ব্যবহার করবে তা চয়ন করবে যাতে কোন প্রোগ্রামটি সহজে তা সনাক্ত করতে পারে (নোট করুন যে প্রোটোকলে কোনও শনাক্তকারী অন্তর্ভুক্ত নেই)। এছাড়াও, প্রতিটি প্রোগ্রামে কোনও পোর্টে বার্তা প্রেরণ করা উচিত তা যাতে সার্ভারটি প্রকৃতপক্ষে বার্তাগুলি গ্রহণ করতে পারে তা জানতে হবে। এই দুটি পোর্ট যা প্রতিটি জমা দিতে হবে কমান্ড লাইন আর্গুমেন্ট হিসাবে।
সদাকাতসু

1
আমি লিখেছি শুধুমাত্র নেটওয়ার্কিং প্রোগ্রাম ইউডিপি ব্যবহার করে। আমি (1) টিসিপি / আইপি ব্যবহার করার সিদ্ধান্ত নিয়েছি এবং (2) এর মধ্যে যে কোনও পার্থক্য বুঝতে হবে এবং (2) প্রযুক্তিটি ব্যবহার করতে আমার লক-স্টেপ প্লেয়ারের আপডেটগুলির পক্ষে সর্বোত্তমভাবে কাজ করার জন্য এটি সমর্থন করে।
সদকাতসু

উত্তর:


5

এআই01 - পাইথন 3

আমি এর জন্য আরও ভাল নামটি এখনও খুঁজে পাচ্ছি না :- পি।

শনাক্তকারী : রশ্মি- i01

প্রযুক্তি : পাইথন 3

নির্বাচিত : হ্যাঁ

যুক্তি :ai01.py identifier port

বিবরণ : অনুমান দ্বারা কাজ করুন। যখন মালিকের জানা নেই এমন কার্ডগুলির সংখ্যা যখন একটি প্রান্তিকের চেয়ে কম হয়, তখন এই এআই পুনরাবৃত্ত বিশ্বব্যাপী অনুমানের দ্বারা সমস্ত অসম্ভব সমাধানগুলি সরিয়ে দিতে শুরু করে। অন্যথায়, এটি স্থানীয় অনুমান ব্যবহার করে।

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

এআই কোডটি এখানে পাওয়া যাবে


আপনি কি আপনার এআই এর সাথে একটি টান অনুরোধ করতে পারেন? আমি এটি প্রতিযোগিতার রেপোতে পেতে চাই।
সদাকাতসু

@ গেমকোডার আমি একটি শক্তিশালী এআই0১ তৈরি করেছি এবং একটি অনুরোধ পাঠিয়েছি।
রায়

1
জিনিসগুলি বর্তমানে যেমন দাঁড়িয়ে আছে, আপনার 01 টি সবচেয়ে শক্তিশালী। আমি যে পরীক্ষাগুলি চালিয়েছি তাতে এটি প্রতিযোগিতামূলকভাবে the 67% ম্যাচ জিতেছে। আমি আশা করছি প্রতিযোগিতা শেষ হওয়ার আগে আমরা কিছু শক্ত এন্ট্রি দেখতে পাব যা এটিকে চ্যালেঞ্জ জানাতে পারে।
সদাকাতসু

আমার দেখুন SpockAI। এটির বিরুদ্ধে বেশ ভাল অভিনয় করে 01। আমি জানি না এটি প্রতিযোগিতা জিতবে কিনা, তবে আপনার জয়ের সংখ্যা হ্রাস পেয়ে আমি আনন্দিত; )
সদাকাতসু

@ গেমকোডার আসলে, আমি নতুন নিয়ম অনুসারে কয়েক দিন আগে আমার এআই আপডেট করেছি। আপনার নতুন এন্ট্রি দেখে আমি আনন্দিত। এটি দুর্দান্ত পারফরম্যান্স বলে মনে হচ্ছে তবে এটি অকার্যকর হওয়ায় আমি এটি বহুবার পরীক্ষা করিনি। সম্ভবত আপনি এটি আরও দ্রুত করতে পারেন যাতে আমাদের পরীক্ষা করা সহজ হয়।
রায়

4

SimpleCluedoPlayer.java

এই শ্রেণিটি ব্যবহার করে AbstractCluedoPlayer, যা সমস্ত আই / ও পরিচালনা করে এবং যুক্তিকে সাধারণ টাইপ করা ইন্টারফেস দিয়ে কাজ করতে দেয়। পুরো বিষয়টি গিথুবের উপর

এটি উচ্চ সম্ভাবনার সাথে এলোমেলো খেলোয়াড়কে পরাজিত করে (সবচেয়ে খারাপ ক্ষেত্রে এটি 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);
        }
    }
}

খুব সুন্দর, পরিষ্কার কোড। আমি সন্দেহ করি যে কেউ পরীক্ষার সার্ভার বা এলোমেলো খেলোয়াড়ের দিকে তাকিয়ে থাকে সেভাবে অনুভব করে। আমি আরও মনে করি যে আপনার ^ _ ^ এর চেয়ে সহজ এআই থাকতে পারে না
সাদাকাতসু

4

SpockAI

আইডেন্টিফাইয়ার: gamecoder-SpockAI

রেপো এন্ট্রি: এখানে ক্লিক করুন

নির্বাচিত: হ্যাঁ

প্রযুক্তি: জাভা 7 ভিত্তিকcom.sadakatsu.clue.jar

যুক্তি: {identifier} portNumber [logOutput: true|false]

বর্ণনা:

SpockAIKnowledgeআমি লিখেছি এমন একটি ক্লাসের উপর নির্মিত একটি স্পিড ক্লু প্লেয়ার । দ্যKnowledgeবর্গ সব সম্ভব বলে যে খেলা দূর পর্যন্ত কি হয়েছে তো দিয়ে যেতে পারতে প্রতিনিধিত্ব করে। এটি গেমের সমাধান এবং খেলোয়াড়দের সম্ভাব্য হাতকে সেট হিসাবে উপস্থাপন করে এবং প্রতিবার কিছু শিখলে যতটা সম্ভব এই সেটগুলি হ্রাস করতে পুনরাবৃত্তির ছাড়গুলি ব্যবহার করে। SpockAIকোন পরামর্শটি সর্বাধিক সহায়ক সবচেয়ে খারাপ ফলাফলের আশ্বাস প্রাপ্ত তা নির্ধারণ করার জন্য এই শ্রেণীর ব্যবহার করে এবং এগুলির পরামর্শগুলির মধ্যে এলোমেলোভাবে একটি প্রস্তাব নির্বাচন করে। যখন এটি কোনও প্রস্তাবকে প্রত্যাখ্যান করার দরকার হয়, এটি হয় প্রস্তাবিত এআই দেখিয়েছে এমন কোনও কার্ড দেখানোর চেষ্টা করে বা এমন বিভাগ থেকে একটি কার্ড দেখানোর চেষ্টা করে যার জন্য এটি সম্ভাবনাগুলি কমিয়ে দেয়। সমাধানটি জেনে গেলে এটি কেবল একটি অভিযোগ তোলে।

আমি সেরা পরামর্শ নির্ধারণের জন্য যে বৌদ্ধিক ব্যবহার করেছি তা নীচে রয়েছে is কোনও পরামর্শ থেকে সমস্ত তথ্য জানার পরে, সম্ভাব্য সমাধান এবং প্লেয়ারের হ্যান্ড সেটগুলি কমিয়ে দেওয়া হবে (যদি পরামর্শটি কোনও নতুন তথ্য না প্রকাশ করে)। তাত্ত্বিকভাবে, সেরা পরামর্শটি হ'ল সম্ভাব্য সমাধানগুলির সংখ্যা সবচেয়ে কমিয়ে দেয়। টাইয়ের ক্ষেত্রে, আমি ধরে নিই যে একটি পরামর্শ যা খেলোয়াড়দের পক্ষে সম্ভাব্য হাতের সংখ্যা সর্বাধিক হ্রাস করে তা ভাল। সুতরাং, প্রতিটি পরামর্শের জন্য, আমি প্রতিটি সম্ভাব্য ফলাফল চেষ্টা করে যা জ্ঞানের দ্বন্দ্ব বাড়ে না। সমাধান / হাতের গণনাগুলির মধ্যে যে কোনও ফলাফলের নূন্যতম উন্নতি রয়েছে বলে মনে করা হয় যে প্রস্তাবটি হবে। তারপরে আমি সমস্ত পরামর্শের ফলাফলের সাথে তুলনা করব এবং কোনটি সেরা ফলাফল পেয়েছে তা বেছে নিন। এইভাবে, আমি সর্বোত্তমতম মামলার তথ্য প্রাপ্তির গ্যারান্টি দিচ্ছি।

আমি SpockAIআরও শক্তিশালী করার জন্য সম্ভাব্য সমাধান এবং সম্ভাব্য খেলোয়াড়ের হাতগুলির একটি ব্রুট ফোর্সের সংমিশ্রণ বিশ্লেষণ যুক্ত করার বিষয়ে বিবেচনা করছি , তবে যেহেতু SpockAIইতিমধ্যে সবচেয়ে ধীরতম, সর্বাধিক সংস্থান-নিবিড় প্রবেশ, তাই আমি সম্ভবত এড়িয়ে যাব।

দাবি পরিত্যাগী:

আমি এই প্রতিযোগিতার জন্য একটি এআই প্রকাশের পরিকল্পনা নিয়েছিলাম কয়েক সপ্তাহ আগে। যেমনটি দাঁড়িয়েছে, আমি গত সপ্তাহে শুক্রবার পর্যন্ত আমার এআই লেখা শুরু করতে সক্ষম হয়েছি এবং আমি আমার কোডটিতে হাস্যকর বাগগুলি খুঁজে পেতে থাকি। এ কারণে, SpockAIএকটি বৃহত থ্রেড পুল ব্যবহার করার সময়সীমার আগে আমি একমাত্র উপায় কাজ করতে সক্ষম হয়েছি । শেষ ফলাফলটি হ'ল (বর্তমানে) স্পোকএইআই + 90% সিপিইউ ব্যবহার এবং 2 জিবি + মেমরির ব্যবহার হিট করতে পারে (যদিও আমি এর জন্য আবর্জনা সংগ্রহকারীকে দায়ী করি)। আমি SpockAIপ্রতিযোগিতায় অংশ নেওয়ার ইচ্ছা করি , তবে অন্যরা যদি মনে করেন যে এটি নিয়ম লঙ্ঘন, তবে আমি "বিজয়ী" উপাধিটি দ্বিতীয় স্থান অধিকার করব, SpockAIজয়ের উচিত । আপনি যদি এইভাবে অনুভব করেন তবে দয়া করে এই উত্তরের বিষয়ে একটি মন্তব্য দিন।


3

InferencePlayer.java

গিথুব-এ সম্পূর্ণ কোড (দ্রষ্টব্য: এটি 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;
    }
}

এটি খেলোয়াড় অস্বীকার করেনি এমন পরামর্শগুলিতে (ইঙ্গিত করে যে তারা those কার্ডগুলির মধ্যে একটিও ধারণ করে না), যে প্রস্তাবগুলি তারা অস্বীকার করেছিল (যেগুলিতে তাদের কমপক্ষে একটি কার্ড রয়েছে তা নির্দেশ করে) এবং যে কার্ডগুলির অবস্থান নিশ্চিত তা সম্পর্কিত তথ্যগুলিকে এটি একত্র করে ies তারপরে এটি পুনরাবৃত্তভাবে সেই তথ্যের সংমিশ্রণে কিছু প্রাথমিক নিয়ম প্রয়োগ করে।

আমি মনে করি না যে আরও কোনও নির্বিচার তথ্য পাওয়া যাবে (মিথ্যা অভিযোগের মাধ্যমে, যা আমি বিরক্ত করার পক্ষে খুব বিরল বলে ধরে নিচ্ছি) বাদে, যদিও আমি কিছু উপেক্ষা করেছি। সেখানে হয় যে প্লেয়ার এক্স কার্ড ওয়াই হয়েছে অনুমান সম্ভাব্যতা করার জন্য একটি আরো পরিশীলিত প্লেয়ারের জন্য সম্ভাব্য ...

অন্যান্য ক্ষেত্রটি যা সম্ভবত উল্লেখযোগ্য উন্নতি স্বীকার করে তা হ'ল কোন পরামর্শ দেওয়ার সিদ্ধান্ত নেওয়া। আমি মোটামুটি আড়ম্বরপূর্ণ ভারী শক্তি প্রয়োগের সাহায্যে তথ্য অর্জনকে সর্বাধিক করার চেষ্টা করি, তবে বিভিন্ন অনুমানের দ্বন্দ্ব থেকে প্রাপ্ত জ্ঞানের আপেক্ষিক গুণাগুণ মূল্যায়নে অনেকটা ন্যায্য যুক্তিযুক্ত হিরিস্টিক রয়েছে। যাইহোক, আমি অন্য কেউ কোনও যোগ্য প্রতিপক্ষকে পোস্ট না করা পর্যন্ত হিউরিস্টিকস টিউন করার চেষ্টা করব না।


আমি আপনার সিম্প্লাইক্লাইডোপ্লেয়ার বা আপনার ইনফারেন্সপ্লেয়ারটি চালাতে পারি না। আমি "স্পিডক্লিউকনটেস্ট / এন্ট্রি / পিটার_টেলর /" ডিরেক্টরিতে পিপীলিকা চালিয়েছি এবং সফলভাবে জেআর তৈরি করেছি। আমি এই জেআরগুলিতে আপেক্ষিক এবং নিখুঁত পথগুলি চেষ্টা করেছি, সেগুলিতে "সনাক্তকারী" এবং "পোর্টনাম্বার" দিয়েছি, তবে টেস্টসবার তাদের প্রত্যেকটির জন্য "সনাক্তকারী বেঁচে থাকা" বার্তার জন্য অপেক্ষা করে। আমি খুঁজে পেয়েছি এবং "/tmp/speed-cluedo-player"+phanfier+".log" খুঁজে পাচ্ছি না। আমি কি কোনওভাবে প্রক্রিয়াটি গোলযোগ করেছি?
সদাকাতসু

@ গেমকোডার, আমার সম্ভবত হার্ড-কোড করা উচিত নয় /tmp। এটি একটি সাধারণ প্যাচ হওয়া উচিত; আমি শীঘ্রই এটি দেখতে হবে।
পিটার টেলর

1
আপনার ফিক্স কাজ করে; আমি রেপোতে এটি একীভূত করেছি। এখন আমি ইনফারেন্সপ্লেয়ারের মাধ্যমে সাবধানতার সাথে পড়তে পারব যাতে নিশ্চিত হওয়া যায় যে এর মধ্যে এবং লজিক্যাল এআই এর মধ্যে আমি> ___ <
সাদাকাতসু

এখানে আপনার প্রতিপক্ষ আসে :-)
রায়

@ রায়, দুর্দান্ত আমি আপনার এআই ছড়িয়ে দেওয়ার চেষ্টা করব এবং এটি দেখতে পেলাম যে এটি আমার থেকে কীভাবে আলাদা হয়: এক দৃষ্টিতে এটি একটি অনুরূপ বিশ্লেষণ ব্যবহার করে বলে মনে হয়।
পিটার টেলর

2

ক্লিউপ্যাডল (ক্লিউস্টিক / ক্লুব্যাট / ক্লুবিফোর) - সি #

আমি ক্লিউবট লিখেছি, একটি সি # ক্লায়েন্ট যার জন্য এটি এআই বাস্তবায়ন করা সহজবোধ্য, এবং ক্লিউপ্যাডল নামে পরিচিত সবচেয়ে গুরুতর প্রচেষ্টা সহ বিভিন্ন এআই s কোডটি https://github.com/jwg4/SpeedClueContest/tree/clue_paddle এ রয়েছে একটি টান অনুরোধ সহ এটিকে প্রবাহে মার্জ করা শুরু করেছে।

ক্লুস্টিক একটি ধারণা-প্রমাণ যা মূলত কেবল যা ঘটে তার বেশিরভাগ অনুমান এবং উপেক্ষা করে। ক্লুব্যাট হ'ল অন্য মূর্খ এআই, এটি বাদ দিয়ে এটি ক্লুস্টিকের কোনও ত্রুটি কাজে লাগানোর চেষ্টা করে যাতে এটি মিথ্যা অভিযোগ করতে বাধ্য হয়। ক্লুবাইফোর একটি যুক্তিসঙ্গত এআই যাতে এটি যুক্তিসঙ্গত পরামর্শ দেয় এবং অন্যরা দেখানো কার্ডগুলি মনে রাখে।

ক্লুপ্যাডেল সবচেয়ে বুদ্ধিমান। এটি নির্ধারণ করার চেষ্টা করে যে কেবল কী কী অস্বীকৃত প্রস্তাব দেওয়া হয়েছে তার ভিত্তিতে কার কী নেই, তবে এর ভিত্তিতে কোন খেলোয়াড়রা প্রদত্ত পরামর্শকে অস্বীকৃতিও দেয় নি। প্রতিটি খেলোয়াড়ের এটিএম কত কার্ড আছে তা অ্যাকাউন্টে নেয় না তবে এটি ঠিক করতে হবে। এটি বেশ কয়েকটি দীর্ঘ ক্লাসের মধ্যে রয়েছে যাতে আমি এখানে পুরো কোডটি পোস্ট করব না, তবে নিম্নলিখিত পদ্ধতিটি একটি গন্ধ দেয়।

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.exe identifier port

অন্য কেউ যদি সি # এআই করতে চান, কেবল নির্জনতায় একটি কনসোল অ্যাপ্লিকেশন প্রকল্প তৈরি করুন, IClueAIএকটি শ্রেণিতে ইন্টারফেসটি প্রয়োগ করুন এবং তারপরে আপনার উত্স Programথেকে প্রাপ্ত ProgramTemplateএবং অন্যান্য প্রকল্পগুলির জন্য কী করে তা অনুলিপি করুন Main()। একমাত্র নির্ভরতা হ'ল ইউনিট পরীক্ষার জন্য NUnit এবং আপনি কোড থেকে সহজেই সমস্ত পরীক্ষার সরিয়ে ফেলতে পারেন (তবে এটি করবেন না, কেবল NUnit ইনস্টল করুন)।


আমি আপনার এআইগুলিকে সংকলন করে কনটেস্ট সার্ভারে (শীঘ্রই পোস্ট করার জন্য) এগুলি পরীক্ষা করার চেষ্টা করেছি। CluePaddleপ্রকল্পের কম্পাইল নয়, দাবি করেন যে NUnitইনস্টল করা নেই যদিও অন্যান্য উইকিগুলো কম্পাইল না। যাগুলি সংকলন করে তারা পরীক্ষার সময় স্টলিং শেষ করে এবং জাভা একটি সংযোগ পুনরায় সেট করার ত্রুটির খবর দেয়। আমি কিছু ভুল করেছি কিনা তা নির্ধারণ করতে আপনি আমাকে সহায়তা করতে পারেন?
সদাকাতসু

সংশোধন: ClueStickআমি যখন এটি শুরু করার চেষ্টা করি তখন একমাত্র এআই হয়। অন্য দু'জন ট্রায়াল টুর্নামেন্টে অংশ নেয় এবং শেষ পর্যন্ত একই লঙ্ঘনের জন্য অযোগ্য ঘোষণা করে। ClueByFourঅকার্যকর প্রস্তাবটি পুনরায় ব্যর্থ করার জন্য অযোগ্য হয়ে যায় যখন এটি কোনও কার্ড ধরে না রাখলে এটি একটি অভিযোগ হিসাবে তোলে। ClueBatএটি কার্ড দেখানো হয়েছে বা তার হাতে রয়েছে এমন অভিযোগ করার জন্য অযোগ্য হয়ে যায়। সম্মতি নিশ্চিত করতে দয়া করে সংশোধিত এআই বিধিনিষেধগুলি দেখুন
সদাকাতসু

@gamecoder আপনি কি NUnit ইনস্টল করেছেন? আপনি যদি এটি ইনস্টল করতে না পারেন তবে আমি শর্তাধীন ইউনিট পরীক্ষার কোডটি মুছে ফেলতে পারি। ক্লুপ্যাডল আমার আসল প্রবেশ - আমি অন্য দু'জনের লঙ্ঘনের বিষয়ে খুব বেশি চিন্তিত নই যেহেতু তারা সত্যিই জয়ের জন্য খেলছে না।
jwg

আমারও কিছু আপডেট আছে CluePaddle। আমি পরে এগুলির জন্য একটি টান অনুরোধ করব।
jwg

আমি NUnit ইনস্টল করেছি। আপনার অন্যান্য প্রকল্পের রেফারেন্সগুলি ব্যবহার করে আমি এমএসভিএসে এর নেমস্পেসগুলি অন্বেষণ করতে সক্ষম হয়েছি। আমি আপনার টান অনুরোধ প্রত্যাশিত
সদাকাতসু
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.