Tam bitiş noktası ve sıfır terminal hızına sahip yarış pisti çeşidi


9

Giriş

Zorluk , oyun yarış pistinin ve bu iki zorluğun çok ilginç bir çeşididir :

Bu zorluğun kaynağı burada (Almanca): c't-Racetrack

Bu zorluk özellikle ilginçtir (ve yukarıda belirtilen iki zorluktan farklıdır), çünkü büyük bir arama alanını yerine getirilmesi gereken bazı kesin koşullarla birleştirir. Büyük arama alanı nedeniyle ayrıntılı arama tekniklerini kullanmak zordur, çünkü kesin koşullar nedeniyle yaklaşık yöntemler de kolayca kullanılamaz. Bu eşsiz kombinasyon (artı fiziğin altında yatan sezgi) nedeniyle sorun büyüleyici (ve yarış arabaları ile ilgili her şey zaten büyüleyici ;-)

Meydan okuma

Aşağıdaki yarış pistine ( kaynak ) bir göz atın :

resim açıklamasını buraya girin

Duvarlardan birine dokunmadan (120,180)önce (320,220)(Almanca "Ziel") ile başlayıp bitirmek zorundasınız .

Araba, formun hızlanma vektörleri tarafından kontrol edilir (a_x,a_y)- örnek olarak:

(8,-6)
(10,0)
(1,9)

İlk sayı x-vektörü için hızlanma, ikincisi y-vektörü için ivmedir. Tamsayı olmalıdırlar çünkü yalnızca ızgaradaki tamsayı noktalarını kullanmanıza izin verilir. Ayrıca, aşağıdaki koşulun karşılanması gerekir:

a_x^2 + a_y^2 <= 100,

bu da herhangi bir yöndeki ivmenin aşağı veya eşit olması gerektiği anlamına gelir 10.

Nasıl çalıştığını görmek için aşağıdaki resme ( kaynak ) bir göz atın :

resim açıklamasını buraya girin

Örnek olarak: (120,180)Sizden başlayarak 8x yönünde ve -6y yönünde hızlanın . Bir sonraki adım için bu, bir (10,0)sonraki sonuç hareketinizi (noktaya doğru) elde etmek için (fiziksel olarak doğru) hızlanmanızı eklediğiniz hızınızdır (146,168). Ortaya çıkan hareket, duvarlardan birine dokunup dokunmadığınızı incelemek söz konusu olduğunda önemli olan şeydir. bir sonraki ivme vektörünüzü bir sonraki hareketi elde etmek için mevcut hızınıza eklersiniz ve böylece her adımda aracınızın bir konumu ve bir hızı vardır. (Mavi okların üstündeki açıklayıcı resimde hız, turuncu oklar içindir. hızlanma için ve ortaya çıkan hareket için koyu kırmızı oklar.)

Ek bir koşul (0,0)olarak, bitiş noktasındayken terminal hızına sahip olmanız gerekir (320,220).

Çıktı, yukarıda belirtilen formda ivme vektörlerinin bir listesi olmalıdır.

Kazanan, en az hızlanma vektörü ile bir çözüm bulan bir program sağlayan kişidir.

Tiebreaker
Ek olarak, bunun en uygun çözüm olduğunu ve bunun en uygun çözüm olup olmadığını veya birkaç en uygun çözüm olup olmadığını (ve hangileri olduğunu) göstermeniz harika olur.

Ayrıca, algoritmanızın nasıl çalıştığının genel bir taslağını verebilir ve kodu anlayabilmemiz için kodu yorumlamanız da iyi olur.

Herhangi bir çözümün geçerli olup olmadığını kontrol eden bir programım var ve geri bildirimde bulunacağım.

Zeyil
Herhangi bir programlama dilini kullanabilirsiniz, ancak birileri R kullandıysa özellikle memnun olurum, çünkü günlük işimde çok kullanıyorum ve bir şekilde buna alıştım :-)

Zeyil II
İlk defa bir ödül kazandım - umarım bu get topu yuvarlar (ya da daha iyisi: get araba sürüyor :-)


@Mego: Yine de ... bunu düşündüğümde: Programı en az iki nedenden dolayı eklemem gerekip gerekmediğinden emin değilim: Birincisi, orijinal meydan okumaya da dahil edilmedi, ikincisi, örneğin bir parçası olan rutinleri içeriyor meydan okuma (örneğin çarpışma tespiti) bu yüzden eğlencenin bir kısmını
bozabilir

1
Programın aslında yolu hesaplaması gerekiyor mu, yoksa önceden en uygun yolu hesaplayıp sonra gibi bir şey yayınlayabilir print "(10,42)\n(62,64)..."miyim?
Loovjo

@Loovjo: Hayır, program yolun kendisini hesaplamalıdır, bu nedenle zeka sadece bir çıktı rutini değil, programa dahil edilmelidir.
vonjd

Yanıtlar:


4

Python, 24 adım (devam eden çalışma)

Fikir, ilk önce sürekli sorunu çözmek, arama alanını büyük ölçüde azaltmak ve ardından sonucu ızgaraya nicelleştirmekti (sadece en yakın ızgara noktasına yuvarlayarak ve çevreleyen 8 kareyi arayarak)

Yolu trigonometrik fonksiyonların bir toplamı olarak parametrelendiririm (polinomlardan farklı olarak, ayrışmazlar ve kontrol altında tutmak daha kolaydır). Ayrıca, hızlanma yerine hızı doğrudan kontrol ediyorum, çünkü sonunda 0'a eğilimli bir ağırlıklandırma fonksiyonunu çarparak sınır koşulunu zorlamak daha kolay.
Benim objektif fonksiyonum,
hızlanma için üstel skordan> 10 -
son nokta ile hedef arasındaki öklid mesafesi için polinom skorundan oluşur -
bir duvarla her bir kavşak için yüksek sabit puan, duvarın kenarlarına doğru azalır

Skoru en aza indirmek için hepsini Nelder-Mead optimizasyonuna atıyorum ve birkaç saniye bekliyorum. Algoritma her zaman sona ulaşmayı, orada durmayı ve maksimum ivmeyi aşmamayı başarır, ancak duvarlarla ilgili sorunları vardır. Yol ya köşelerden ışınlanır ve orada sıkışır ya da hedefin hemen karşısında bir duvarın yanında durur (sol görüntü)
resim açıklamasını buraya girin

Test sırasında, şanslıydım ve umut verici bir şekilde (sağ görüntü) dalgalı bir yol buldum ve parametreleri daha sonra biraz daha başarılı bir optimizasyon için bir başlangıç ​​tahmin olarak kullanabilirsiniz.

Nicemleme
Parametrik bir yol bulduktan sonra, ondalık noktalarını kaldırma zamanı gelmişti. 3x3 mahalleye bakmak, arama alanını kabaca 300 ^ N'den 9 ^ N'ye düşürür, ancak yine de uygulanması çok büyük ve sıkıcıdır. Bu yola inmeden önce, objektif işleve (yorum yapılan kısımlar) bir "Izgaraya Yapış" terimi eklemeyi denedim. Güncellenmiş hedef ve basit yuvarlama ile yüzlerce optimizasyon adımı, çözümü elde etmek için yeterliydi.

[(9, -1), (4, 0), (1, 1), (2, 2), (-1, 2), (-3, 4), (-3, 3), (-2 , 3), (-2, 2), (-1, 1), (0, 0), (1, -2), (2, -3), (2, -2), (3, -5 ), (2, -4), (1, -5), (-2, -3), (-2, -4), (-3, -9), (-4, -4), (- 5, 8), (-4, 8), (5, 8)]

Adım sayısı düzeltildi ve optimizasyonun bir parçası değildi, ancak yolun analitik bir tanımına sahip olduğumuzdan (ve maksimum hızlanma 10'un çok altında olduğundan), daha az sayıda dilimler

from numpy import *
from scipy.optimize import fmin
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection as LC

walls = array([[[0,0],[500,0]],   # [[x0,y0],[x1,y1]]
        [[500,0],[500,400]],
        [[500,400],[0,400]],
        [[0,400],[0,0]],

        [[200,200],[100,200]],
        [[100,200],[100,100]],
        [[100,100],[200,100]],

        [[250,300],[250,200]],

        [[300,300],[300,100]],
        [[300,200],[400,200]],
        [[300,100],[400,100]],

        [[100,180],[120, 200]], #debug walls
        [[100,120],[120, 100]],
        [[300,220],[320, 200]],
        #[[320,100],[300, 120]],
])

start = array([120,180])
goal = array([320,220])

###################################
# Boring stuff below, scroll down #
###################################
def weightedintersection2D(L1, L2):
    # http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
    p = L1[0]
    q = L2[0]
    r = L1[1]-L1[0]
    s = L2[1]-L2[0]
    d = cross(r,s)
    if d==0: # parallel
        if cross(q-p,r)==0: return 1 # overlap
    else:
        t = cross(q-p,s)*1.0/d
        u = cross(q-p,r)*1.0/d
        if 0<=t<=1 and 0<=u<=1: return 1-0*abs(t-.5)-1*abs(u-.5) # intersect at p+tr=q+us
    return 0

def sinsum(coeff, tt):
    '''input: list of length 2(2k+1), 
    first half for X-movement, second for Y-movement.
    Of each, the first k elements are sin-coefficients
    the next k+1 elements are cos-coefficients'''
    N = len(coeff)/2
    XS = [0]+list(coeff[:N][:N/2])
    XC =     coeff[:N][N/2:]
    YS = [0]+list(coeff[N:][:N/2])
    YC =     coeff[N:][N/2:]
    VX = sum([XS[i]*sin(tt*ww[i]) + XC[i]*cos(tt*ww[i]) for i in range(N/2+1)], 0)
    VY = sum([YS[i]*sin(tt*ww[i]) + YC[i]*cos(tt*ww[i]) for i in range(N/2+1)], 0)
    return VX*weightfunc, VY*weightfunc

def makepath(vx, vy):
    # turn coordinates into line segments, to check for intersections
    xx = cumsum(vx)+start[0]
    yy = cumsum(vy)+start[1]
    path = []
    for i in range(1,len(xx)):
        path.append([[xx[i-1], yy[i-1]],[xx[i], yy[i]]])
    return path

def checkpath(path):
    intersections = 0
    for line1 in path[:-1]: # last two elements are equal, and thus wrongly intersect each wall
        for line2 in walls:
            intersections += weightedintersection2D(array(line1), array(line2))
    return intersections

def eval_score(coeff):
    # tweak everything for better convergence
    vx, vy = sinsum(coeff, tt)
    path = makepath(vx, vy)
    score_int = checkpath(path)
    dist = hypot(*(path[-1][1]-goal))
    score_pos = abs(dist)**3
    acc = hypot(diff(vx), diff(vy))
    score_acc = sum(exp(clip(3*(acc-10), -10,20)))
    #score_snap = sum(abs(diff(vx)-diff(vx).round())) + sum(abs(diff(vy)-diff(vy).round()))
    print score_int, score_pos, score_acc#, score_snap
    return score_int*100 + score_pos*.5 + score_acc #+ score_snap

######################################
# Boring stuff above, scroll to here #
######################################
Nw = 4 # <3: paths not squiggly enough, >6: too many dimensions, slow
ww = [1*pi*k for k in range(Nw)]
Nt = 30 # find a solution with tis many steps
tt = linspace(0,1,Nt)
weightfunc = tanh(tt*30)*tanh(30*(1-tt)) # makes sure end velocity is 0

guess = random.random(4*Nw-2)*10-5
guess = array([ 5.72255365, -0.02720178,  8.09631272,  1.88852287, -2.28175362,
        2.915817  ,  8.29529905,  8.46535503,  5.32069444, -1.7422171 ,
       -3.87486437,  1.35836498, -1.28681144,  2.20784655])  # this is a good start...
array([ 10.50877078,  -0.1177561 ,   4.63897574,  -0.79066986,
         3.08680958,  -0.66848585,   4.34140494,   6.80129358,
         5.13853914,  -7.02747384,  -1.80208349,   1.91870184,
        -4.21784737,   0.17727804]) # ...and it returns this solution      

optimsettings = dict(
    xtol = 1e-6,
    ftol = 1e-6,
    disp = 1,
    maxiter = 1000, # better restart if not even close after 300
    full_output = 1,
    retall = 1)

plt.ion()
plt.axes().add_collection(LC(walls))
plt.xlim(-10,510)
plt.ylim(-10,410)
path = makepath(*sinsum(guess, tt))
plt.axes().add_collection(LC(path, color='red'))
plt.plot(*start, marker='o')
plt.plot(*goal, marker='o')
plt.show()

optres = fmin(eval_score, guess, **optimsettings)
optcoeff = optres[0]    

#for c in optres[-1][::optimsettings['maxiter']/10]:
for c in array(optres[-1])[logspace(1,log10(optimsettings['maxiter']-1), 10).astype(int)]:
    vx, vy = sinsum(c, tt)
    path = makepath(vx,vy)
    plt.axes().add_collection(LC(path, color='green'))
    plt.show()

Yapılacaklar: Kaba bir yön duygusu elde etmek için bir başlangıç ​​yolu çizmenizi sağlayan GUI. Her şey 14 boyutlu uzaydan rastgele örneklemeden daha iyidir


Aferin! Görünüşe göre en az 17 adım - bu ek bilgilerle bir çözüm bulmak için programınızı nasıl değiştirirsiniz?
vonjd

Ah canım: Programım (320,220) ile bitmediğini ama (320,240) ile bitirdiğini gösteriyor - lütfen kontrol eder misin?
vonjd

1
whoops, çözümü güncelledi, ayrıca 24 adıma indirdi. Elle ince ayar, resme bakarak, genel bir vaka ile çalışacak şekilde
otomatize edilerek
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.