Покерфайон Лео


13

Беземоційне обличчя

Вступ

Лео подобається грати в покер, але його робота в Tech Inc. занадто вимоглива до нього, щоб навчитися грати добре. Лев, будучи комп'ютерним науковцем, не відлякує. Він вирішує зайняти більше часу, ніж знадобиться, щоб просто вивчити покер, і використовувати його для написання бота в покер, щоб допомогти йому грати краще. Але тепер у Лео є проблема: щоб зрозуміти, як грати трохи краще, Лео потрібно спостерігати за кількома іграми кількох «людей», але «людям» потрібні різні стилі гри, щоб поліпшити якість та реальність гри.

Змагання

Лео нагадує, що насправді існує веб-сайт, присвячений проблемам програмування, і залучає вашу допомогу! Ваше завдання - написати програму, яка грає в "Pokerface" модифіковану версію покеру на 5 карт. Програма буде приймати вклад як 5-картка в будь-якому форматі, який ви бажаєте, після чого програма виводить:

  • Точно (з урахуванням регістру) "справжнього" "1" або "t", якщо гравець бажає обміняти картки, будь-який інший не порожній вихід.
  • Якщо це правда, перелік індексів карт та / або імен карт, які гравець хоче обміняти.
  • Єдине число від 0 до 3, яке вказує, скільки додаткових карт хоче гравець.
  • Роздрукуйте руку, яку хоче використати гравець.

(Дивіться форматування нижче)

Правила Pokerface

  • Оскільки покерфактор є текстовою пригодницькою грою, картки повинні бути представлені послідовно. Картки представлені двома символьними кодами, перший символ - костюм, а другий - назва картки.
    • Картки:
      • 2-9 = 2-9
      • 10 = Т
      • Джек = J
      • Королева = Q
      • Король = К
      • Туз = А
    • Костюми:
      • Лопати = S
      • Клуби = С
      • Серця = Н
      • Алмаз = D

Отже, туз лопат був би SA, 10 сердечок - HT, четверте з діамантів - D4 тощо.

  • Один раунд Pokerface складається з чотирьох кроків:
    • Колода перестановлюється і кожному гравцеві передається п’ять карт.
    • Кожному гравцеві надається можливість обміняти якомога більше карт.
    • Кожному гравцеві надається можливість отримати до трьох карт.
    • Кожен гравець повинен розкрити свою найкращу руку.
  • Найкраща рука виграє і набирає гравцю очко. У разі нічиєї, обидва гравці отримують очко.
  • В одній грі проводиться десять раундів, і гравець з найбільшою кількістю очок виграє і отримує єдину "виграшну очку". У разі вирівнювання, обидва гравці отримують виграшну очко.
  • Лев насправді не має великої кількості грошей, тому ваш бот може припустити, що це ідеальний світ, де немає ставок.

Руки

  • Руки мають рівно 5 карт у довжину (початковий вхід та кінцевий вихід).
  • Руки класифікуються відповідно до описаних тут правил .

Введення-виведення

  • Лео знає лише Java, тому ваша програма повинна виконуватися через API API (командний рядок), а також використовувати STDIN та STDOUT для введення та виведення відповідно.
  • Для кожного етапу введення та виводу, детально описаного вище, кожен вхід і вихід повинні існувати в одному рядку.
  • Після кінцевого результату повинен бути принаймні один зворотний новий рядок. (Це пов’язано з способом зчитування вхідних даних із STDIN)
  • Не допускаються сторонні введення / виведення, крім проміжних і провідних пробілів. Парсер просто не розуміє таких речей, як final_hand=...або draw 0.
  • Під час малювання, вихід - це єдине ціле число, при обміні результатами - це цілий список чи / та карт, визначений нижче, а при передачі оригінальної руки виведення - це список карт, визначений нижче.
  • Усі вхідні / вихідні номери повинні бути додатними цілими числами в базі 10.
  • Ви можете визначити формат для введення картки (див. Формат публікації нижче).
  • True визначається як "true", "1" або "t", а false - будь-яке інше не порожнє значення.
  • Під час кроку обміну:
    • Індекси карт повинні бути виведені з принаймні одним проміжком між ними (наприклад 3 4 0)
    • Імена карт повинні бути виведені з принаймні одним проміжком між ними (наприклад H4 S8)
    • Імена та індекси карт можуть бути змішані у висновку (наприклад 0 H7 3 D3)
    • Доступні пробіли та провідні місця.
    • Вхід у результаті програвача, який виводить вище, буде форматований, як зазначено bot.jlscфайлом, у тому ж порядку, що і запитувалося
  • Кількість карт, які гравець хоче додати до своєї руки, може мати провідні та відсталі місця.
  • Руки повинні бути виведені з принаймні одним пробілом між ними (наприклад H4 D5 CA), дозволені проміжки та провідні місця.
  • Руки не повинні бути виведені в правильному порядку (наприклад , H4 D4 C4 DA SAі H4 DA D4 SA C4обидва представляють собою 4, 4, 4, Ace, Ace, який є повним будинком).
  • Якщо ви хочете створити стратегію, аналізуючи руки опонентів, ви можете зберігати дані в <botname>/dataкаталозі.
    • Після того, як конкуруючі боти показали свої руки, вони будуть записуватися до кожного каталогу даних ботів, в hands.txt, з кожною рукою в новому рядку (розділеному \ n). Файл буде закодовано в US_ASCII.
  • Після того, як ваш бот запитує нові картки або обмінні картки, вони будуть вводитись залежно від формату, який ви вказали у bot.jlscфайлі.

Формат повідомлення

  • Кожна публікація повинна містити дві речі:
    • Вихідний код вашого бота або посилання на загальнодоступне сховище.
    • Zip-файл, що містить:
      • Скомпільована / виконувана версія вашого бота (Якщо файлом є .exe або інший файл, який не піддається компіляції, будь ласка, просто додайте інструкції щодо компіляції у свій пост).
      • bot.jlscФайл, дивіться нижче (сторона примітка: .jlsc розширення тільки з сайд - проекту шахти, формат конфігурації Файл нижче сірників правильний синтаксис, так що не хвилюйтеся.).
    • Файл .zip повинен бути названий таким же, як і ваш бот.
  • Якщо у вас немає доступу до Windows чи іншої утиліти ZIP, або ви не можете зробити .zip з будь-якої причини, просто додайте текст файлу bot.jlsc у свій пост

Файл bot.jlsc:

name= "Botty"
link= "example.com"
cmd= "java -jar Botty.jar"
input_hand= "${0} ${1} ${2} ${3} ${4}"
input_1= "${0}"
input_2= "${0} ${1}"
input_3= "${0} ${1} ${2}"
input_4= "${0} ${1} ${2} ${3}"

Де:

  • "cmd" - команда командного рядка Windows для запуску вашого бота. Зауважте, що ваш бот буде в каталозі <botname>, тому відповідно налаштуйте команду.
  • "name" - це ім'я вашого бота.
  • "link" - посилання на вашу відповідь, вам доведеться відредагувати це після публікації.
    • "input_hand" - це те, як ви хочете відформатувати оригінальну угоду ($ {#}, що представляє картки 0-4).
  • "input_1" - це форматування введення однієї додаткової картки.
  • "input_2" - це те, як ви хочете форматувати вхід двох додаткових карток.
  • "input_3" - це те, як ви хочете форматувати вхід трьох додаткових карток.
  • "input_4" - це те, як ви хочете форматувати вхід чотирьох додаткових карток.

Особливості

  • Ці лазівки заборонені (див. "Загальні підводні камені")
  • Ви не можете писати бота, він завжди виводить найкращу можливу руку в рамках набору правил. (тобто немає тривалих бот-грубої сили, нічого не повинно бути настільки "хорошим", як LeoBot)
  • Ваш бот повинен працювати більше ~ 100 мс або менше (поблажливіший на цей момент, максимум ~ 1 секунду)
  • Будь-який вихід бота після обраної руки буде ігнорований.
  • Стандартні лазівки заборонені.
  • Так, я знаю, що Linux краще, але у мене є Windows PC, тому переконайтеся, що компільована / виконувана версія вашої програми може бути запущена з командного рядка Windows.
    • У мене вже встановлені python та java на моєму комп’ютері, але я готовий оновити нові версії та встановити інші середовища, тому, будь ласка, вкажіть, який тип середовища потребує ваша програма.
  • Ви не можете писати бота, який робить те саме, що інший бот у кожному випадку. Спам-боти дозволені, але не рекомендуються.
  • Ваш бот може використовувати лише ті картки, які він має. Карти, втрачені через обмін або не розпочаті з початку, мають недійсний вихід у кінцевій руці.
  • Введення та вихід можуть містити лише символи ASCII.

Турніри

  • Турніри будуть проводитись, коли я знайду час (мій графік майже такий же упакований, як у Лео, тому це моє бути трохи нечасто. Вибачте за незручності.)
  • У чотирьох іграх для чотирьох ботів буде протистояти іншим, і буде одна гра для кожного можливого набору ботів (тобто багато ігор).
    • Цей процес повториться п’ять разів.
    • Зважаючи на те, як обробник турніру складає групи ботів, буде додано до трьох ботів для заповнення, щоб число ботів було розділеним на 4. Ці боти просто повернуть ту руку, якою вони були розроблені.
  • Після кожного раунду та гри, кількість ботів підраховується на основі кількості виграних ігор.
    • Кілька ботів можуть ділити позицію (зв'язки для перших виграних першими опублікованими).
  • Після завершення турніру, бали будуть додані внизу цієї публікації.

Оцінка балів

Нормальні правила KoTH. Бот (и), які виграють у більшості ігор, виграють виклик.

ЛеоБот

Бот Лео досить розумний. Він не обмінює жодної карти, це занадто важко, але він вимагає максимальної кількості додаткових карт, і він визначає найкращу можливу руку, яку він може зробити, і грає в цю руку. Основна логіка леобота наведена нижче.

package com.gmail.socraticphoenix.pokerface.leobot;

import com.gmail.socraticphoenix.pokerface.lib.card.Card;
import com.gmail.socraticphoenix.pokerface.lib.card.Deck;
import com.gmail.socraticphoenix.pokerface.lib.rule.HandRegistry;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class LeoBot {

    public static void main(String[] args) {
        List<Card> hand = new ArrayList<>();

        Scanner scanner = new Scanner(System.in);
        hand.addAll(Card.parseHand(scanner.nextLine()));
        System.out.println(false);

        System.out.println(3);
        hand.addAll(Card.parseHand(scanner.nextLine()));

        List<List<Card>> possibleHands = LeoBot.getSubsets(hand, 5);
        System.out.println(Deck.toString(possibleHands.stream().sorted((a, b) -> HandRegistry.determineWinner(b, a).comparable()).findFirst().get()));
    }

    private static <T> void getSubsets(List<T> superSet, int k, int idx, List<T> current, List<List<T>> solution) {
        if (current.size() == k) {
            solution.add(new ArrayList<>(current));
            return;
        }
        if (idx == superSet.size()) return;
        T x = superSet.get(idx);
        if (!current.contains(x)) {
            current.add(x);
        }
        getSubsets(superSet, k, idx + 1, current, solution);
        current.remove(x);
        getSubsets(superSet, k, idx + 1, current, solution);
    }

    public static <T> List<List<T>> getSubsets(List<T> superSet, int k) {
        List<List<T>> res = new ArrayList<>();
        getSubsets(superSet, k, 0, new ArrayList<T>(), res);
        return res;
    }

}

Зауважте, що якщо ЛеоБот послідовно виграє турніри, і є хороша кількість заявок, я перестану включати його в біг.

Важливі посилання

Відмова від відповідальності

Leo та Tech Inc. є елементами історії, і будь-яка схожість з реальними компаніями чи людьми є чисто ненавмисною. (Однак, коли "ситуація" Лео додає або віднімає умови запитання, вони фактично є частиною питання ...)


1
@SocratPhoenix Я дуже рекомендую зважити його зараз чи ніколи. Гравцям було б дійсно несправедливо коригувати рахунок після подання в ранг.
Натан Меррілл

2
@DestructibleWatermelon краще? Просто FYI, це було на пісочниці приблизно 2-3 дні ... Ніхто не коментував. Я маю на увазі, все це круто
Сократичний Фенікс

2
Також, @NathanMerrill, мабуть, все ще має рацію щодо того, що виграє німий бот. Дещо дослідивши cjam, 5-байтна програма "f"q+відповідає мінімальним вимогам. Якщо у змаганнях 10 чоловік, це, ймовірно, перемагає всі немітні записи (мабуть, німий запис має> 75 символів, 5 * 10 (оцінка тупого бота, останній) = 50 <75 (оцінка дуже маленького розумного бота (підходить першим))). Таким чином, ви, ймовірно, повинні видалити кодгольф від цього виклику
Destructible Lemon

2
навіть якщо Cjam не вдасться використати, справа в тому, що гантелі стануть розумною стратегією, а вилучення кодового гольфу усуває всі труднощі врівноваження продуктивності розміру VS
Destructible Lemon

1
Загинув код-гольф на смерть ....
Сократичний Фенікс

Відповіді:


1

(Python), Pairbot, не дуже конкурентоспроможний (я не знаю, як скласти команди cmd та ін.)

Pairbot змагатиметься, як тільки хтось допоможе з bot.jlsc та zip-файлами тощо.


Парбот знає, що ти не завжди отримуєш добрі руки. Він знає, що хороші руки рідкісні. Паработ знає, що пари та інші дублікати - одні з найкращих рук. Паработ також знає, що найнижча рука, яку ви можете отримати, - це семи максимум, тому він знає, чи має він 6 високих, це насправді прямий (пара не знає, чому він це знає). Він також знає, якщо його найнижча карта - 10 (без пар), це теж пряма (пара знає, що таким чином він може отримати королівський флеш). Паработ перевіряє в основному однакову кількість дуп, але також перевіряє два типи прямих в особливих випадках.

card_values={"2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8,
             "9":9, "T":10, "J":11, "Q":12, "K":13, "A":14,}
straight=False
def card_valuing(item):
    return card_values[item[1]]

input_list=input().split()
pairs_to_keep=[]
for item in input_list:
    if sum(item[1]==card[1] for card in input_list)>1:
        pairs_to_keep+=[item]
cards_to_remove=input_list
for item in pairs_to_keep:cards_to_remove.remove(item)#we want to remove all non pairs
hand=pairs_to_keep
if pairs_to_keep==[]:
    input_list.sort(key=card_valuing, reverse=True)
    if card_values[input_list[0][1]]==6:
        straight=True
        hand=input_list
    elif card_values[input_list[-1][1]]==10:
        straight=True
        hand=input_list
    else:
        print("true\n"+" ".join(input_list[1:]))
        hand+=input_list[0]+input().split()
elif input_list!=[]:
    print("true\n"+" ".join(input_list))
    hand+=input().split()
else:print(0, end=', ')
if straight:print("0\n0\n"+" ".join(hand))
else:
    print("3")
    hand+=input().split()
    same_number_dict={} #holds the amount of each type (A, 2, 3, etc.)

    def dict_value(item):
        return int(same_number_dict[item[1]])*100+card_values[item[1]]

    for card in hand:
        same_number_dict[card[1]]=sum(card[1] == item[1] for item in hand)

    hand=list(sorted(hand, key=dict_value, reverse=True))
    final_hand =[]
    last_number=hand[0][1]
    hand_taken=0
    while hand_taken < 5:
        if last_number==hand[0][1]:
            final_hand+=[hand[0]]
            hand=hand[1:]
            hand_taken+=1
        else:
            for card in hand:
                if same_number_dict[card[1]]>5-hand_taken:
                    same_number_dict[card[1]]=5-hand_taken
            hand=list(sorted(hand, key=dict_value, reverse=True))
            last_number=hand[0][1]
    print(" ".join(final_hand))

Формат для введення такий же, як у прикладі: розділений пробілами


Якби Сократичний Фенікс міг допомогти з файлами, це було б добре


Розумний! Отже, файл, який ви хочете, є тут , я збираюсь відредагувати основну публікацію, щоб зробити фактичний .zip необов’язковим ...
Socrat Phoenix

Також +1 для FGITW
Phoenix

Більше схожий на FGITLOSG (Найшвидший пістолет у країні повільних гармат).
Зруйнований лимон

Правда. Я не впевнений, що введення / вихід у правильній формі. Коли я ввожу руку, програма надрукує "True", а потім її поточну руку. Я вважаю, що ви хочете просто надрукувати "false", оскільки "True" означає, що ви хочете обмінятись картками. По-друге, програмі необхідно надрукувати єдине ціле число під час малювання, або цілі числа, розділені пробілами при обміні. Не "малювати 0." Я спробую уточнити основний пост.
Сократичний Фенікс

[Чи вважає це зараз змаганням?] Не побачив нових повідомлень. Я виправлю бота одразу
руйнуючий лимон

1

Водопровідник, Пітон

Сантехнік - це все про промивання. Сантехнік також надає пріоритетні картки з більшою вартістю (це означає, що він іноді може отримувати прямі флеші, особливо королівські (якщо вони трапляються).) Водопровідник сильно заплутався, якщо йому не підходить флеш, за винятком того, що він може пощастить прямо. Водопровідник матиме відмивання приблизно в 20% часу, якщо розрахунки Шерлока9 будуть правильними

hand=input().split()
suit_in_hand={"S":0,"C":0,"D":0,"H":0}
card_values={"2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8,
             "9":9, "T":10, "J":11, "Q":12, "K":13, "A":14,}
def value_sort(x):
    return card_values[x[1]]
def suit_sort(x):
    return suit_in_hand[x[0]]

for card in hand:
    suit_in_hand[card[0]]+=1

hand.sort(key=suit_sort, reverse=True)

print(" ".join(hand[suit_in_hand[hand[0][0]]:]))
hand=hand[:suit_in_hand[hand[0][0]]]

for new_card in input().split():
    hand+=[new_card]
    suit_in_hand[new_card[0]]+=1

print(3)

for new_card in input().split():
    hand+=[new_card]
    suit_in_hand[new_card[0]]+=1
hand.sort(key=value_sort, reverse=True)
hand.sort(key=suit_sort, reverse=True)
print(" ".join(hand[:5]))

Також приймає дані, розділені пробілами, як і інші мої два боти


Примітка: я трохи змінив правила виводу через помилку у власній програмі турніру. Тепер після остаточного результату повинен бути принаймні один зворотний новий рядок.
Сократичний Фенікс

1

LadyGaga, Python 3

  • Дещо сліпий до костюмів
  • Має плаття, повне клопів
  • І любить грати в Poker Face раз на час

    from math import ceil as f
    M=lambda A:max(set(A),key=A.count)
    K=lambda A:A.count(M(A))
    O=lambda A:range(len(A))
    J=lambda A:A[0]+str(U(A[1]))
    X={"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"T":10,"J":11,"Q":12,"K":13,"A":14}
    def V(A):return([A[0]]+[int(X[A[1]])])
    def U(c):
     if c==10:c='T'
     if c==11:c='J'
     if c==12:c='Q'
     if c==13:c='K'
     if c==14:c='A'
     return(c)
    def P(A):
     S=[];C=[];t=len(A)
     for x in A:S.append(x[0]);C.append(x[1])
     B=[0]*9;q=len(set(C));p=K(C);D=list(set(C));D.sort()
     B[2]=1/f(13**(4-p));B[6]=1/f(13**(3-p));B[8]=1/f(13**(2-p))
     if (p,q)==(2,4):B[3]=1/1100;B[7]=5/34
     if (p,q)==(3,3):B[3]=1/169;B[7]=1/169
     if (p,q)==(4,2):B[3]=1/13;B[7]=1
     if (p,q)==(2,3):B[3]=5/169;B[7]=1
     if (p,q)==(3,2):B[3]=1;B[7]=1
     for x in O(D):D[x]-=x
     j=M(D);h=K(D)-5;B[5]=13**h
     for x in O(D):
      if j+h<D[x]<j-h and D[x]!=j:B[5]*=13
     W=K(S);B[4]=(4**(W-t))*(13-W)/52
     return(B,M(S))
    def E(B,h,u):
     x=0;D=[];C=[]
     while 1:
      k=list(C)
      C=[]
      while 1:
       p=list(B);del p[x]
       if len(D)==3:break
       if P(p)[0][h]>=P(B)[0][h]:C.append(B[x])
       x+=1
       if x>len(p):break
      if len(C)==0:break
      for x in O(C):
       if k==C or not((u in C[x])and(len(C)-1)):D.append(C[x]);del B[B.index(C[x])]
     return(D)
    s=input()
    A=s.split(' ')
    b=list(map(V,A));G,u=P(b);F=[649739,72192,4164,693,508,254,46.3,20,1.4];H=[]
    for x in O(F):F[x]=1-((1-(1/F[x]))**4)
    for x in O(F):H.append(G[x]-F[x])
    Y=H.index(max(H));p=[]
    e=E(list(b),Y,u);g=list(e)
    for x in O(e):e[x]=J(e[x])
    print(' '.join(e)if len(e)else'')
    for x in g:
     if x in b:del b[b.index(x)]
    s=input()
    if len(s):
     A=s.split(' ')
     b+=list(map(V,A))
    print(3)
    s=input()
    A=s.split(' ')
    b+=list(map(V,A));G,u=P(b);H=[]
    for x in O(F):H.append(G[x]-F[x])
    Y=H.index(max(H))
    e=E(list(b),Y,u)
    for x in e:
     if x in b:del b[b.index(x)]
    for x in O(b):b[x]=J(b[x])
    print(' '.join(b[:5]))
    print()
    
    • (I / O), змодельований після PlumberBot -Edit: великі виправлення помилок завдяки Destructible Watermelon -Edit: Завдяки новим правилам, останній рядок після остаточного виводу

Можливо, ви хочете використовувати словник замість усіх складних речей для значень карт
Destructible Lemon

Все, що було запаковано в масив, вже було мені відомо. Який розділ коду я міг би скоротити?
Маджента

def V(A): b=[A[0]];c=A[1] if c=='T':c=10 if c=='J':c=11 if c=='Q':c=12 if c=='K':c=13 if c=='A':c=14 return (b + [int(c)]) до x={"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"T":10,"J":11,"Q":12,"K":13,"A":14,} def V(A):return(A[0] + x[A[1]])
Зруйнований лимон

Pairbot лише трохи довший за вашу програму, і це тому, що він читабельний
руйнівний лимон

Я знаю. Погані звички кодогольфінгу.
Маджента

0

LuckyBot, Python

Парбот запросив свого приятеля Лакібота, який вискочив на шанс. Лакібот спостерігав багато вигаданого покеру, і вважав, що він з'ясував таємницю покеру: удача. Всім відомо, що справжні плюси (наприклад, Джеймс Бонд) справді покладаються і отримують хороші руки, а не майстерність. Отже, він не дивиться на свої картки, а намагається зібрати в них якомога більше удачі


lucky_number=24 #IMPORTANT

from random import randint as roll


def lucky_shuffle(i):
    return sorted(i, key=lucky_dice)


def lucky_dice(seed):
    return sum(roll(1,6)for i in range(roll(1,6)))


hand=lucky_shuffle(input().split())

throw=lucky_dice(lucky_number)%5
print("true\n"+" ".join(hand[throw:]))

hand=hand[:throw]+lucky_shuffle(input().split())

hand=lucky_shuffle(hand)
hand=lucky_shuffle(hand)
#One more for good luck
hand=lucky_shuffle(hand)
#maybe one more
hand=lucky_shuffle(hand)
#I got a good feeling about this one
hand=lucky_shuffle(hand)

hand=lucky_shuffle(hand)
#I think I'm done
hand=lucky_shuffle(hand)
#for real this time


hand=lucky_shuffle(hand)

print("3")
hand=hand+lucky_shuffle(input().split())
#All right, I got a real good feeling about this,
#let me shuffle some more luck into them cards!


def extra_super_lucky_shuffle(item):
 return lucky_shuffle(lucky_shuffle(lucky_shuffle(\
    lucky_shuffle(lucky_shuffle(lucky_shuffle(\
        lucky_shuffle(lucky_shuffle(lucky_shuffle(item)))))))))


def super_duper_extra_ultra_uber_luckyshuffle(item):
    return extra_super_lucky_shuffle(extra_super_lucky_shuffle(\
        extra_super_lucky_shuffle(extra_super_lucky_shuffle(item))))


hand=super_duper_extra_ultra_uber_luckyshuffle(super_duper_extra_ultra_uber_luckyshuffle(hand))
#cmoooooooooooooooon
print(hand[:5])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.