Vectory'ye! - Vektör Yarış Grand Prix


39

Kullanıcı CarpetPython , artan arama alanı nedeniyle buluşsal çözümlere daha fazla odaklanan bu soruna yeni bir bakış attı . Şahsen ben bu meydan okuma benimkinden daha güzel olduğunu düşünüyorum, o yüzden bir deneyin!

Vektörel yarış, bir kalem ve bir kare kare kağıt ile oynanabilen bağımlılık yapan bir oyundur. Kağıdın üzerine rasgele bir yarış pisti çiziyorsunuz, bir başlangıç ​​ve bitiş tanımlıyorsunuz ve sonra nokta büyüklüğündeki arabanızı sıra tabanlı bir şekilde kullanıyorsunuz. Mümkün olduğu kadar çabuk bitirin ama bir duvarda kalmamaya dikkat edin!

Parça

  • Harita, her hücrenin tam sayı koordinatlarına sahip olduğu iki boyutlu bir ızgaradır.
  • Izgara hücreleri üzerinde hareket edersiniz.
  • Her ızgara hücresi, parçanın bir parçası veya bir duvardır.
  • Tam olarak bir iz hücresi başlangıç ​​koordinatıdır.
  • Hedef olarak en az bir iz hücresi belirlendi. Bunlardan herhangi birine inmek yarışı tamamlar. Birden fazla hedef hücre mutlaka bağlı değildir.

Arabayı Yönetmek

Aracınız belirli bir koordinatta ve hız vektörüyle başlar (0, 0). Her turda, hızın her bir bileşenini ayarlayabilir ±1veya olduğu gibi bırakabilirsiniz. Sonra, ortaya çıkan hız vektörü aracınızın konumuna eklenir.

Bir resim yardımcı olabilir! Kırmızı daire son sıranızdaydı. Mavi daire şu anki pozisyonunuz. Hızınız kırmızıdan mavi daireye vektördür. Bu sırada hızınızı nasıl ayarladığınıza bağlı olarak yeşil dairelerin herhangi birine gidebilirsiniz.

                                    görüntü tanımını buraya girin

Eğer varsa kara bir duvarda, hemen kaybederler.

Senin görevin

Tahmin ettiniz: girdi olarak bir yarış pisti verildiğinde, aracı hedef hücrelerden birine mümkün olduğu kadar az zamanda yönlendiren bir program yazın . Çözümünüz, keyfi izlerle makul derecede iyi başa çıkabilmeli ve verilen test durumlarına göre özellikle optimize edilmemelidir.

Giriş

Programınız çağrıldığında, stdin'den okuyun :

target
n m
[ASCII representation of an n x m racetrack]
time

targetParçayı tamamlamak için harcayabileceğiniz maksimum dönüş sayısıdır ve parça timeiçin toplam süre bütçenizdir (saniye cinsinden değil). Zamanlamayla ilgili ayrıntılar için aşağıya bakınız.

Yeni satır ayrılmış parça için aşağıdaki karakterler kullanılır:

  • # - duvar
  • S- başlangıç
  • *- bir hedef
  • . - diğer tüm iz hücreleri (yol)

n x mSağlanan ızgara dışındaki tüm hücrelerin duvar olduğu belirtilir.

Koordinat orijini sol üst köşede.

İşte basit bir örnek:

8
4.0
9 6
###...***
###...***
###...***
......###
S.....###
......###

0 tabanlı dizinlemeyi kullanarak başlangıç ​​koordinatı olur (0,4).

Her hamleden sonra daha fazla girdi alacaksınız:

x y
u v
time

Nerede x, y, u, vtüm 0 tabanlı tam sayılardır. (x,y)şu anki konumunuz ve (u,v)mevcut hızınız. Bunun x+uve / veya y+vsınırların dışında olabileceğine dikkat edin.

timesaniye cinsinden zaman bütçenizden geriye kalan her şeydir. Bunu görmezden gelmekten çekinmeyin. Bu, yalnızca uygulamalarını zaman sınırına çıkarmak isteyen katılımcılar içindir.

Oyun bittiğinde (bir duvara indiğiniz, sınırların dışına çıkmış, targetdönüşleri aştığınız , zamanınız dolmuş veya hedefe ulaştığınız için), boş bir çizgi alacaksınız.

Çıktı

Her tur için stdout'a yaz :

Δu Δv

burada Δuve Δvher biri -1, 0, 1. Bu, (u,v)yeni pozisyonunuzu belirlemek için eklenecektir . Sadece netleştirmek için, talimatlar aşağıdaki gibidir.

(-1,-1) ( 0,-1) ( 1,-1)
(-1, 0) ( 0, 0) ( 1, 0)
(-1, 1) ( 0, 1) ( 1, 1)

Yukarıdaki örnek için en uygun çözüm

1 0
1 -1
1 0

Denetleyicinin kendisini stderr'e eklemediğini unutmayın , bu nedenle botunuzu geliştirirken ihtiyaç duyabileceğiniz her türlü hata ayıklama çıktısı için kullanabilirsiniz. Lütfen gönderilen kodunuzda bu tür çıktıları kaldırın / yorum yapın.

Botunuzun her bir turda cevap vermesi yarım saniye sürebilir. Daha uzun süren dönüşlerde, target/2saniye başına bir bütçe (parça başına) olacak . Bir dönüş yarım saniyeden daha uzun sürdüğünde, ek süre zaman bütçenizden düşülecektir. Zaman bütçeniz sıfıra ulaştığında, mevcut yarış iptal edilir.

Yeni: Pratik nedenlerden dolayı, bir hafıza limiti belirlemeliyim (çünkü hafıza makul parça boyutları için zamandan daha sınırlı görünüyor). Bu nedenle, yarışmacının Proses Gezgini tarafından Özel Bayt olarak ölçülen 1GB'tan fazla bellek kullandığı herhangi bir test çalışmasını iptal etmek zorunda kalacağım .

puanlama

20 parçanın bir kriter var . Her parça için:

  • Parçayı tamamlarsanız puanınız, bölünmüştarget bir hedef hücreye ulaşmak için gereken hareket sayısıdır .
  • Eğer zaman / bellek tükenirse veya hedefe ulaşmak değil önce targetdönüşler geçtikten veya herhangi bir zamanda aut / a duvarında indirseydim puanınız olduğunu 2.
  • Eğer programınız deterministik değilse, puanınız o yolda 10'un üzerinde koşunun ortalamasıdır (lütfen cevabınızı belirtiniz).

Genel puanınız, bireysel takip puanlarının toplamıdır. En düşük puan kazanır!

Ayrıca, her katılımcı resmi listeye eklenecek ek bir kıyaslama izi sağlayabilir (ve şiddetle tavsiye edilir) . Bu yeni parça dahil olmak üzere önceki cevaplar yeniden değerlendirilecektir. Bu, hiçbir çözümün mevcut test durumlarına çok yakın bir şekilde uyarlanmadığından emin olmak ve kaçırmış olabileceğim ilginç olayları hesaba katmaktır (ancak hangisini tespit edebilirsiniz).

Kravat kırma

Şimdi zaten optimal bir çözüm olduğuna göre, bu muhtemelen katılımcıların puanları için ana faktör olacaktır.

Eğer bir bağ varsa (tüm parçaları en iyi şekilde veya başka bir şekilde çözen birkaç cevap nedeniyle), bağı kırmak için ek (daha büyük) test durumları ekleyeceğim. Bu bağlayıcıları yaratırken herhangi bir insani yanlılığı önlemek için, bunlar sabit bir şekilde üretilecektir :

  • Bu şekilde üretilen son parçaya ngöre kenar uzunluğunu arttıracağım 10. (Bağları koparmazlarsa boyutları atlayabilirim.)
  • Temel bu vektör grafiktir
  • Bu, Mathematica pasajı kullanılarak istenen çözünürlükte rasterleştirilecektir .
  • Başlangıç ​​sol üst köşede. Özellikle, parçanın sonundaki en üstteki sıranın en solundaki hücre olacaktır.
  • Amaç sağ alt köşede. Özellikle, parçanın sonundaki en alt sıranın en sağındaki hücre olacaktır.
  • targetİrade 4*n.

İlk kriterin son parçası zaten böyle üretildi n = 50.

Kontrol eden, denetleyici

Gönderileri test eden program Ruby'de yazılmıştır ve kullanacağım kıyaslama dosyasıyla birlikte GitHub'da bulunabilir . Orada randomracer.rbrastgele hareketler seçen basit bir bot var. İletişimin nasıl çalıştığını görmek için botun temel yapısını başlangıç ​​noktası olarak kullanabilirsiniz.

Kendi botunuzu, seçtiğiniz bir parça dosyasına karşı aşağıdaki gibi çalıştırabilirsiniz:

ruby controller.rb track_file_name command to run your racer

Örneğin

ruby controller.rb benchmark.txt ruby randomracer.rb

Depo ayrıca iki sınıf içerir Point2Dve Track. Gönderiniz Ruby'de yazılmışsa, rahatınız için bunları kullanmaktan çekinmeyin.

Komut satırı anahtarları

Sen komut satırı anahtarları ekleyebilir -v, -s, -tBenchmark'ın dosya adından önce. Birden fazla anahtar kullanmak istiyorsanız, örneğin, bunu da yapabilirsiniz -vs. Onların yaptığı budur:

-v (ayrıntılı): Kontrol cihazından biraz daha fazla hata ayıklama çıktısı üretmek için bunu kullanın.

-s (sessiz): Konumunuzu ve hızınızı kendiniz takip etmeyi tercih ederseniz ve zaman bütçesini umursamıyorsanız, bu bayrakla her dönüş (gönderiminize gönderilen) bu üç satırlık satırı kapatabilirsiniz.

-t(tracks): Test edilecek bireysel parçaları seçmenize izin verir. Örneğin -t "1,2,5..8,15", sadece 1, 2, 5, 6, 7, 8 ve 15 numaralı parçaları test eder. Bu özellik ve seçenekler ayrıştırıcı için Ventero'ya çok teşekkürler .

Gönderiminiz

Özet olarak, lütfen cevabınıza aşağıdakileri ekleyin:

  • Puanın.
  • Eğer siz rastgelelik kullandığınız Birden ishal üzerinden puanınızı ortalamasını böylece, bu belirtiniz.
  • Gönderiminizin kodu.
  • Seçtiğiniz dil için bir Windows 8 makinesinde çalışan ücretsiz bir derleyicinin veya tercümanın konumu.
  • Gerekirse derleme talimatları.
  • Gönderinizi çalıştırmak için kullanılan bir Windows komut satırı dizesi.
  • Gönderiniz için -sbayrak gerekip gerekmediği.
  • (isteğe bağlı) Kriterlere eklenecek yeni ve çözülebilir bir iz. targetManuel olarak parçanız için makul bir karar vereceğim . Parça ölçüt eklendiğinde, cevabınızı düzeltirim. Sizden farklı bir pist sorma hakkını saklı tutuyorum (orantısız şekilde büyük bir pist eklerseniz, pistte müstehcen ASCII resimleri vb. Dahil). Test senaryosunu benchmark setine eklediğimde, bu yazıdaki karmaşayı azaltmak için cevabınızdaki parkuru benchmark dosyasındaki linkle değiştireceğim.

Gördüğünüz gibi, tüm gönderimleri bir Windows 8 makinesinde test edeceğim. Gönderinizi Windows'ta çalıştırmanın kesinlikle bir yolu yoksa, Ubuntu VM'de de deneyebilirim. Ancak bu, oldukça yavaş olacaktır; bu nedenle, zaman sınırını aşmak istiyorsanız, programınızın Windows'ta çalıştığından emin olun.

En iyi sürücü vektörleşsin!

Ama ben oynamak istiyorum!

Kendini daha iyi hissetmek için oyunu kendin denemek istersen, bu uygulama var . Orada kullanılan kurallar biraz daha sofistike, ancak yararlı olacak kadar benzer.

Liderler Sıralaması

Son güncelleme: 01.09.2014, 21:29 UTC
Kriterlerdeki parça sayısı: 25
Tie-breaker boyutları: 290, 440

  1. 6.86688 - Kuroi Neko
  2. 8.73108 - kullanıcı2357112 - 2. gönderim
  3. 9.86627 - nneonneo
  4. 10.66109 - kullanıcı2357112 - 1. gönderim
  5. 12.49643 - Ray
  6. 40.0759 - takma ad117 (olasılıksal)

Ayrıntılı test sonuçları . (Olası başvurular için puanlar ayrı ayrı belirlenmiştir.)

Yanıtlar:


5

C ++ 11 - 6.66109

Yine bir başka geniş kapsamlı ilk arama uygulaması, yalnızca optimize edildi.

-S seçeneği ile çalıştırılmalıdır .
Girişi tamamen sterilize edilmemiştir, bu nedenle yanlış izler balkabağına dönüşebilir.

Microsoft Visual C ++ 2013 ile test ettim, default / O2 bayrağıyla derlemeye başla (hız için optimize et).
G ++ ve Microsoft IDE ile Tamam derler.
Barebone hafıza ayırıcım bir bok parçası, bu yüzden diğer STL uygulamalarıyla çalışmasını beklemeyin unordered_set!

#include <cstdint>
#include <iostream>
#include <fstream>
#include <sstream>
#include <queue>
#include <unordered_set>

#define MAP_START 'S'
#define MAP_WALL  '#'
#define MAP_GOAL  '*'

#define NODE_CHUNK_SIZE   100 // increasing this will not improve performances
#define VISIT_CHUNK_SIZE 1024 // increasing this will slightly reduce mem consumption at the (slight) cost of speed

#define HASH_POS_BITS 8 // number of bits for one coordinate
#define HASH_SPD_BITS (sizeof(size_t)*8/2-HASH_POS_BITS)

typedef int32_t tCoord; // 32 bits required to overcome the 100.000 cells (insanely) long challenge

// basic vector arithmetics
struct tPoint {
    tCoord x, y;
    tPoint(tCoord x = 0, tCoord y = 0) : x(x), y(y) {}
    tPoint operator+ (const tPoint & p) { return tPoint(x + p.x, y + p.y); }
    tPoint operator- (const tPoint & p) { return tPoint(x - p.x, y - p.y); }
    bool operator== (const tPoint & p) const { return p.x == x && p.y == y;  }
};

// a barebone block allocator. Improves speed by about 30%
template <class T, size_t SIZE> class tAllocator
{
    T * chunk;
    size_t i_alloc;
    size_t m_alloc;
public:
    typedef T                 value_type;
    typedef value_type*       pointer;
    typedef const value_type* const_pointer;
    typedef std::size_t       size_type;
    typedef value_type&       reference;
    typedef const value_type& const_reference;
    tAllocator()                                              { m_alloc = i_alloc = SIZE; }
    template <class U> tAllocator(const tAllocator<U, SIZE>&) { m_alloc = i_alloc = SIZE; }
    template <class U> struct rebind { typedef tAllocator<U, SIZE> other; };
    pointer allocate(size_type n, const_pointer = 0)
    {
        if (n > m_alloc) { i_alloc = m_alloc = n; }      // grow max size if request exceeds capacity
        if ((i_alloc + n) > m_alloc) i_alloc = m_alloc;  // dump current chunk if not enough room available
        if (i_alloc == m_alloc) { chunk = new T[m_alloc]; i_alloc = 0; } // allocate new chunk when needed
        T * mem = &chunk[i_alloc];
        i_alloc += n;
        return mem;
    }
    void deallocate(pointer, size_type) { /* memory is NOT released until process exits */ }
    void construct(pointer p, const value_type& x) { new(p)value_type(x); }
    void destroy(pointer p) { p->~value_type(); }
};

// a node in our search graph
class tNode {
    static tAllocator<tNode, NODE_CHUNK_SIZE> mem; // about 10% speed gain over a basic allocation
    tNode * parent;
public:
    tPoint pos;
    tPoint speed;
    static tNode * alloc (tPoint pos, tPoint speed, tNode * parent) { return new (mem.allocate(1)) tNode(pos, speed, parent); }
    tNode (tPoint pos = tPoint(), tPoint speed = tPoint(), tNode * parent = nullptr) : parent(parent), pos(pos), speed(speed) {}
    bool operator== (const tNode& n) const { return n.pos == pos && n.speed == speed; }
    void output(void)
    {
        std::string output;
        tPoint v = this->speed;
        for (tNode * n = this->parent ; n != nullptr ; n = n->parent)
        {
            tPoint a = v - n->speed;
            v = n->speed;
            std::ostringstream ss;  // a bit of shitty c++ text I/O to print elements in reverse order
            ss << a.x << ' ' << a.y << '\n';
            output = ss.str() + output;
        }
        std::cout << output;
    }
};
tAllocator<tNode, NODE_CHUNK_SIZE> tNode::mem;

// node queueing and storing
static int num_nodes = 0;
class tNodeJanitor {
    // set of already visited nodes. Block allocator improves speed by about 20%
    struct Hasher { size_t operator() (tNode * const n) const 
    {
        int64_t hash = // efficient hashing is the key of performances
            ((int64_t)n->pos.x   << (0 * HASH_POS_BITS))
          ^ ((int64_t)n->pos.y   << (1 * HASH_POS_BITS))
          ^ ((int64_t)n->speed.x << (2 * HASH_POS_BITS + 0 * HASH_SPD_BITS))
          ^ ((int64_t)n->speed.y << (2 * HASH_POS_BITS + 1 * HASH_SPD_BITS));
        return (size_t)((hash >> 32) ^ hash);
        //return (size_t)(hash);
    }
    };
    struct Equalizer { bool operator() (tNode * const n1, tNode * const n2) const
        { return *n1 == *n2; }};
    std::unordered_set<tNode *, Hasher, Equalizer, tAllocator<tNode *, VISIT_CHUNK_SIZE>> visited;
    std::queue<tNode *> queue; // currently explored nodes queue
public:
    bool empty(void) { return queue.empty();  }
    tNode * dequeue() { tNode * n = queue.front(); queue.pop(); return n; }
    tNode * enqueue_if_new (tPoint pos, tPoint speed = tPoint(0,0), tNode * parent = nullptr)
    {
        tNode signature (pos, speed);
        tNode * n = nullptr;
        if (visited.find (&signature) == visited.end()) // the classy way to check if an element is in a set
        {
            n = tNode::alloc(pos, speed, parent);
            queue.push(n);
            visited.insert (n);
num_nodes++;
        }
        return n;
    }
};

// map representation
class tMap {
    std::vector<char> cell;
    tPoint dim; // dimensions
public:
    void set_size(tCoord x, tCoord y) { dim = tPoint(x, y); cell.resize(x*y); }
    void set(tCoord x, tCoord y, char c) { cell[y*dim.x + x] = c; }
    char get(tPoint pos)
    {
        if (pos.x < 0 || pos.x >= dim.x || pos.y < 0 || pos.y >= dim.y) return MAP_WALL;
        return cell[pos.y*dim.x + pos.x];
    }
    void dump(void)
    {
        for (int y = 0; y != dim.y; y++)
        {
            for (int x = 0; x != dim.x; x++) fprintf(stderr, "%c", cell[y*dim.x + x]);
            fprintf(stderr, "\n");
        }
    }
};

// race manager
class tRace {
    tPoint start;
    tNodeJanitor border;
    static tPoint acceleration[9];
public:
    tMap map;
    tRace ()
    {
        int target;
        tCoord sx, sy;
        std::cin >> target >> sx >> sy;
        std::cin.ignore();
        map.set_size (sx, sy);
        std::string row;
        for (int y = 0; y != sy; y++)
        {
            std::getline(std::cin, row);
            for (int x = 0; x != sx; x++)
            {
                char c = row[x];
                if (c == MAP_START) start = tPoint(x, y);
                map.set(x, y, c);
            }
        }
    }

    // all the C++ crap above makes for a nice and compact solver
    tNode * solve(void)
    {
        tNode * initial = border.enqueue_if_new (start);
        while (!border.empty())
        {
            tNode * node = border.dequeue();
            tPoint p = node->pos;
            tPoint v = node->speed;
            for (tPoint a : acceleration)
            {
                tPoint nv = v + a;
                tPoint np = p + nv;
                char c = map.get(np);
                if (c == MAP_WALL) continue;
                if (c == MAP_GOAL) return new tNode (np, nv, node);
                border.enqueue_if_new (np, nv, node);
            }
        }
        return initial; // no solution found, will output nothing
    }
};
tPoint tRace::acceleration[] = {
    tPoint(-1,-1), tPoint(-1, 0), tPoint(-1, 1),
    tPoint( 0,-1), tPoint( 0, 0), tPoint( 0, 1),
    tPoint( 1,-1), tPoint( 1, 0), tPoint( 1, 1)};

#include <ctime>
int main(void)
{
    tRace race;
    clock_t start = clock();
    tNode * solution = race.solve();
    std::cerr << "time: " << (clock()-start)/(CLOCKS_PER_SEC/1000) << "ms nodes: " << num_nodes << std::endl;
    solution->output();
    return 0;
}

Sonuçlar

 No.       Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1       37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2       38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3       33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4       10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5        9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6       15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7       17 x 8        16   0.31250   Racer reached goal at ( 14, 0) in 5 turns.
  8       19 x 13       18   0.27778   Racer reached goal at ( 0, 11) in 5 turns.
  9       60 x 10      107   0.14953   Racer reached goal at ( 0, 6) in 16 turns.
 10       31 x 31      106   0.23585   Racer reached goal at ( 27, 0) in 25 turns.
 11       31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12       50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13      100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14       79 x 63      242   0.24380   Racer reached goal at ( 3, 42) in 59 turns.
 15       26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16       17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17       50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18       10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19       55 x 55       45   0.17778   Racer reached goal at ( 50, 26) in 8 turns.
 20      101 x 100     100   0.14000   Racer reached goal at ( 99, 99) in 14 turns.
 21   100000 x 1         1   1.00000   Racer reached goal at ( 0, 0) in 1 turns.
 22       50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
 23      290 x 290    1160   0.16466   Racer reached goal at ( 269, 265) in 191 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:                 6.66109

performanslar

Bu berbat C ++ dili, sadece bir kibrit çöpü taşımak için çemberin içinden atlamak için bir püf noktası vardır. Bununla birlikte, nispeten hızlı ve hafıza açısından verimli bir kod üretmeye teşvik edebilirsiniz.

Karma

Burada anahtar, düğümler için iyi bir karma tablo sağlamaktır. Şimdiye kadar yürütme hızı için baskın faktör.
İki uygulama unordered_set(GNU ve Microsoft)% 30 uygulama hızı farkı (GNU lehine, yay!) Verdi.

Aradaki fark gerçekten şaşırtıcı değil, kamyon yükü kodunun arkasında gizli unordered_set.

Meraktan, karma tablonun son durumu hakkında bazı istatistikler yaptım.
Her iki algoritma da neredeyse aynı kepçe / eleman oranına sahip olmakla birlikte, bölme değişmektedir:
290x290 bağ kırıcı için GNU, boş olmayan kepçe başına ortalama 1,5 eleman alırken, Microsoft 5,8 (!).

Karma işlevim Microsoft tarafından çok iyi rasgele değil gibi görünüyor ... Acaba Redmond’daki çocuklar STL’lerini gerçekten kıyaslıyor mu, yoksa belki de kullanım durumum GNU’nun uygulanmasını destekliyor mu ...

Elbette, sağlama fonksiyonum hiçbir yerde optimal olamaz. Her zamanki tamsayı karışımını çoklu kayma / katırlara dayalı olarak kullanabilirdim, ancak karma etkin bir fonksiyonun hesaplanması zaman alıyor.

Ekleme sayısına göre karma tablo sorgularının sayısı çok yüksektir. Örneğin, 290x290 bağlantı koparıcıda, 22.7 milyon sorgu için yaklaşık 3.6 milyon ek var.
Bu bağlamda, suboptimal ama hızlı karma, daha iyi performans sağlar.

Bellek ayırma

Verimli bir hafıza tahsis etmek ikincisidir. Performansları yaklaşık% 30 artırdı. Eklenen boktan kodun değip değmeyeceği tartışmalıdır :).

Geçerli sürüm, düğüm başına 40 ila 55 bayt kullanır.
İşlevsel veriler bir düğüm için 24 bayt gerektirir (4 koordinat ve 2 işaretçi).
Çılgın 100.000 satırlık test durumundan dolayı, koordinatların 4 bayt kelimeyle kaydedilmesi gerekir, aksi takdirde şort kullanarak 8 bayt kazanabilirsiniz (maksimum 32767 koordinat değeriyle). Kalan baytlar çoğunlukla sırasız kümenin karma tablosu tarafından tüketilir. Bu, veri işlemenin aslında "kullanışlı" veri yükünden biraz daha fazla tükettiği anlamına gelir.

Ve kazanan...

Win7 altındaki bilgisayarımda, bağ kırıcı (vaka 23, 290x290) en kötü sürüm (yani Microsoft tarafından derlenmiş) tarafından yaklaşık 2,2 saniyede, yaklaşık 185 Mb bellek tüketimi ile çözüldü.
Karşılaştırma için, mevcut lider (user2357112 tarafından python kodu) 30 saniyeden biraz daha uzun sürüyor ve yaklaşık 780 Mb tüketiyor.

Denetleyici sorunları

Hayatımı kurtarmak için Ruby'de kod yazabileceğime emin değilim.
Ancak, denetleyici kodunun dışında iki sorun tespit ettim ve hackledim:

1) harita okuma track.rb

Yakut 1.9.3 kuruluysa, iz okuyucu shift.to_iiçin uygun olmadığı hakkında titremesi gerekirdi string.lines.
Çevrimiçi Ruby belgelerine uzun bir süreden sonra dizgilerden vazgeçtim ve bunun yerine ara bir dizi kullandım (dosyanın başında):

def initialize(string)
    @track = Array.new;
    string.lines.each do |line|
        @track.push (line.chomp)
    end

2) hayaletleri öldürmek controller.rb

Diğer posterlerin daha önce belirttiği gibi, kontrolör bazen daha önce çıkmış olan süreçleri öldürmeye çalışır. Bu utanç verici hata çıktılarından kaçınmak için, istisnayı basitçe uzaklaştırdım (satır 134 civarında):

if silent
    begin # ;!;
        Process.kill('KILL', racer.pid)
    rescue Exception => e
    end

Test durumu

BFS çözücülerinin kaba kuvvet yaklaşımını yenmek için, en kötü iz 100.000 hücre haritasının tam tersi: hedefin mümkün olduğu kadar uzağında tamamen serbest bir alan.

Bu örnekte, sol üst köşedeki hedefi ve sağ alt köşeden başlayan bir 100x400 haritası.

Bu haritanın 28 turda bir çözümü var, ancak bir BFS çözücü onu bulmak için milyonlarca eyaleti keşfedecek (maden 10.022.658 eyaleti sayıldı, 12 saniye sürdü ve 600 Mb'de zirveye çıktı!).

290x290 bağlantı kesicinin yüzeyinin yarısından daha azıyla, yaklaşık 3 kat daha fazla düğüm ziyareti gerektirir. Öte yandan, sezgisel / A * tabanlı bir çözücü onu kolayca yenmeli.

30
100 400
*...................................................................................................
....................................................................................................
                          < 400 lines in all >
....................................................................................................
....................................................................................................
...................................................................................................S

Bonus: bir eşdeğer (ama biraz daha az verimli) PHP sürümü

İçsel dil yavaşlığı beni C ++ kullanmaya ikna etmeden önce başladım.
PHP iç hash tabloları Python'ınki kadar verimli görünmüyor, en azından bu özel durumda :).

<?php

class Trace {
    static $file;
    public static $state_member;
    public static $state_target;
    static function msg ($msg)
    {
        fputs (self::$file, "$msg\n");
    }

    static function dump ($var, $msg=null)
    {
        ob_start();
        if ($msg) echo "$msg ";
        var_dump($var);
        $dump=ob_get_contents();
        ob_end_clean();
        fputs (self::$file, "$dump\n");
    }

    function init ($fname)
    {
        self::$file = fopen ($fname, "w");
    }
}
Trace::init ("racer.txt");

class Point {
    public $x;
    public $y;

    function __construct ($x=0, $y=0)
    {
        $this->x = (float)$x;
        $this->y = (float)$y;
    }

    function __toString ()
    {
        return "[$this->x $this->y]";
    }

    function add ($v)
    {
        return new Point ($this->x + $v->x, $this->y + $v->y);
    }

    function vector_to ($goal)
    {
        return new Point ($goal->x - $this->x, $goal->y - $this->y);
    }
}

class Node {
    public $posx  , $posy  ;
    public $speedx, $speedy;
    private $parent;

    public function __construct ($posx, $posy, $speedx, $speedy, $parent)
    {
        $this->posx = $posx;
        $this->posy = $posy;
        $this->speedx = $speedx;
        $this->speedy = $speedy;
        $this->parent = $parent;
    }

    public function path ()
    {
        $res = array();
        $v = new Point ($this->speedx, $this->speedy);
        for ($node = $this->parent ; $node != null ; $node = $node->parent)
        {
            $nv = new Point ($node->speedx, $node->speedy);
            $a = $nv->vector_to ($v);
            $v = new Point ($node->speedx, $node->speedy);
            array_unshift ($res, $a);
        }
        return $res;
    }
}

class Map {

    private static $target;       // maximal number of turns
    private static $time;         // time available to solve
    private static $sx, $sy;      // map dimensions
    private static $cell;         // cells of the map
    private static $start;        // starting point
    private static $acceleration; // possible acceleration values

    public static function init ()
    {
        // read map definition
        self::$target = trim(fgets(STDIN));
        list (self::$sx, self::$sy) = explode (" ", trim(fgets(STDIN)));
        self::$cell = array();
        for ($y = 0 ; $y != self::$sy ; $y++) self::$cell[] = str_split (trim(fgets(STDIN)));
        self::$time = trim(fgets(STDIN));

        // get starting point
        foreach (self::$cell as $y=>$row)
        {
            $x = array_search ("S", $row);
            if ($x !== false)
            {
                self::$start = new Point ($x, $y);
Trace::msg ("start ".self::$start);
                break;
            }
        }

        // compute possible acceleration values
        self::$acceleration = array();
        for ($x = -1 ; $x <= 1 ; $x++)
        for ($y = -1 ; $y <= 1 ; $y++)
        {
            self::$acceleration[] = new Point ($x, $y);
        }
    }

    public static function solve ()
    {
        $now = microtime(true);
        $res = array();
        $border = array (new Node (self::$start->x, self::$start->y, 0, 0, null));
        $present = array (self::$start->x." ".self::$start->y." 0 0" => 1);
        while (count ($border))
        {
if ((microtime(true) - $now) > 1)
{
Trace::msg (count($present)." nodes, ".round(memory_get_usage(true)/1024)."K");
$now = microtime(true);
}
            $node = array_shift ($border);
//Trace::msg ("node $node->pos $node->speed");
            $px = $node->posx;
            $py = $node->posy;
            $vx = $node->speedx;
            $vy = $node->speedy;
            foreach (self::$acceleration as $a)
            {
                $nvx = $vx + $a->x;
                $nvy = $vy + $a->y;
                $npx = $px + $nvx;
                $npy = $py + $nvy;
                if ($npx < 0 || $npx >= self::$sx || $npy < 0 || $npy >= self::$sy || self::$cell[$npy][$npx] == "#")
                {
//Trace::msg ("invalid position $px,$py $vx,$vy -> $npx,$npy");
                    continue;
                }
                if (self::$cell[$npy][$npx] == "*")
                {
Trace::msg ("winning position $px,$py $vx,$vy -> $npx,$npy");
                    $end = new Node ($npx, $npy, $nvx, $nvy, $node);
                    $res = $end->path ();
                    break 2;
                }
//Trace::msg ("checking $np $nv");
                $signature = "$npx $npy $nvx $nvy";
                if (isset ($present[$signature])) continue;
//Trace::msg ("*** adding $np $nv");
                $border[] = new Node ($npx, $npy, $nvx, $nvy, $node);
                $present[$signature] = 1;
            }
        }
        return $res;
    }
}

ini_set("memory_limit","1000M");
Map::init ();
$res = Map::solve();
//Trace::dump ($res);
foreach ($res as $a) echo "$a->x $a->y\n";
?>

erf ... Benim barebone tahsisçim sadece biraz fazla barebone. O zaman g ++ ile çalışması için gerekli pisliği ekleyeceğim. Bunun için üzgünüm.

Tamam, düzeltildi. G ++ sürümü aslında% 30 daha hızlı çalışıyor. Şimdi stderr'de bazı istatistikler çıkarır. Yorum yapmaktan çekinmeyin (kaynağın son satırlarından). Blunder için tekrar özür dilerim.

Tamam, şimdi çalışıyor ve puanınızı çoğalttım. Çok hızlı! :) Test vakanızı kıyas ölçütüne ekleyeceğim, ancak hedefi 400diğer tüm hedefleri nasıl belirlediğime paralel olarak değiştireceğim (kravat kırıcı hariç). Diğer tüm gönderileri yeniden kaydettikten sonra ana yazıyı güncelleyeceğim.
Martin Ender

Sonuçlar güncellendi. Bağlayıcıya gerek yoktu, çünkü diğer tüm başvurular test parçanızdaki hafıza sınırını aşıyordu. Bunun için tebrikler! :)
Martin Ender

Teşekkürler. Aslında bu zorluk bana bu STL karma tablolarına girmem için bir fırsat verdi. C ++ bağırsaklarından nefret etmeme rağmen, yardımcı olamıyorum ama merakım yüzünden öldürülüyor. Miyav! :).

10

C ++, 5.4 (deterministik, optimal)

Dinamik programlama çözümü. Muhtemelen optimal. Çok hızlı: 20 testisin hepsini 0.2s'de çözer. 64 bit makinelerde özellikle hızlı olmalı. Kurulun her yöne 32.000'den az yer olduğunu varsayalım (umarım doğru olmalıdır).

Bu yarışçı biraz sıra dışı. Başlangıç ​​çizgisindeki en uygun yolu hesaplar ve ardından hesaplanan yolu anında yürütür. Zaman kontrolünü göz ardı eder ve optimizasyon aşamasını zamanında bitirebileceğini varsayar (makul herhangi bir modern donanım için doğru olması gerekir). Aşırı derecede büyük haritalarda, yarışçının farklı bölümlere ayrılması olasılığı çok düşük. Segfault'a ikna edebiliyorsanız, bir kek puanı elde edersiniz ve açık bir döngü kullanması için onu düzelteceğim.

İle derleyin g++ -O3. C ++ 11 gerektirebilir (için <unordered_map>). Çalıştırmak için derlenmiş çalıştırılabilir dosyayı çalıştırın (bayrak veya seçenek desteklenmez; tüm girdiler stdin üzerinden alınır).

#include <unordered_map>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

#include <cstdint>

#define MOVES_INF (1<<30)

union state {
    struct {
        short px, py, vx, vy;
    };
    uint64_t val;
};

struct result {
    int nmoves;
    short dvx, dvy;
};

typedef std::unordered_map<uint64_t, result> cache_t;
int target, n, m;
std::vector<std::string> track;
cache_t cache;

static int solve(uint64_t val) {
    cache_t::iterator it = cache.find(val);
    if(it != cache.end())
        return it->second.nmoves;

    // prevent recursion
    result res;
    res.nmoves = MOVES_INF;
    cache[val] = res;

    state cur;
    cur.val = val;
    for(int dvx = -1; dvx <= 1; dvx++) for(int dvy = -1; dvy <= 1; dvy++) {
        state next;
        next.vx = cur.vx + dvx;
        next.vy = cur.vy + dvy;
        next.px = cur.px + next.vx;
        next.py = cur.py + next.vy;
        if(next.px < 0 || next.px >= n || next.py < 0 || next.py >= m)
            continue;
        char c = track[next.py][next.px];
        if(c == '*') {
            res.nmoves = 1;
            res.dvx = dvx;
            res.dvy = dvy;
            break;
        } else if(c == '#') {
            continue;
        } else {
            int score = solve(next.val) + 1;
            if(score < res.nmoves) {
                res.nmoves = score;
                res.dvx = dvx;
                res.dvy = dvy;
            }
        }
    }

    cache[val] = res;
    return res.nmoves;
}

bool solve_one() {
    std::string line;
    float time;

    std::cin >> target;
    // std::cin >> time; // uncomment to use "time" control
    std::cin >> n >> m;
    if(!std::cin)
        return false;
    std::cin.ignore(); // skip newline at end of "n m" line

    track.clear();
    track.reserve(m);

    for(int i=0; i<m; i++) {
        std::getline(std::cin, line);
        track.push_back(line);
    }

    cache.clear();

    state cur;
    cur.vx = cur.vy = 0;
    for(int y=0; y<m; y++) for(int x=0; x<n; x++) {
        if(track[y][x] == 'S') {
            cur.px = x;
            cur.py = y;
            break;
        }
    }

    solve(cur.val);

    int sol_len = 0;
    while(track[cur.py][cur.px] != '*') {
        cache_t::iterator it = cache.find(cur.val);
        if(it == cache.end() || it->second.nmoves >= MOVES_INF) {
            std::cerr << "Failed to solve at p=" << cur.px << "," << cur.py << " v=" << cur.vx << "," << cur.vy << std::endl;
            return true;
        }

        int dvx = it->second.dvx;
        int dvy = it->second.dvy;
        cur.vx += dvx;
        cur.vy += dvy;
        cur.px += cur.vx;
        cur.py += cur.vy;
        std::cout << dvx << " " << dvy << std::endl;
        sol_len++;
    }

    //std::cerr << "Score: " << ((float)sol_len) / target << std::endl;

    return true;
}

int main() {
    /* benchmarking: */
    //while(solve_one())
    //    ;

    /* regular running */
    solve_one();
    std::string line;
    while(std::cin) std::getline(std::cin, line);

    return 0;
}

Sonuçlar

 No.    Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1    37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2    38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3    33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4    10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5     9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6    15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7    17 x 8        16   0.31250   Racer reached goal at ( 15, 0) in 5 turns.
  8    19 x 13       18   0.27778   Racer reached goal at ( 1, 11) in 5 turns.
  9    60 x 10      107   0.14953   Racer reached goal at ( 2, 6) in 16 turns.
 10    31 x 31      106   0.25472   Racer reached goal at ( 28, 0) in 27 turns.
 11    31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12    50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13   100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14    79 x 63      242   0.26860   Racer reached goal at ( 3, 42) in 65 turns.
 15    26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16    17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17    50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18    10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19    55 x 55       45   0.17778   Racer reached goal at ( 52, 26) in 8 turns.
 20    50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:              5.40009

Yeni Test Çantası


1
Bunun gibi bir şey bekleniyordu. Bulmacada dinamik programlamayı olanaksız kılmak için yeterli durum yok. Eğer girersem, çözmek için daha karmaşık arama stratejileri gerektiren bir harita göndermem gerekecek.
user2357112

Yarışçı test sınavında nasıl performans gösteriyor?
user2357112

0.14 (14 hamle)
nneonneo

Bu zaman harcanıyor mu, yoksa hamle mi / hedef mi? Hareket ederse / hedef ise, zaman açısından nasıl performans gösterir?
user2357112

1
Sanırım döngü önleme kodunda bir hata buldum. Bu bir devlet S'den arama ulaştığında, optimal yolu S. dönmek için olamaz her eyalet için optimal bir yol eğer o görünebilir varsayar gelmez , o zaman devlet optimal yolunda S dönüş olamaz biz olabilir çünkü ( sadece açık olan döngüyü kaldırın ve daha kısa bir yol elde edin) ve bu nedenle bu durum için çok yüksek bir sonuç almamızın önemi yok. Bununla birlikte, optimal bir yol bu sırada A ve B durumlarından geçerse, ancak B hala istifte iken arama önce A'yı bulur, sonra A sonuçları döngü önleme tarafından zehirlenir.
user2357112

6

Python 2 , deterministik, optimal

İşte yarışçım. Ben testte test etmedim (Ruby'nin hangi versiyonunun ve kurulumunun kurulacağı konusunda hala göze çarpıyor), fakat her şeyi optimal ve zaman sınırı altında çözmeli. Çalıştırılacak komut python whateveryoucallthefile.py. İhtiyaç -skontrolör bayrağı.

# Breadth-first search.
# Future directions: bidirectional search and/or A*.

import operator
import time

acceleration_options = [(dvx, dvy) for dvx in [-1, 0, 1] for dvy in [-1, 0, 1]]

class ImpossibleRaceError(Exception): pass

def read_input(line_source=raw_input):
    # We don't use the target.
    target = int(line_source())

    width, height = map(int, line_source().split())
    input_grid = [line_source() for _ in xrange(height)]

    start = None
    for i in xrange(height):
        for j in xrange(width):
            if input_grid[i][j] == 'S':
                start = i, j
                break
        if start is not None:
            break

    walls = [[cell == '#' for cell in row] for row in input_grid]
    goals = [[cell == '*' for cell in row] for row in input_grid]

    return start, walls, goals

def default_bfs_stop_threshold(walls, goals):
    num_not_wall = sum(sum(map(operator.not_, row)) for row in walls)
    num_goals = sum(sum(row) for row in goals)
    return num_goals * num_not_wall

def bfs(start, walls, goals, stop_threshold=None):
    if stop_threshold is None:
        stop_threshold = default_bfs_stop_threshold(walls, goals)

    # State representation is (x, y, vx, vy)
    x, y = start
    initial_state = (x, y, 0, 0)
    frontier = {initial_state}
    # Visited set is tracked by a map from each state to the last move taken
    # before reaching that state.
    visited = {initial_state: None}

    while len(frontier) < stop_threshold:
        if not frontier:
            raise ImpossibleRaceError

        new_frontier = set()
        for x, y, vx, vy in frontier:
            for dvx, dvy in acceleration_options:
                new_vx, new_vy = vx+dvx, vy+dvy
                new_x, new_y = x+new_vx, y+new_vy
                new_state = (new_x, new_y, new_vx, new_vy)

                if not (0 <= new_x < len(walls) and 0 <= new_y < len(walls[0])):
                    continue
                if walls[new_x][new_y]:
                    continue
                if new_state in visited:
                    continue

                new_frontier.add(new_state)
                visited[new_state] = dvx, dvy

                if goals[new_x][new_y]:
                    return construct_path_from_bfs(new_state, visited)
        frontier = new_frontier

def construct_path_from_bfs(goal_state, best_moves):
    reversed_path = []
    current_state = goal_state
    while best_moves[current_state] is not None:
        move = best_moves[current_state]
        reversed_path.append(move)

        x, y, vx, vy = current_state
        dvx, dvy = move
        old_x, old_y = x-vx, y-vy # not old_vx or old_vy
        old_vx, old_vy = vx-dvx, vy-dvy
        current_state = (old_x, old_y, old_vx, old_vy)
    return reversed_path[::-1]

def main():
    t = time.time()

    start, walls, goals = read_input()
    path = bfs(start, walls, goals, float('inf'))
    for dvx, dvy in path:
        # I wrote the whole program with x pointing down and y pointing right.
        # Whoops. Gotta flip things for the output.
        print dvy, dvx

if __name__ == '__main__':
    main()

Nneonneo'nun yarışmacısını inceledikten sonra (ama aslında test etmedim, çünkü aynı zamanda bir C ++ derleyicisine sahip değildim), hedefin ne kadar yakın ya da ne kadar kısa olursa olsun durum alanını neredeyse ayrıntılı bir şekilde araştırdığını göründüğünü gördüm. bir yol zaten bulundu. Ayrıca, zaman kurallarının uzun, karmaşık bir çözüme sahip bir harita inşa etmek için uzun ve sıkıcı bir zaman sınırı gerektirdiği anlamına geldiğini de buldum. Böylece harita gönderim oldukça basittir:

Yeni Test Çantası

(GitHub uzun çizgi gösteremez. Pist *S.......[and so on].....)


Ek gönderi: Python 2, çift yönlü arama

Bu, en fazla ilk gönderimi optimize etmeye çalışırken, iki ay önce yazdığım bir yaklaşım. O sırada var olan test vakaları için bir iyileştirme sunmadım, bu yüzden göndermedim, ama kuroi'nin yeni haritası için bellek başlığı altında zorlukla sıkışmış gibi görünüyor. Hala Kuroi'nin çözücüsünün bunu yenmesini bekliyorum, ama nasıl devam edeceği ile ilgileniyorum.

# Bidirectional search.
# Future directions: A*.

import operator
import time

acceleration_options = [(dvx, dvy) for dvx in [-1, 0, 1] for dvy in [-1, 0, 1]]

class ImpossibleRaceError(Exception): pass

def read_input(line_source=raw_input):
    # We don't use the target.
    target = int(line_source())

    width, height = map(int, line_source().split())
    input_grid = [line_source() for _ in xrange(height)]

    start = None
    for i in xrange(height):
        for j in xrange(width):
            if input_grid[i][j] == 'S':
                start = i, j
                break
        if start is not None:
            break

    walls = [[cell == '#' for cell in row] for row in input_grid]
    goals = [[cell == '*' for cell in row] for row in input_grid]

    return start, walls, goals

def bfs_to_bidi_threshold(walls, goals):
    num_not_wall = sum(sum(map(operator.not_, row)) for row in walls)
    num_goals = sum(sum(row) for row in goals)
    return num_goals * (num_not_wall - num_goals)

class GridBasedGoalContainer(object):
    '''Supports testing whether a state is a goal state with `in`.

    Does not perform bounds checking.'''
    def __init__(self, goal_grid):
        self.goal_grid = goal_grid
    def __contains__(self, state):
        x, y, vx, vy = state
        return self.goal_grid[x][y]

def forward_step(state, acceleration):
    x, y, vx, vy = state
    dvx, dvy = acceleration

    new_vx, new_vy = vx+dvx, vy+dvy
    new_x, new_y = x+new_vx, y+new_vy

    return (new_x, new_y, new_vx, new_vy)

def backward_step(state, acceleration):
    x, y, vx, vy = state
    dvx, dvy = acceleration

    old_x, old_y = x-vx, y-vy
    old_vx, old_vy = vx-dvx, vy-dvy

    return (old_x, old_y, old_vx, old_vy)

def bfs(start, walls, goals):
    x, y = start
    initial_state = (x, y, 0, 0)
    initial_frontier = {initial_state}
    visited = {initial_state: None}

    goal_state, frontier, visited = general_bfs(
        frontier=initial_frontier,
        visited=visited,
        walls=walls,
        goalcontainer=GridBasedGoalContainer(goals),
        stop_threshold=float('inf'),
        step_function=forward_step
    )

    return construct_path_from_bfs(goal_state, visited)

def general_bfs(
        frontier,
        visited,
        walls,
        goalcontainer,
        stop_threshold,
        step_function):

    while len(frontier) <= stop_threshold:
        if not frontier:
            raise ImpossibleRaceError

        new_frontier = set()
        for state in frontier:
            for accel in acceleration_options:
                new_state = new_x, new_y, new_vx, new_vy = \
                        step_function(state, accel)

                if not (0 <= new_x < len(walls) and 0 <= new_y < len(walls[0])):
                    continue
                if walls[new_x][new_y]:
                    continue
                if new_state in visited:
                    continue

                new_frontier.add(new_state)
                visited[new_state] = accel

                if new_state in goalcontainer:
                    return new_state, frontier, visited
        frontier = new_frontier
    return None, frontier, visited

def max_velocity_component(n):
    # It takes a distance of at least 0.5*v*(v+1) to achieve a velocity of
    # v in the x or y direction. That means the map has to be at least
    # 1 + 0.5*v*(v+1) rows or columns long to accomodate such a velocity.
    # Solving for v, we get a velocity cap as follows.
    return int((2*n-1.75)**0.5 - 0.5)

def solver(
        start,
        walls,
        goals,
        mode='bidi'):

    x, y = start
    initial_state = (x, y, 0, 0)
    initial_frontier = {initial_state}
    visited = {initial_state: None}
    if mode == 'bidi':
        stop_threshold = bfs_to_bidi_threshold(walls, goals)
    elif mode == 'bfs':
        stop_threshold = float('inf')
    else:
        raise ValueError('Unsupported search mode: {}'.format(mode))

    goal_state, frontier, visited = general_bfs(
        frontier=initial_frontier,
        visited=visited,
        walls=walls,
        goalcontainer=GridBasedGoalContainer(goals),
        stop_threshold=stop_threshold,
        step_function=forward_step
    )

    if goal_state is not None:
        return construct_path_from_bfs(goal_state, visited)

    # Switching to bidirectional search.

    not_walls_or_goals = []
    goal_list = []
    for x in xrange(len(walls)):
        for y in xrange(len(walls[0])):
            if not walls[x][y] and not goals[x][y]:
                not_walls_or_goals.append((x, y))
            if goals[x][y]:
                goal_list.append((x, y))
    max_vx = max_velocity_component(len(walls))
    max_vy = max_velocity_component(len(walls[0]))
    reverse_visited = {(goal_x, goal_y, goal_x-prev_x, goal_y-prev_y): None
                        for goal_x, goal_y in goal_list
                        for prev_x, prev_y in not_walls_or_goals
                        if abs(goal_x-prev_x) <= max_vx
                        and abs(goal_y - prev_y) <= max_vy}
    reverse_frontier = set(reverse_visited)
    while goal_state is None:
        goal_state, reverse_frontier, reverse_visited = general_bfs(
            frontier=reverse_frontier,
            visited=reverse_visited,
            walls=walls,
            goalcontainer=frontier,
            stop_threshold=len(frontier),
            step_function=backward_step
        )
        if goal_state is not None:
            break
        goal_state, frontier, visited = general_bfs(
            frontier=frontier,
            visited=visited,
            walls=walls,
            goalcontainer=reverse_frontier,
            stop_threshold=len(reverse_frontier),
            step_function=forward_step
        )
    forward_path = construct_path_from_bfs(goal_state, visited)
    backward_path = construct_path_from_bfs(goal_state,
                                            reverse_visited,
                                            step_function=forward_step)
    return forward_path + backward_path[::-1]

def construct_path_from_bfs(goal_state,
                            best_moves,
                            step_function=backward_step):
    reversed_path = []
    current_state = goal_state
    while best_moves[current_state] is not None:
        move = best_moves[current_state]
        reversed_path.append(move)
        current_state = step_function(current_state, move)
    return reversed_path[::-1]

def main():
    start, walls, goals = read_input()
    t = time.time()
    path = solver(start, walls, goals)
    for dvx, dvy in path:
        # I wrote the whole program with x pointing down and y pointing right.
        # Whoops. Gotta flip things for the output.
        print dvy, dvx

if __name__ == '__main__':
    main()

Bu, bazen 12. ve 13. davada başarısız olur. Neden hata mesajları biraz olduğundan beri bilmiyorum ... düşmanca
Ray

@Ray Hata mesajları da alıyorum, ancak yine de bu sonuçların sonucunu alıyorum. Sanırım denetleyicimde bir şey olabilir, çünkü denetleyici yarışçı işlemini öldürmeye çalışıyor gibi görünmekle birlikte zaten bitmiştir.
Martin Ender

@ m.buettner Sebebini buldum, add -s sonra Tamam olacak.
Ray

@Ray Oh evet, bunu yapıyorum. Sonuç zaten orada olmasına rağmen denetleyici işlemi öldürmeye çalışırken hala 13 ve 14 numaralı parçalarda hata alıyorum. Sanırım buna bakmalıyım, ama skoru etkilemiyor, bu yüzden henüz rahatsız etmedim
Martin Ender

Ne yazık ki, başka bir kural daha eklemek zorunda kaldım. Hafıza bu zorlukta zamandan daha sınırlı görünüyor, bu yüzden hafıza tüketimini sınırlamak için zorlanmak zorunda kaldım. Yarışçınızın 1GB'tan fazla bellek kullandığı herhangi bir işlem, zaman sınırını aşan aynı etkiye düşürülür. Mevcut parça grubunda, puanınız bu değişiklikten etkilenmedi. (Etraftaki bağ kırıcılarda bu sınıra ulaşacağınızı düşünüyorum n = 400.) Herhangi bir optimizasyon uygularsanız lütfen bana bildirin, böylece testleri tekrar başlatabilirim.
Martin Ender

3

Python 3: 6.49643 (En İyi, BFS)

Eski 20 dava referans dosyası için 5.35643 puan aldı. @Nneonneo'nun çözümü 5.4 olduğundan en uygun değil. Bazı hatalar olabilir.

Bu çözüm, Grafiği aramak için BFS'yi kullanır, her arama durumu (x, y, dx, dy) şeklindedir. Sonra durumları mesafelere eşlemek için bir harita kullanıyorum. En kötü durumda, zaman ve mekan karmaşıklığı O (n ^ 2 m ^ 2) 'dir. Bu nadiren gerçekleşecek çünkü hız çok yüksek olmayacak veya yarışçı çarpacak. Aslında, tüm 22 test aşamasını tamamlamak için makinemde 3 saniyeye mal oldu.

from collections import namedtuple, deque
import itertools

Field = namedtuple('Map', 'n m grids')

class Grid:
    WALL = '#'
    EMPTY = '.'
    START = 'S'
    END = '*'

def read_nums():
    return list(map(int, input().split()))

def read_field():
    m, n = read_nums()
    return Field(n, m, [input() for i in range(n)])

def find_start_pos(field):
    return next((i, j)
        for i in range(field.n) for j in range(field.m)
        if field.grids[i][j] == Grid.START)

def can_go(field, i, j):
    return 0 <= i < field.n and 0 <= j < field.m and field.grids[i][j] != Grid.WALL

def trace_path(start, end, prev):
    if end == start:
        return
    end, step = prev[end]
    yield from trace_path(start, end, prev)
    yield step

def solve(max_turns, field, time):
    i0, j0 = find_start_pos(field)
    p0 = i0, j0, 0, 0
    prev = {}
    que = deque([p0])
    directions = list(itertools.product((-1, 0, 1), (-1, 0, 1)))

    while que:
        p = i, j, vi, vj = que.popleft()
        for dvi, dvj in directions:
            vi1, vj1 = vi + dvi, vj + dvj
            i1, j1 = i + vi1, j + vj1
            if not can_go(field, i1, j1):
                continue
            p1 = i1, j1, vi1, vj1
            if p1 in prev:
                continue
            que.append(p1)
            prev[p1] = p, (dvi, dvj)
            if field.grids[i1][j1] == Grid.END:
                return trace_path(p0, p1, prev)
    return []

def main():
    for dvy, dvx in solve(int(input()), read_field(), float(input())):
        print(dvx, dvy)

main()

# Sonuçlar

± % time ruby controller.rb benchmark.txt python ../mybfs.py                                                                                                                                                                             !9349
["benchmark.txt", "python", "../mybfs.py"]

Running 'python ../mybfs.py' against benchmark.txt

 No.       Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1       37 x 1        36   0.22222   Racer reached goal at ( 36, 0) in 8 turns.
  2       38 x 1        37   0.24324   Racer reached goal at ( 37, 0) in 9 turns.
  3       33 x 1        32   0.25000   Racer reached goal at ( 32, 0) in 8 turns.
  4       10 x 10       10   0.40000   Racer reached goal at ( 7, 7) in 4 turns.
  5        9 x 6         8   0.37500   Racer reached goal at ( 6, 0) in 3 turns.
  6       15 x 7        16   0.37500   Racer reached goal at ( 12, 4) in 6 turns.
  7       17 x 8        16   0.31250   Racer reached goal at ( 14, 0) in 5 turns.
  8       19 x 13       18   0.27778   Racer reached goal at ( 0, 11) in 5 turns.
  9       60 x 10      107   0.14953   Racer reached goal at ( 0, 6) in 16 turns.
 10       31 x 31      106   0.23585   Racer reached goal at ( 27, 0) in 25 turns.
 11       31 x 31      106   0.24528   Racer reached goal at ( 15, 15) in 26 turns.
 12       50 x 20       50   0.24000   Racer reached goal at ( 49, 10) in 12 turns.
 13      100 x 100    2600   0.01385   Racer reached goal at ( 50, 0) in 36 turns.
 14       79 x 63      242   0.24380   Racer reached goal at ( 3, 42) in 59 turns.
 15       26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16       17 x 1        19   0.52632   Racer reached goal at ( 16, 0) in 10 turns.
 17       50 x 1        55   0.34545   Racer reached goal at ( 23, 0) in 19 turns.
 18       10 x 7        23   0.34783   Racer reached goal at ( 1, 3) in 8 turns.
 19       55 x 55       45   0.17778   Racer reached goal at ( 50, 26) in 8 turns.
 20      101 x 100     100   0.14000   Racer reached goal at ( 99, 99) in 14 turns.
 21   100000 x 1         1   1.00000   Racer reached goal at ( 0, 0) in 1 turns.
 22       50 x 50      200   0.05500   Racer reached goal at ( 47, 46) in 11 turns.
-------------------------------------------------------------------------------------
TOTAL SCORE:                 6.49643

ruby controller.rb benchmark.txt python ../mybfs.py  3.06s user 0.06s system 99% cpu 3.146 total

Evet, user2357112 adlı kullanıcının yorumuna göre, nneonneo'nun döngüsü önlenmesinde bir hata var. Gibi bildiğim kadarıyla, hız sınırlıdır O(√n)uygulamanızı bulunması şeklinde O(n³)kare izgaralarinda (aynı diğerleri gibi, sanırım). Gönderinizi, bugün ise user2357112'lere karşı puanlamak için bir kravat kırıcı ekleyeceğim.
Martin Ender

BTW, başka bir test senaryosu eklemeyi düşünüyor musunuz?
Martin Ender

@ m.buettner Hayır, bu oyun için yeterince iyi bir anlayışa sahip değilim. Bu yüzden benim test çantası ilginç bir tane olmayacak.
Ray

Ne yazık ki, başka bir kural daha eklemek zorunda kaldım. Hafıza bu zorlukta zamandan daha sınırlı görünüyor, bu yüzden hafıza tüketimini sınırlamak için zorlanmak zorunda kaldım. Yarışçınızın 1GB'tan fazla bellek kullandığı herhangi bir işlem, zaman sınırını aşan aynı etkiye düşürülür. Bu kuralla, gönderiminiz, büyüklüğü bağlayan bir kesicinin sınırını geçen ilk kişidir n=270, bu yüzden şimdi diğer iki "en uygun" gönderimin arkasındasınız. Olduğu söyleniyor, sizin gönderiminiz aynı zamanda üçün en yavaşı, bu nedenle yine de daha büyük bir bağlayıcı ile üçüncü olurdu.
Martin Ender

Lütfen herhangi bir optimizasyon uygularsanız bana bildirin, böylece testleri tekrar başlatabilirim.
Martin Ender

1

RandomRacer, ~ 40.0 (10 çalışmadan ortalama)

Bu botun hiçbir zaman bir parkuru bitirmesi değil, kesinlikle 10 denemede bir kez olduğundan daha az sıklıkta. (Her 20 ila 30 simülasyonda en kötü durumdan bir puan alırım.)

Bu çoğunlukla temel durum olarak hareket etmek ve bir yarışçı için olası (Ruby) bir uygulamayı göstermek içindir:

# Parse initial input
target = gets.to_i
size = gets.split.map(&:to_i)
track = []
size[1].times do
    track.push gets
end
time_budget = gets.to_f

# Find start position
start_y = track.find_index { |row| row['S'] }
start_x = track[start_y].index 'S'

position = [start_x, start_y]
velocity = [0, 0]

while true
    x = rand(3) - 1
    y = rand(3) - 1
    puts [x,y].join ' '
    $stdout.flush

    first_line = gets
    break if !first_line || first_line.chomp.empty?

    position = first_line.split.map(&:to_i)
    velocity = gets.split.map(&:to_i)
    time_budget = gets.to_f
end

Çalıştır

ruby controller.rb benchmark.txt ruby randomracer.rb

1

Rastgele Yarışçı 2.0, ~ 31

Peki bu yayınlanan en iyi çözücüyü yenmeyecek, ancak rastgele bir yarışçıda küçük bir gelişme. Başlıca fark, bu yarışçının, geçerli yerlerin tükenmediği sürece ve duvarın olmadığı bir yere rastgele gitmeyi düşünmesidir ve eğer dönecek bir hedefe gidebilirse, olacaktır. Aynı zamanda, başka bir hareket olmadıkça (muhtemel olmayan, ancak mümkün olmadıkça) aynı noktada kalmak için hareket etmeyecektir.

Java'da uygulanmış, java8 ile derlenmiş, ancak Java 6'nın iyi olması gerekir. Komut satırı parametresi yok. Oldukça iyi bir hiyerarşi kümesi var, sanırım Java'yı doğru yapıyorum.

import java.util.Scanner;
import java.util.Random;
import java.util.ArrayList;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class VectorRacing   {
    private static Scanner in = new Scanner(System.in);
    private static Random rand = new Random();
    private static Track track;
    private static Racer racer;
    private static int target;
    private static double time;
    public static void main(String[] args)  {
        init();
        main_loop();
    }
    private static void main_loop() {
        Scanner linescan;
        String line;
        int count = 0,
            x, y, u, v;

        while(!racer.lost() && !racer.won() && count < target)  {
            Direction d = racer.think();
            racer.move(d);
            count++;
            System.out.println(d);

            line = in.nextLine();
            if(line.equals("")) {
                break;
            }
            linescan = new Scanner(line);
            x = linescan.nextInt();
            y = linescan.nextInt();
            linescan = new Scanner(in.nextLine());
            u = linescan.nextInt();
            v = linescan.nextInt();
            time = Double.parseDouble(in.nextLine());

            assert x == racer.location.x;
            assert y == racer.location.y;
            assert u == racer.direction.x;
            assert v == racer.direction.y;
        }
    }
    private static void init()  {
        target = Integer.parseInt(in.nextLine());
        int width = in.nextInt();
        int height = Integer.parseInt(in.nextLine().trim());
        String[] ascii = new String[height];
        for(int i = 0; i < height; i++) {
            ascii[i] = in.nextLine();
        }
        time = Double.parseDouble(in.nextLine());
        track = new Track(width, height, ascii);
        for(int y = 0; y < ascii.length; y++)   {
            int x = ascii[y].indexOf("S");
            if( x != -1)    {
                racer = new RandomRacer(track, new Location(x, y));
                break;
            }
        }
    }

    public static class RandomRacer extends Racer   {
        public RandomRacer(Track t, Location l) {
            super(t, l);
        }
        public Direction think()    {
            ArrayList<Pair<Location, Direction> > possible = this.getLocationsCanMoveTo();
            if(possible.size() == 0)    {
                return Direction.NONE;
            }
            Pair<Location, Direction> ret = null;
            do  {
                ret = possible.get(rand.nextInt(possible.size()));
            }   while(possible.size() != 1 && ret.a.equals(this.location));
            return ret.b;
        }
    }

    // Base things
    public enum Direction   {
        NORTH("0 -1"), SOUTH("0 1"), EAST("1 0"), WEST("-1 0"), NONE("0 0"),
        NORTH_EAST("1 -1"), NORTH_WEST("-1 -1"), SOUTH_EAST("1 1"), SOUTH_WEST("-1 1");

        private final String d;
        private Direction(String d) {this.d = d;}
        public String toString()    {return d;}
    }
    public enum Cell    {
        WALL('#'), GOAL('*'), ROAD('.'), OUT_OF_BOUNDS('?');

        private final char c;
        private Cell(char c)    {this.c = c;}
        public String toString()    {return "" + c;}
    }

    public static class Track   {
        private Cell[][] track;
        private int target;
        private double time;
        public Track(int width, int height, String[] ascii) {
            this.track = new Cell[width][height];
            for(int y = 0; y < height; y++) {
                for(int x = 0; x < width; x++)  {
                    switch(ascii[y].charAt(x))  {
                        case '#':   this.track[x][y] = Cell.WALL; break;
                        case '*':   this.track[x][y] = Cell.GOAL; break;
                        case '.':
                        case 'S':   this.track[x][y] = Cell.ROAD; break;
                        default:    System.exit(-1);
                    }
                }
            }
        }
        public Cell atLocation(Location loc)    {
            if(loc.x < 0 || loc.x >= track.length || loc.y < 0 || loc.y >= track[0].length) return Cell.OUT_OF_BOUNDS;
            return track[loc.x][loc.y];
        }

        public String toString()    {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(bos);
            for(int y = 0; y < track[0].length; y++)    {
                for(int x = 0; x < track.length; x++)   {
                    ps.append(track[x][y].toString());
                }
                ps.append('\n');
            }
            String ret = bos.toString();
            ps.close();
            return ret;
        }
    }

    public static abstract class Racer  {
        protected Velocity tdir;
        protected Location tloc;
        protected Track track;
        public Velocity direction;
        public Location location;

        public Racer(Track track, Location start)   {
            this.track = track;
            direction = new Velocity(0, 0);
            location = start;
        }
        public boolean canMove() throws GoHereDammitException {return canMove(Direction.NONE);}
        public boolean canMove(Direction d) throws GoHereDammitException    {
            tdir = new Velocity(direction);
            tloc = new Location(location);
            tdir.add(d);
            tloc.move(tdir);
            Cell at = track.atLocation(tloc);
            if(at == Cell.GOAL) {
                throw new GoHereDammitException();
            }
            return at == Cell.ROAD;
        }
        public ArrayList<Pair<Location, Direction> > getLocationsCanMoveTo()    {
            ArrayList<Pair<Location, Direction> > ret = new ArrayList<Pair<Location, Direction> >(9);
            for(Direction d: Direction.values())    {
                try {
                    if(this.canMove(d)) {
                        ret.add(new Pair<Location, Direction>(tloc, d));
                    }
                }   catch(GoHereDammitException e)  {
                    ret.clear();
                    ret.add(new Pair<Location, Direction>(tloc, d));
                    return ret;
                }
            }
            return ret;
        }
        public void move()  {move(Direction.NONE);}
        public void move(Direction d)   {
            direction.add(d);
            location.move(direction);
        }
        public boolean won()    {
            return track.atLocation(location) == Cell.GOAL;
        }
        public boolean lost()   {
            return track.atLocation(location) == Cell.WALL || track.atLocation(location) == Cell.OUT_OF_BOUNDS;
        }
        public String toString()    {
            return location + ", " + direction;
        }
        public abstract Direction think();

        public class GoHereDammitException extends Exception    {
            public GoHereDammitException()  {}
        }
    }

    public static class Location extends Point  {
        public Location(int x, int y)   {
            super(x, y);
        }
        public Location(Location l) {
            super(l);
        }
        public void move(Velocity d)    {
            this.x += d.x;
            this.y += d.y;
        }
    }

    public static class Velocity extends Point  {
        public Velocity(int x, int y)   {
            super(x, y);
        }
        public Velocity(Velocity v) {
            super(v);
        }
        public void add(Direction d)    {
            if(d == Direction.NONE) return;
            if(d == Direction.NORTH || d == Direction.NORTH_EAST || d == Direction.NORTH_WEST)  this.y--;
            if(d == Direction.SOUTH || d == Direction.SOUTH_EAST || d == Direction.SOUTH_WEST)  this.y++;
            if(d == Direction.EAST || d == Direction.NORTH_EAST || d == Direction.SOUTH_EAST)   this.x++;
            if(d == Direction.WEST || d == Direction.NORTH_WEST || d == Direction.SOUTH_WEST)   this.x--;
        }
    }

    public static class Point   {
        protected int x, y;
        protected Point(int x, int y)   {
            this.x = x;
            this.y = y;
        }
        protected Point(Point c)    {
            this.x = c.x;
            this.y = c.y;
        }
        public int getX()   {return x;}
        public int getY()   {return y;}
        public String toString()    {return "(" + x + ", " + y + ")";}
        public boolean equals(Point p)  {
            return this.x == p.x && this.y == p.y;
        }
    }

    public static class Pair<T, U>  {
        public T a;
        public U b;
        public Pair(T t, U u)   {
            a=t;b=u;
        }
    }
}

Sonuçlar (Gördüğüm en iyi vaka)

Running 'java VectorRacing' against ruby-runner/benchmark.txt

 No.    Size     Target   Score     Details
-------------------------------------------------------------------------------------
  1    37 x 1        36   0.38889   Racer reached goal at ( 36, 0) in 14 turns.
  2    38 x 1        37   0.54054   Racer reached goal at ( 37, 0) in 20 turns.
  3    33 x 1        32   0.62500   Racer reached goal at ( 32, 0) in 20 turns.
  4    10 x 10       10   0.40000   Racer reached goal at ( 9, 8) in 4 turns.
  5     9 x 6         8   0.75000   Racer reached goal at ( 6, 2) in 6 turns.
  6    15 x 7        16   2.00000   Racer did not reach the goal within 16 turns.
  7    17 x 8        16   2.00000   Racer hit a wall at position ( 8, 2).
  8    19 x 13       18   0.44444   Racer reached goal at ( 16, 2) in 8 turns.
  9    60 x 10      107   0.65421   Racer reached goal at ( 0, 6) in 70 turns.
 10    31 x 31      106   2.00000   Racer hit a wall at position ( 25, 9).
 11    31 x 31      106   2.00000   Racer hit a wall at position ( 8, 1).
 12    50 x 20       50   2.00000   Racer hit a wall at position ( 27, 14).
 13   100 x 100    2600   2.00000   Racer went out of bounds at position ( 105, 99).
 14    79 x 63      242   2.00000   Racer went out of bounds at position (-2, 26).
 15    26 x 1        25   0.32000   Racer reached goal at ( 25, 0) in 8 turns.
 16    17 x 1        19   2.00000   Racer went out of bounds at position (-2, 0).
 17    50 x 1        55   2.00000   Racer went out of bounds at position ( 53, 0).
 18    10 x 7        23   2.00000   Racer went out of bounds at position ( 10, 2).
 19    55 x 55       45   0.33333   Racer reached goal at ( 4, 49) in 15 turns.
 20    50 x 50      200   2.00000   Racer hit a wall at position ( 14, 7).
-------------------------------------------------------------------------------------
TOTAL SCORE:             26.45641

Evet, çalışmasını sağladım, ancak bir .classnedenden dolayı dosyanın bulunduğu dizinden çalıştırmak zorunda kaldım (denetleyicinin bulunduğu dizin yerine). Bir test çantası eklemeye karar verirseniz bana bir yorum yapın, böylece kıyaslamaya ekleyebilirsiniz. Puanınız yaklaşık 10 üzerinden 33 (skor tablosuna bakın) idi, ancak bu teste eklenen her yeni test testinde de değişebilir.
Martin Ender

Ah diğer dizinden de kaçtı. Komut satırında Java ile aşina olmayanlar için:java -cp path/to/class/file VectorRacing
Martin Ender

Ah, evet bir sürü ders yaptım (tam olarak 13). Senaryoyu her zaman sınıflarımın dizininden çalıştırıyordum, o yüzden test etmedim. Bir test davası açabilirim, ancak önce rastgele olmayan bir yarışçı yapmaya çalışacağımı düşünüyorum.
takma

Elbette. Bunu yaparsanız, lütfen ayrı bir cevap olarak ekleyin. (Ve bunlardan her birine bir test senaryosu eklemekten çekinmeyin.)
Martin Ender

Ne yazık ki, başka bir kural daha eklemek zorunda kaldım. Hafıza bu zorlukta zamandan daha sınırlı görünüyor, bu yüzden hafıza tüketimini sınırlamak için zorlanmak zorunda kaldım. Yarışçınızın 1GB'tan fazla bellek kullandığı herhangi bir işlem, zaman sınırını aşan aynı etkiye düşürülür. Mevcut parça grubunda, puanınız bu değişiklikten etkilenmedi (ve muhtemelen hiç olmayacak).
Martin Ender
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.