Al ya da Bırak II: Bilgisayarlar İçin Bir Oyun Gösterisi


20

Bu, her Pazartesi Midnight PST'de yayınlayacağım bir dizi bulmacada ikinci. İlk bulmaca burada .

Bağlam:

Seçkin bir milyarder, dünyanın en iyi ve en parlak programcılarını çekmek için bir oyun şovu yarattı. Pazartesi günleri gece yarısının inişinde, bir katılımcı havuzundan bir kişiyi haftanın yarışmacısı olarak seçer ve onlara bir oyun sunar. Bu haftanın şanslı yarışmacısısınız!

Bu haftanın oyunu:

Ana bilgisayar, 10.000 dijital zarf yığınına API erişimi sağlar. Bu zarflar rastgele sıralanır ve içlerinde 1 ila 10.000 dolar arasında bir dolar değeri içerir (iki zarf aynı dolar değerini içermez).

Kullanabileceğiniz 4 komut var:

  1. Oku (): Yığının üstündeki zarftaki dolar rakamını okuyun.

  2. Take (): Zarftaki dolar şeklini oyun şov cüzdanınıza ekleyin ve zarfı desteden çıkarın.

  3. Başarılı (): Yığının üstündeki zarfı çıkar.

  4. Oracle (M): Şu anda Okuyabileceğinizi () içermeyen, bir sonraki M zarfların ortalama değerini döndürür.

Kurallar:

  1. Zarfta Pass () kullanırsanız, içindeki para sonsuza kadar kaybolur.

  2. $ X içeren bir zarfta Take () kullanırsanız, bu noktadan sonra <$ X içeren bir zarf üzerinde Take () yöntemini kullanamazsınız. Bu zarflardan birinde Take () cüzdanınıza 0 $ ekleyecektir.

  3. T dönüşünde Oracle (M) kullanırsanız, T + 1 ile T + M ortalamaları arasındaki zarflar iade edilir. Oracle (), T + M dönüşüne kadar devre dışı bırakıldı.

Oyunu en fazla parayla bitiren bir algoritma yazın.

Algoritmanızı Python'a yazıyorsanız, @Maltysen tarafından sağlanan bu denetleyiciyi kullanmaktan çekinmeyin: https://gist.github.com/livinginformation/70ae3f2a57ecba4387b5

Notlar 1: Bu durumda "Maksimal", N> = 1000 çalıştıktan sonra cüzdanınızdaki ortanca değer anlamına gelir. Her ne kadar yanlış olduğumu kanıtlasam da, belirli bir algoritma için medyan değerin N sonsuza yükseldikçe yakınlaşacağını umuyorum. Ortalamayı en üst düzeye çıkarmaya çalışmaktan çekinmeyin, ancak ortalamanın medyandan daha küçük bir N tarafından atılma olasılığının daha yüksek olduğunu hissediyorum.

Not 2: Bu bulmacanın önceki kısmına yönelik tüm çözümlerin burada geçerli olduğu için, bunları yeniden göndermenin değeri azdır. Kısım II için yalnızca önceki bulmacaların algoritmik iyileştirmeleri dikkate alınacaktır.

Düzenleme: Metadaki bu yazının ışığında ödül koşulu kaldırıldı .


Vay canına, aşırı uyuduğuma inanamıyorum: O
Beta Çürümesi

@Beta Çürüme saati geçiyor! :)
LivingInformation

Rüyanın anlamı nedir? Daha önce okunan tüm zarfların bir çetelesini tutarak kendi özgür kehanetinizi oluşturabilirsiniz. Neyi yanlış anlıyorum?
Luis Mendo

1
@LuisMendo Kendi hesabınızla, sadece kalan tüm değerlerin ortalamasını bilebilirsiniz . Kehanetle, Mseçeceğiniz bir sonraki değerlerin ortalamasını alabilirsiniz M.
Reto Koradi

1
Önceki zorluğunuzdaki tüm çözümler aynı zamanda bu zorluğa da geçerli çözümler olduğundan, bunları dolaylı olarak sunabilir miyiz?
Reto Koradi

Yanıtlar:


9

Harika $ 713337 $ 817829 $ 818227

Önyükleme kodu:

class Instance {
    List values = new ArrayList(1..10000); {
        Collections.shuffle(values)
    }
    int i = 0
    int value = 0
    int max = 0
    int nextOracle = 0

    def pass() {
        if (i >= 10000)
            throw new NoSuchElementException()
        i++
    }

    def take() {
        if (i >= 10000)
            throw new NoSuchElementException()
        int v = values[i]
        if (v > max) {
            max = v
            value += v
        }
        i++
    }

    double oracle(int m) {
        if (m <= 0 || i < nextOracle || i + m >= 10000)
            throw new NoSuchElementException()

        nextOracle = i + m
        values.subList(i + 1, i + m + 1).stream().reduce { l, r -> r+l }.get() / m
    }

    int read() {
        if (i >= 10000)
            throw new NoSuchElementException()
        values[i]
    }
}

Algoritma

double square(double v) { v * v }
final double factor = Math.pow(1.5, 1.1)
int attempts = 5000
(1..attempts).stream().parallel().mapToLong {
    def puzzle = new Instance()

    int[] memory = 1..10000 // We will remember every envelope
    int memStart = 0

    while (memStart < 10000 - 3) {
        int value = puzzle.read()
        int i = Arrays.binarySearch(memory, memStart, 10000, value) - memStart
        if (i < 0) { // We can't use the money
            puzzle.pass()
            continue
        }
        if (i == 0) { // Of course we take the lowest
            puzzle.take()
            memStart++
            continue
        }
        int remaining = Arrays.stream(memory, i + 1 + memStart, 10000).sum() // Money we could win if taken
        int losing = Arrays.stream(memory, memStart, memStart + i).sum() // Money we cna't win if taken
        if (value > losing) { // If we pass, we lose money automatically
            puzzle.take()
            memStart += i + 1
        } else if ((losing - value * 16 / 7) * square(Math.log(i)) > remaining / factor) {
            System.arraycopy(memory, memStart, memory, ++memStart, i)
            puzzle.pass()
        } else {
            puzzle.take()
            memStart += i + 1
        }
    }

    // It's broken down to last three elements
    List values = Arrays.copyOfRange(memory, 10000 - 3, 10000)
    while (!values.contains(puzzle.read())) // Skip values we can't use
        puzzle.pass()
    int value1 = puzzle.read()
    int value2 = puzzle.oracle(1)
    if (value1 == values.max() && (
            values.contains(value2)
            ? (value1 * 2 < values.sum() && values.min() == value2)
            : (value1 < values.min() / 2 + (values - [value1]).max())
            )) {
        puzzle.pass()
    }

    // Finish it
    while (puzzle.i < puzzle.values.size()) {
        puzzle.take()
    }

    puzzle.value as Long
}.sum() / attempts // Sum runs and average

Kalan değerleri olası değerlerle karşılaştırırım. Bu komut dosyası hızlı değildir (1000x simülasyon başına 1 dakika sürer) ... ancak simülasyonları aynı anda gerçekleştirir.

Algoritmamın neden çalıştığı hakkında hiçbir fikrim yok, ama sadece deneme yanılma oldu: matematiksel işlemleri bir araya getirmek ve sabitleri manipüle etmek. Skorun dalgalanmalarını azaltmak için mevcut puan için 5000x koştum (yineleme sayısına bağlı olarak +/- 4000 $).

Sonunda kehanet olmasa bile , önceki bulmaca için @ orlp'nin çözümünü hala (zar zor) yenmelidir .


7

C # - 803.603 $ şimdi -> 804.760 $ (kehanetle)

Önyükleme Kodu

public static class ShuffleExtension
{
    private static Random rng = new Random();  

    public static void Shuffle<T>(this IList<T> list)  
    {  
        int n = list.Count;
        while (n > 1) {  
            n--;  
            int k = rng.Next(n + 1);  
            T value = list[k];  
            list[k] = list[n];  
            list[n] = value;  
        }  
    }
}

public class Puzzle
{
    public List<int> Values = new List<int>(10000);

    public Puzzle()
    {
        for ( int i = 1; i <= 10000; i++ )
        {
            Values.Add(i);
        }
        Values.Shuffle();
    }

    public int i = 0;
    public int value = 0;
    public int max = 0;
    public int nextOracle = 0;

    public void Pass() {
        if ( i >= Values.Count )
            throw new IndexOutOfRangeException();
        i++;
    }

    public void Take() {
        if (i >= Values.Count )
            throw new IndexOutOfRangeException();
        int v = Values[i];
        if (v > max) {
            max = v;
            value += v;
        }
        i++;
    }

    public double oracle(int m) {
    if (m <= 0) { 
        throw new IndexOutOfRangeException();
    }
    if ( i < nextOracle ) {
        throw new IndexOutOfRangeException();
    }
    if ( i + 1 + m > Values.Count ) {
        throw new IndexOutOfRangeException();
    }

    nextOracle = i + m;
    var oracleValues = new List<int>();
    for ( int l = 0; l < m; l++ )
    {
        oracleValues.Add(Values[i + 1 + l]);
    }
    return oracleValues.Average (v => v);
}

    public int Read() {
        if (i >= Values.Count )
            throw new IndexOutOfRangeException();
        return Values[i];
    }
}

Oyun Kodu:

    void Main()
{
    var m = 0;
    for ( int l = 0; l < 1000; l++ )
    {
        var game = new Puzzle();
        var maxVal = 0;
        var lastOracle = 0;
        var lastOracleValue = 0.0m;
        var oracleValueForIOf = 0;

        for ( int i = 0; i < 10000; i++ )
        {
            var val = game.Read();
            var oracleStep = 1;
            var canUseOracle = (i - lastOracle >= oracleStep) && i + oracleStep + 1 <= 10000;
            if ( canUseOracle )
            {
                var oracle = game.oracle(oracleStep);
                lastOracle = i;
                lastOracleValue = (decimal)oracle;
                oracleValueForIOf = i + 1;
            }
            if ( TakeTheMoney(val, maxVal, oracleValueForIOf, lastOracleValue, i) )
            {
                maxVal = val;
                game.Take();
            }
            else
            {
                game.Pass();
            }
        }
        m += game.value;
    }
    ((int)(m / 1000)).Dump();
}

private bool TakeTheMoney(int val, int maxVal, int oracleValueForIOf, decimal lastOracleValue, int i)
{
    if ( val > maxVal )
    {
        if ( oracleValueForIOf != i + 1
            &&
            (val < 466.7m + (0.9352m * maxVal) + (0.0275m * i))
            )
        {
            return true;
        }

        if (oracleValueForIOf == i + 1)
        {
            if ( val < 466.7m + (0.9352m * maxVal) + (0.0275m * i) )
            {
                return true;
            }
            if ( lastOracleValue > 466.7m + (0.9352m * val) + (0.0275m * i + 1) )
            {
                if ( val < 466.7m + (0.9352m * maxVal) + (0.0275m * i + 1) )
                {
                    return true;
                }
            }
        }
    }
    return false;
}

Kredi Reto Koradi'ye aittir ( /codegolf//a/54181/30910 )

Düzenleme: Oracle Temel Kullanımı uygulandı. Bir sonraki kehanet kullanılacak eşiğin üzerindeyse geçerli zarfı Oracle Index'in dizinine genişletin. Bu sık sık vurmak değil ama bir gelişme olduğunu ;-)


4
Önceki zorluğun çözümlerini yeniden yayınlamanın çok verimli olduğunu düşünmüyorum. Hepimiz bu çözümlerin bu zorluğun bir temeli olarak kullanılabileceğini kabul ettik ve OP için bunu nasıl ele almamız gerektiğini soran bir yorum bırakmıştım. Fikir, önceki çözümün çözümlerinden ideal olarak daha iyi olan kendi çözümünüzü bulmanızdır.
Reto Koradi

lütfen downvoting durdurmak :) benim başvurudan sonra not numarası 2 eklendi. ve diğer çözümlerden daha etkili olduğu için - buraya gönderdim. mevcut çözümleri yenmek için oracle kullanmaya gerek yok.
Stephan Schinkel

@StephanSchinkel Geçerli puanı artırmak için Oracle'ı dahil etmeyi başarırsanız benim oylarım var. Sadece 1 $ bile.
Dorus

@BetaDecay tam olarak topluluk tarafından kaşlarını çatmış olan nedir? Ben sadece op gelen soruyu takip ettim. Sunumdan sonra bir kez daha Note Number 2 eklendi.
Stephan Schinkel

Sınavın I. bölümünden bir çözüm kullanmamak.
Stephan Schinkel

4

Python - 74112 dolar

Yalnızca geçerli değer bir sonraki değerden düşükse (yani her ikisini de alabilirsiniz) alın.

def algo():
  try:
    o=oracle(1)
  except ValueError:
    take()
  r=read()
  if r>o:
    passe()
  else:
    take()

Python - (hala ortalamayı hesaplıyor)

Bu cevabın hesaplanması ÇOK UZUN. Yaklaşık 670.000 $ 'a ulaşır . Gördüğüm her zarfı hatırlıyorum. Her karar vermem gerektiğinde, geçerli zarfı alırsam veya sırasıyla bırakırsam cüzdanıma potansiyel olarak ekleyebileceğim iki kalan zarf listesi oluştururum.

Kodu optimize etmedim.

def algo_2():
  global max_taken, past
  weight=0.92 #Empirically chosen.
  r=read()
  if len(past)==0:
    past.append(r)
    passe()
    return
  if r<max_taken:
    past.append(r)
    take() #the same as passe
    return
  coming=[x for x in range(1,10001) if x not in past and x>max_taken and x!=r ]
  comingIfTake=[x for x in range(1,10001) if x not in past and x>r ]
  if sum(coming)*weight<=sum(comingIfTake)+r:
    past.append(r)
    take()
  else:
    past.append(r)
    passe()

Ve init_game şöyle başlar:

def init_game():
    global stack, wallet, max_taken, oracle_turns, past
    past=[]

3
Geçmiş, gelen ve comingIfTake'i temsil etmek için kümeler kullanır ve kavşaklar kullanırsanız, kodunuz çok daha hızlı olur.
Nathan Merrill

4

C # - 780.176 dolar

Bir sonraki değerin, kalan tüm değerlerin% 5'inin altında olup olmadığını kontrol edin. Biz sonuna kadar daha rahat olsun.

public class Taker
{
    private List<int> remaining;
    private Game game;

    public Taker(Game game)
    {
        this.game = game;
        remaining = Enumerable.Range(1, game.Size + 100).ToList();
    }

    int score = 0;

    public int PlayGame()
    {
        for (int i = 0; i < game.Size; i++)
        {
            if (game.Read() < game.Max ||
                game.Read() > selectThreshold() ||
                doOracle()
                )
            {
                remaining.Remove(game.Read());
                game.Pass();
                continue;
            }
            remaining = remaining.SkipWhile(j => j < game.Read()).ToList();
            score += game.Take();
        }
        return score;
    }

    private bool doOracle()
    {
        return game.Oracle(1) < game.Read() &&
            game.Oracle(1) > game.Max;
    }

    private int selectThreshold()
    {
        int selector = (int)(remaining.Count * 0.05);
        return remaining.ElementAt(selector);
    }
}

Ve oyun sınıfım, çok çirkin, oyun sınıfı, kehanete izin veriliyorsa bile doğrulamıyor, ancak sadece Oracle (1) kullandığımdan bu bir sorun olmamalı.

public class Game
{
    private int[] list;
    private int position = 0;
    private int max = 0;
    public int Max { get { return max; } }
    public int Size { get { return list.Length; } }

    public Game(int[] list)
    {
        this.list = list;
    }

    public int Read()
    {
        return list[position];
    }

    public int Take()
    {
        if (list[position] < max)
        {
            position++;
            return 0;
        }
        max = list[position];
        return list[position++];
    }

    public void Pass()
    {
        position++;
    }

    public int Oracle(int M)
    {
        int next = position + 1;
        M = Math.Max(0, Math.Min(M, list.Length - next));
        return new ArraySegment<int>(list, next, M).Sum();
    }
}

4

Java, 804.991 dolar

Puan 1001 mermidir. Muhtemelen bu cevap ile Stephan Schinkel'in araması yapmak için çok yakın .

Bu, önceki zorluktaki cevabıma dayanıyor, getirileri tahmin etmek için aynı entropi tabanlı hesaplamayı kullanıyor. Temel fark, zarfları şimdi çiftler halinde almasıdır (1 ve 2, sonra 3 ve 4, vb.) Ve olası alma, alma, geçiş, vb. Kombinasyonlarına bakar. geçerli zarf sayısı gerçekten az olduğunda kesin tahmini puan.

Yazdığım "sarıcı" gerçekten gerçek bir sarıcı değil, sadece Oracle(1)her turda bir işlev çağırmak yerine çiftler halinde zarflar veriyor .

Genel olarak, artan karmaşıklığa rağmen, bu botun gerçekten benim öncekinden daha iyi olmadığını söyleyebilirim.

oyuncu

import java.lang.Math;
public class Player2
{
    public int[] V;

    public Player2(int s)
    {
        V = new int[s];
        for(int i = 0; i<V.length; i++)
        {
            V[i] = i+1;
        }
        ////System.out.println();
    }

    public boolean [] takeQ(int x, int y)
    {
        //System.out.println("Look: " + x + " " + y);
        boolean [] move = new boolean[]{false,false};
        double max = 0;
        double val = 0;
        int[] nextV = V;

        ////System.out.println("look " + x);
        int i = find(V,x);
        if(i >= 0)  //if found
        {
            //try taking first envelope
            int[] newVt = takeSlice(V,i);
            //System.out.println("  T: " + ats(newVt));
            int j = find(newVt,y);
            if(j >= 0)
            {
                //try taking first and second
                int[] newVtt = takeSlice(newVt,j);
                val = x + y + calcVal(newVtt);
                //System.out.println("  TT: " + ats(newVtt) + " " + val);
                if(val > max)
                {
                    move = new boolean[]{true,true};
                    max = val;
                    nextV = newVtt;
                }
            }
            //try taking first and passing second
            int[] newVtp = passSlice(newVt,j);

            val = x + calcVal(newVtp);
            //System.out.println("  TP: " + ats(newVtp) + " " + val);
            if(val > max)
            {
                move = new boolean[]{true,false};
                max = val;
                nextV = newVtp;
            }
        }
        int[] newVp = passSlice(V,i);
        //System.out.println("  V: " + ats(V));
        //System.out.println("  P: " + ats(newVp));
        int j = find(newVp,y);
        if(j >= 0)
        {
            //try passing first and taking second
            int[] newVpt = takeSlice(newVp,j);
            val = y + calcVal(newVpt);
            //System.out.println("  PT: " + ats(newVpt) + " " + val);
            if(val > max)
            {
                move = new boolean[]{false,true};
                max = val;
                nextV = newVpt;
            }
        }
        //try taking first and passing second
        int[] newVpp = passSlice(newVp,j);

        val = calcVal(newVpp);
        //System.out.println("  PP: " + ats(newVpp) + " " + val);
        if(val > max)
        {
            move = new boolean[]{false,false};
            max = val;
            nextV = newVpp;
        }
        V = nextV;
        //System.out.println("  NEW: " + ats(V));
        return move;
    }

    public static String ats(int [] a)
    {
        String s = "";
        for(int i = 0; i < a.length; i++)
        {
            s += a[i] + ",";
        }
        return s;
    }

    public static int[] takeSlice (int[] list, int loc)
    {
        int [] newlist = new int[list.length - loc - 1];
        for(int j = loc + 1; j < list.length; j++)
        {
            newlist[j - loc - 1] = list[j];
        }
        return newlist;
    }

    public static int[] passSlice (int[] list, int loc)
    {
        int [] newlist = list;
        if(loc >= 0)
        {
            newlist = new int[list.length-1];
            for(int k = 0; k < loc; k++)
            {
                newlist[k] = list[k];
            }
            for(int k = loc + 1; k < list.length; k++)
            {
                newlist[k-1] = list[k];
            }
        }
        return newlist;
    }

    public static double calcVal(int [] list)
    {
        if(list.length < 8)
        {
            for(int i : list)
            {
                ////System.out.print(i + ",");
            }

                ////System.out.println();
            return computeMean(list);

        }
        return smoothEstimate(list);
    }

    public static double computeMean(int[] V)
    {
        if(V.length == 1)
        {
            return V[0];
        }
        else if(V.length > 1)
        {
            double[] Es = new double[V.length];
            for(int i = 0; i < V.length; i++)
            {
                int[] newVp = new int[V.length - 1];
                for(int j = 0; j < i; j++)
                {
                    newVp[j] = V[j];
                }
                for(int j = i + 1; j < V.length; j++)
                {
                    newVp[j-1] = V[j];
                }
                double pass = computeMean(newVp);
                int[] newVt = new int[V.length - i - 1];
                for(int j = i + 1; j < V.length; j++)
                {
                    newVt[j - i - 1] = V[j];
                }
                double take = V[i] + computeMean(newVt);
                if(take > pass)
                {
                    Es[i] = take;
                }
                else
                {
                    Es[i] = pass;
                }
            }
            double sum = 0;
            for(double d : Es)
            {
                sum += d;
            }
            return sum/V.length;
        }
        else
        {
            return 0;
        }
    }

    public static double smoothEstimate(int [] list)
    {
        double total = 0;
        for(int i : list)
        {
            total+=i;
        }
        double ent = 0;
        for(int i : list)
        {
            if(i > 0)
            {
                ent -= i/total * Math.log(i/total);
            }
        }
        ////System.out.println("      total " + total);
        ////System.out.println("      entro " + Math.exp(ent));
        ////System.out.println("      count " + list.length);
        return total * Math.pow(Math.exp(ent),-0.5) * 4.0/3;// * 1.1287 + 0.05284);
    }

    public static int find(int[] list, int search)
    {
        int first  = 0;
        int last   = list.length - 1;
        int middle = (first + last)/2;

        while( first <= last )
        {
            if ( list[middle] < search )
                first = middle + 1;    
            else if ( list[middle] == search )
                break;
            else
                last = middle - 1;

            middle = (first + last)/2;
        }

        if(first > last)
        {
            return -1;
        }
        return middle;
    }
}

kontrolör

import java.lang.Math;
import java.util.Random;
import java.util.ArrayList;
import java.util.Collections;
public class Controller2
{
    public static void main(String [] args)
    {
        int size = 10000;
        int rounds = 1001;
        ArrayList<Integer> results = new ArrayList<Integer>();
        for(int round = 0; round < rounds; round++)
        {
            int[] envelopes = new int[size];
            for(int i = 0; i<envelopes.length; i++)
            {
                envelopes[i] = i+1;
            }
            shuffleArray(envelopes);
            Player2 p = new Player2(size);
            int cutoff = 0;
            int winnings = 0;
            for(int i = 0; i<envelopes.length; i+=2)
            {
                boolean [] take = p.takeQ(envelopes[i],envelopes[i+1]);
                if(take[0] && envelopes[i] >= cutoff)
                {
                    winnings += envelopes[i];
                    cutoff = envelopes[i];
                }
                if(take[1] && envelopes[i+1] >= cutoff)
                {
                    winnings += envelopes[i+1];
                    cutoff = envelopes[i+1];
                }
            }
            results.add(winnings);
        }
        Collections.sort(results);
        System.out.println(rounds + " rounds, median is " + results.get(results.size()/2));

    }

    //stol... I mean borrowed from http://stackoverflow.com/questions/1519736/random-shuffling-of-an-array
    static void shuffleArray(int[] ar)
    {
        Random rnd = new Random();
        for (int i = ar.length - 1; i > 0; i--)
        {
            int index = rnd.nextInt(i + 1);
            // Simple swap
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }
}

Bitcoin adresi: 1BVBs9ZEP8YY4EpV868nxi2R23YfL7hdMq


3

Python 3 - 615570 dolar

Aslında kehaneti kullanmaz ... Eh :)

def algo():
    global prevs

    try:
        prevs.append(read())
    except NameError:
        prevs = [read()]

    if len(prevs) > 10000:
        prevs = [prevs[-1]]

    if read() < round(len(prevs),-1):
        take()
    else:
        passe()

Önceki tüm zarfların bir listesini oluşturur ve geçerli zarfın 10 zarflık artışlarla önceki zarf sayısından daha az olup olmadığını kontrol eder.


0

Python, 87.424

İşte sade ve kolay bir algoritma, şanslı yedi.

def LuckyNumber7():
Test = read()
if "7" in str(Test):
    take()
else:
    passe()

test(LuckyNumber7)

Temel olarak, read () öğesini bir dizeye dönüştürür ve içinde yedi olup olmadığını kontrol eder. Varsa, zarfı alır. Değilse, geçer.

Ortalama 81.000 civarında, takip etmiyorum.


Yani bu şansa güvenmenin başarılı bir strateji olmadığını gösteriyor? ;)
Reto Koradi

@RetoKoradi Yep: D
The_Basset_Hound
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.