2D Çarpışma Tespiti


21

Bu zorluk, son zamanlarda basit bir oyun için yazmak zorunda kaldığım gerçek çarpışma tespitine dayanıyor.

İki nesneye verilen , iki nesnenin çarpışma halinde olup olmadığına (yani kesişen) olup olmadığına bağlı olarak bir gerçeği veya sahte değeri döndüren bir program veya işlev yazın .

Üç tür nesneyi desteklemeniz gerekir:

  • Hat segmentleri : 4 yüzer ile temsil edilen, yani iki uç noktayı gösteren (x 1 , y 1 ) ve (x 2 , y 2 ) . Bitiş noktalarının aynı olmadığını varsayabilirsiniz (bu nedenle çizgi kesimi dejenere değildir).
  • Diskler : yani, merkezler için iki (x, y) ve biri yarıçap r için bir (pozitif) olmak üzere 3 yüzer ile temsil edilen doldurulmuş daireler .
  • Boşluklar : bunlar bir diskin tamamlayıcısıdır. Yani, bir boşluk , bir merkez ve yarıçap tarafından belirtilen dairesel bir bölge dışında tüm 2D alanı doldurur .

Programınız veya işleviniz, tanımlayıcı bir tamsayı (kendi seçiminize göre) ve 3 veya 4 değişken şeklinde iki tane nesne alacaktır. STDIN, ARGV veya fonksiyon argümanı ile giriş yapabilirsiniz. Girişi önceden işlenmemiş herhangi bir formda, örneğin 8 ila 10 bireysel sayı, iki virgülle ayrılmış değer listesi veya iki liste olarak temsil edebilirsiniz. Sonuç iade edilebilir veya STDOUT'a yazılabilir.

Nesnelerin en az 10-10 uzunluk birimi olduğu veya bu kadar kesiştiği varsayılabilir , bu nedenle kayan nokta türlerinin sınırlamaları hakkında endişelenmenize gerek yoktur.

Bu kod golf, yani en kısa cevap (bayt cinsinden) kazanır.

Test Kılıfları

Liste tabanlı bir giriş formatı kullanarak 0, disk bölümlerini 1ve boşlukları içeren çizgi parçalarını temsil 2eden aşağıdakilerin tümü bir gerçek çıktı üretmelidir:

[0,[0,0],[2,2]], [0,[1,0],[2,4]]        # Crossing line segments
[0,[0.5,0],[-0.5,0]], [1,[0,0],1]       # Line contained in a disc
[0,[0.5,0],[1.5,0]], [1,[0,0],1]        # Line partially within disc
[0,[-1.5,0.5],[1.5,0.5]], [1,[0,0],1]   # Line cutting through disc
[0,[0.5,2],[-0.5,2]], [2,[0,0],1]       # Line outside cavity
[0,[0.5,0],[1.5,0]], [2,[0,0],1]        # Line partially outside cavity
[0,[-1.5,0.5],[1.5,0.5]], [2,[0,0],1]   # Line cutting through cavity
[1,[0,0],1], [1,[0,0],2]                # Disc contained within another
[1,[0,0],1.1], [1,[2,0],1.1]            # Intersecting discs
[1,[3,0],1], [2,[0,0],1]                # Disc outside cavity
[1,[1,0],0.1], [2,[0,0],1]              # Disc partially outside cavity
[1,[0,0],2], [2,[0,0],1]                # Disc encircling cavity
[2,[0,0],1], [2,[0,0],1]                # Any two cavities intersect
[2,[-1,0],1], [2,[1,0],1]               # Any two cavities intersect

aşağıdakilerin tümü sahte çıktıyla sonuçlanmalıdır

[0,[0,0],[1,0]], [0,[0,1],[1,1]]        # Parallel lines
[0,[-2,0],[-1,0]], [0,[1,0],[2,0]]      # Collinear non-overlapping lines
[0,[0,0],[2,0]], [0,[1,1],[1,2]]        # Intersection outside one segment
[0,[0,0],[1,0]], [0,[2,1],[2,3]]        # Intersection outside both segments
[0,[-1,2],[1,2]], [1,[0,0],1]           # Line passes outside disc
[0,[2,0],[3,0]], [1,[0,0],1]            # Circle lies outside segment
[0,[-0.5,0.5],[0.5,-0.5]], [2,[0,0],1]  # Line inside cavity
[1,[-1,0],1], [1,[1,1],0.5]             # Non-intersecting circles
[1,[0.5,0],0.1], [2,[0,0],1]            # Circle contained within cavity

İlk başta düşündüğümden daha zor. Hat / hat durumundan başlayarak şaşırtıcı sayıda kenar vakasına rastladım. Koleksiyon bölümlerine izin veremez misin? İşleri çok kolaylaştırırdı. ;)
Emil

@Emil Üzgünüz, ancak gönderimden 9 saat sonra, başkalarının meydan okuma üzerinde çalışmaya başlamış olabileceğini varsaymak zorunda kalacağım ve açıklamayı değiştirmek (kırılma sorunlarını düzeltmenin dışında) bana iyi bir fikir gibi görünmüyor. Nasıl yaptığınıza bağlı olarak, paralel çizgi kesimleri çizgi çizgi çarpışmaları için endişelenmeniz gereken tek kenar durum olmalıdır, sanırım.
Martin Ender

Tabii, değiştirmenizi beklemiyordum. Kolin çizgisi segmentlerinin farklı değişkenlerini kullanmamın kodumun uzunluğunu iki katına çıkarmasından dolayı biraz sinirliydim. :) (Bu arada harika bir meydan okuma!)
Emil 20

Koleksiyon puanları "10 ^ -10 ile çarpışmaz" altına girmiyor mu?
TwiNight

@TwiNight Eğer iki çizgi collinear ise ama üst üste binmeyin. Örneğin[0,[-2,0],[-1,0]], [0,[1,0],[2,0]]
Martin Ender,

Yanıtlar:


6

APL, 279 208 206 203

s←1 ¯1
f←{x←⊣/¨z←⍺⍵[⍋⊣/¨⍺⍵]
2 2≡x:∧/0∧.=⌊(2⊃-⌿↑z)⌹⍣(≠.×∘⌽/x)⍉↑x←s×-/2⊢/↑z
2≡2⌷x:∨/((2⊃z)∇2,x[1]×(2⌷⊃z)+,∘-⍨⊂y÷.5*⍨+.×⍨y←⌽s×⊃-/y),x[1]=(×⍨3⊃⊃z)>+.×⍨¨y←(s↓⌽↑z)-2⌷⊃z
~x∨.∧x[1]≠(.5*⍨+.×⍨2⊃-⌿↑z)<-/⊢/¨z×s*1⌷x}

İşlevdeki satır kesmeleri fnetlik içindir. Deyim ayırıcı ile değiştirilmeleri gerekir.

Bu kadar karmaşık bir APL programı yaptığımdan bu yana çok zaman geçti. Ben son kez olduğunu düşünüyorum bu ama karmaşık olarak olduğuna bile emin değilim.

Giriş formatı
Temel olarak OP ile aynıdır 0, oyuk kullanımı dışında , 1disk için ve 2çizgi parçası için.

Büyük güncelleme

Farklı bir algoritma kullanarak çok fazla karakter oynamayı başardım. Daha fazla gboğa yok ** t !!

Ana işlev fdurumlara ayrılmıştır:


2 2≡x: Segment segmenti

Bu durumda, vektörü her çizginin son noktalarından hesaplayın ve kesişimin vektörlerin içinde olup olmadığını kontrol etmek için bir doğrusal denklem sistemi çözün.

Uyarılar:

  • Bir vektörün bitiş noktası, vektörün bir parçası olarak kabul edilmez (menşei iken). Bununla birlikte, bir vektörün sadece ucu diğerinde ise, girdi spesifikasyonlara göre geçersizdir.
  • Dejenere olmayan paralel segmentler, eşdoğrusallıktan bağımsız olarak her zaman yanlış döndürür.
  • Segmentlerden biri dejenere ise, her zaman yanlış döndürün. Her iki bölüm dejenere ise, her zaman doğru döndürün.

Örnekler: (Sağdaki şekilde eylem halinde olan nota 1'i not edin)


2≡2⌷xSegment-diğer

Bu durumda, diğer nesne daireseldir. Mesafenin son noktalarının mesafe kontrolünü kullanarak dairenin içinde olup olmadığını kontrol edin.

Disk durumunda, ayrıca verilen bölüme dik çapta bir çizgi parçası oluşturun. Segmentlerin özyinelemeyle çarpışıp çarpışmadığını kontrol edin.
Boşluk durumunda, söz konusu parçanın yapısında dejenere olması için "0 kez" gizlice girin. (Neden şimdi 0oyuk ve 1disk için kullanıyorum bakalım?) Verilen segment dejenere olmadığından, segment-segment çarpışma tespiti her zaman yanlıştır.

Son olarak mesafe kontrollerinin sonuçları ile çarpışma tespitinin birleştirilmesi. Boşluk durumunda, önce mesafe kontrollerinin sonuçlarını dikkate almayın. Sonra (her iki durumda da) VEYA 3 birlikte sonuçlanır.

Segment segmenti uyarılarına gelince, 3 sayısı adreslenir (ve kullanılır). Sayı 2, burada dejenere olmadıklarında asla paralel olmayan, dik bölümleri kestiğimiz için sorun değil. 1 numara, sadece verilen disk noktalarında, verilen uç noktalardan biri yapılmış çapta olduğunda etkili olur. Bitiş noktası dairenin içinde iyiyse, mesafe kontrolleri onunla ilgilenirdi. Uç nokta daire üzerinde ise, oluşturulan çap verilen bölüme paralel olduğu için, ikincisi, diske dokunan, geçerli giriş olmayan sadece bir nokta olan daireye teğet olmalıdır.

Örnekler:


Varsayılan durum: Diğer-diğer

Merkezler arasındaki mesafeyi hesaplayın. Disk-disk çarpışması, ancak mesafe yarıçapların toplamından küçükse gerçekleşir. Disk boşluğu çarpışması, eğer mesafe sadece yarıçap farkından büyükse oluşur.

Kavite boşluğu durumunun üstesinden gelmek için, mesafe kontrolünün sonucunu, VE de tanımlayıcı tamsayıların her biriyle ve ardından VEYA ile birlikte onları olumsuzlayın. Bazı mantıkları kullanarak, bu işlemin sadece her iki tanımlayıcı tamsayıların da hatalı olması durumunda (Boşluk-boşluk durumu) ya da mesafe kontrolü doğru şekilde döndürüldüğünde doğru olduğunu gösterebilir.


Anladığım kadarıyla eğer programınız ASCII yerine Unicode'a yayılan karakterler kullanılarak yazılmışsa, bayt sayısının Unicode'un karakter başına 2 baytlık niteliğini kabul etmesi gerekir.
COTO

@COTO Unicode belirtmedim. Farkındayım kadarıyla gibi, APL karakter seti byte içine uyum yapar ve orada olan tüm APL karakterleri içeren tek bayt kod sayfaları böylece kodlama kullanarak, bayt sayısı gayet iyi. Bayt sayımı, çoğunlukla "bayt" karakterlerinde çoklu bayt dizelerinde kodlayan veya Mathematica'nın Unicode kısayollarını kullananlar için geçerlidir.
Martin Ender

@ MartinBüttner: Yani diyelim ki APL için açıkça tasarlananların yanı sıra, herhangi bir metin düzenleyicide dizinin 1 bayt sürümünü makul veya pratik olarak temsil edemese bile, karakter başına 1 bayt olarak sayılıyor. dilin belirtmesinde 256 veya daha az izin verilen karakter olduğundan?
COTO

@COTO Eh ve kodlamaları çünkü varız tek baytlık kodlanmış dosya yorumlanabilir Buna göre. Eğer insanlar kodlamalarını yapmak zorunda kalsalardı bu yoldan gitmeye istekli olacağımı sanmıyorum. Aksi takdirde , 257'den az farklı karakter kullanan herhangi bir program (ki bu PPCG'de neredeyse herhangi bir cevap olurdu) iddia edebilir. Sadece Unicoding'i birkaç yıl önce öldürmek için APL'yi cezalandırmamamız gerektiğini düşünüyorum - o zamanlar sahip olduğunuz baytları anımsatıcı olarak çalışan garip korkak karakterler olarak yorumlamak mantıklı gelmişti.
Martin Ender

1
@COTO APL'ye dayanan ve yalnızca ASCII karakterlerini kullanan J var. Genelde benzer puan alırlar, bu yüzden muhtemelen Unicode tarafından atılmış olsa bile sizi yener. Ve dilin hiçbirinin golf oynamak için tasarlanmadığını ve AFAIK’ın aslında profesyonelce kullanıldığını da eklemeliyim. Ve buradaki golf mücadeleleri yeşil onay işaretini almakla ilgili bir şey değil, programınızdaki her küçük byte'ı dilinizin araçlarıyla sıkmak ve aynı "ağırlık kategorisinde" herkesi yenmekle ilgilidir, bu size genellikle daha fazla kazanç sağlar Zayıf bir dil kullanmaktan ziyade. ;)
Martin Ender

5

Javascript - 393 bayt

minified:

F=(s,a,t,b,e,x)=>(x=e||F(t,b,s,a,1),[A,B]=a,[C,D]=b,r=(p,l)=>([g,h]=l,[f,i]=y(h,g),[j,k]=y(p,g),m=Math.sqrt(f*f+i*i),[(f*j+i*k)/m,(f*k-i*j)/m]),u=(p,c)=>([f,g]=c,[i,j]=y(p,f),i*i+j*j<g*g),y=(p,c)=>[p[0]-c[0],p[1]-c[1]],[n,o]=r(C,a),[q,v]=r(D,a),w=(v*n-o*q)/(v-o),z=r(B,a)[0],Y=u(A,b),Z=u(B,b),[v*o<0&&w*(w-z)<0,Y||Z||o<D&&o>-D&&n*(n-z)<0,!Y||!Z,x,u(A,[C,D+B]),B>D||!u(A,[C,D-B]),x,x,1][s*3+t])

Expanded:

F = (s,a,t,b,e,x) => (
    x = e || F(t,b,s,a,1),
    [A,B] = a,
    [C,D] = b,
    r = (p,l) => (
        [g,h] = l,
        [f,i] = y(h,g),
        [j,k] = y(p,g),
        m = Math.sqrt( f*f + i*i ),
        [(f*j + i*k)/m, (f*k - i*j)/m] ),
    u = (p,c) => (
        [f,g] = c,
        [i,j] = y(p,f),
        i*i + j*j < g*g ),
    y = (p,c) => [p[0] - c[0], p[1] - c[1]],
    [n,o] = r(C,a),
    [q,v] = r(D,a),
    w = (v*n - o*q)/(v - o),
    z = r(B,a)[0],
    Y = u(A,b), Z = u(B,b),
    [   v*o < 0 && w*(w-z) < 0,
        Y || Z || o < D && o > -D && n*(n-z) < 0,
        !Y || !Z,
        x,
        u(A,[C,D+B]),
        B > D || !u(A,[C,D-B]),
        x,
        x,
        1
    ][s*3+t]);

Notlar:

  • FGerekli argümanları kabul eden ve istenen değeri veren fonksiyonu tanımlar
  • giriş formatı OP'deki formatla aynıdır, ancak her ilkel için tamsayı türü kodunun demetden ayrı olması dışında. Örneğin, F( 0,[[0,0],[2,2]], 0,[[1,0],[2,4]] )ya da F( 1,[[3,0],1], 2,[[0,0],1] ).
  • OP’de verilen tüm test durumlarında onaylanmış kod
  • sıfır uzunluklu çizgi parçaları ve sıfır yarıçaplı daireler dahil olmak üzere tüm kenar ve köşe kasalarını taşımalıdır

Ah, bu iki davadan bahsettiğin için teşekkürler. Sıfır yarıçapı dairelerin ele alınması gerekmez (yazı pozitif yarıçapı söyler) ve ayrıca çizgi bölümünün uçlarının belirgin olacağını açıklığa kavuşturacağım.
Martin Ender

4

Python, 284

Tüm bu geometrik hilelere kıyasla oldukça çöp algoritması kullanıyorum, ancak test senaryolarına ulaşmak bir dakikadan fazla sürse bile doğru cevapları alıyor. En büyük avantaj, yalnızca üç puanlama işlevini yazmam gerekmesi ve tepe tırmanma tüm son durumlarla ilgileniyor.

golfed:

import math,random as r
n=lambda(a,c),(b,d):math.sqrt((a-b)**2+(c-d)**2)
x=lambda(t,a,b),p:max(eval(["n(b,p)-n(a,b)+","-b+","b-"][t]+'n(a,p)'),0)
def F(t,j):
q=0,0;w=1e9
 for i in q*9000:
    y=x(t,q)+x(j,q)
    if y<w:p,w=q,y
    q=(r.random()-.5)*w+p[0],(r.random()-.5)*w+p[1]
 return w<.0001

Ungolfed:

import math
import random as r
def norm(a, b):
 return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2)

def lineWeight(a, b, p):
 l1 = norm(a, p)
 l2 = norm(b, p)
 return min(l1, l2, l1 + l2 - norm(a, b))

def circleWeight(a, r, p):
 return max(0, norm(a, p) - r)

def voidWeight(a, r, p):
 return max(0, r - norm(a, p))

def weight(f1, f2, s1, s2, p):
 return f1(s1[1], s1[2], p) + f2(s2[1], s2[2], p)

def checkCollision(s1, s2):
 a = [lineWeight, circleWeight, voidWeight]
 f1 = a[s1[0]]
 f2 = a[s2[0]]
 p = (0.0, 0.0)
 w = 0
 for i in a*1000:
  w = weight(f1, f2, s1, s2, p)
  p2 = ((r.random()-.5)*w + p[0], (r.random()-.5)*w + p[1])
  if(weight(f1, f2, s1, s2, p2) < w):
   p = p2
 if w < .0001:
  return True
 return False

Ve son olarak, başka birinin bunu python ile denemek istemesi durumunda bir test senaryosu:

import collisiongolfedbak
reload(collisiongolfedbak)

tests = [
[0,[0,0],[2,2]], [0,[1,0],[2,4]],        # Crossing line segments
[0,[0.5,0],[-0.5,0]], [1,[0,0],1],       # Line contained in a disc
[0,[0.5,0],[1.5,0]], [1,[0,0],1],        # Line partially within disc
[0,[-1.5,0.5],[1.5,0.5]], [1,[0,0],1],   # Line cutting through disc
[0,[0.5,2],[-0.5,2]], [2,[0,0],1],       # Line outside cavity
[0,[0.5,0],[1.5,0]], [2,[0,0],1],        # Line partially outside cavity
[0,[-1.5,0.5],[1.5,0.5]], [2,[0,0],1],   # Line cutting through cavity
[1,[0,0],1], [1,[0,0],2],                # Disc contained within another
[1,[0,0],1.1], [1,[2,0],1.1],            # Intersecting discs
[1,[3,0],1], [2,[0,0],1],                # Disc outside cavity
[1,[1,0],0.1], [2,[0,0],1],              # Disc partially outside cavity
[1,[0,0],2], [2,[0,0],1],                # Disc encircling cavity
[2,[0,0],1], [2,[0,0],1] ,               # Any two cavities intersect
[2,[-1,0],1], [2,[1,0],1] ,              # Any two cavities intersect
[0,[0,0],[1,0]], [0,[0,1],[1,1]] ,       # Parallel lines
[0,[-2,0],[-1,0]], [0,[1,0],[2,0]],      # Collinear non-overlapping lines
[0,[0,0],[2,0]], [0,[1,1],[1,2]],        # Intersection outside one segment
[0,[0,0],[1,0]], [0,[2,1],[2,3]],        # Intersection outside both segments
[0,[-1,2],[1,2]], [1,[0,0],1],           # Line passes outside disc
[0,[2,0],[3,0]], [1,[0,0],1],            # Circle lies outside segment
[0,[-0.5,0.5],[0.5,-0.5]], [2,[0,0],1],  # Line inside cavity
[1,[-1,0],1], [1,[1,1],0.5],             # Non-intersecting circles
[1,[0.5,0],0.1], [2,[0,0],1]            # Circle contained within cavity
]

for a, b in zip(tests[0::2], tests[1::2]):
 print collisiongolfedbak.F(a,b)
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.