2D 충돌 감지


21

이 도전은 최근 간단한 게임을 위해 작성해야했던 실제 충돌 감지를 기반으로합니다.

두 개의 객체가 주어지면 두 객체 가 충돌하는지 (즉, 교차하는지)에 따라 참 또는 거짓 값을 반환하는 프로그램이나 함수를 작성하십시오 .

세 가지 유형의 객체를 지원해야합니다.

  • 선분 : 4로 표시되는 플로트 즉 두 종점을 나타내는 (X 1 , Y 1 )(X 2 , Y 2 ) . 끝 점이 동일하지 않다고 가정 할 수 있습니다 (따라서 선분이 축퇴되지 않음).
  • 디스크 : 즉 채워진 원은 3 개의 플로트로 표시되며, 2 개는 중심 (x, y )에 대해 2 개, 반경 ( r )에 대해 1 (양)입니다 .
  • 공동 : 이것들은 디스크의 보완입니다. 즉, 캐비티는 중심과 반경으로 지정된 원형 영역을 제외한 모든 2D 공간을 채 웁니다 .

프로그램이나 함수는 식별 정수 (선택한 정수)와 3 개 또는 4 개의 부동 소수점 형식으로 이러한 두 개의 객체를받습니다. STDIN, ARGV 또는 함수 인수를 통해 입력을받을 수 있습니다. 사전 처리되지 않은 편리한 형식 (예 : 8-10 개의 개별 숫자, 쉼표로 구분 된 두 개의 값 목록 또는 두 개의 목록)으로 입력을 나타낼 수 있습니다. 결과는 STDOUT에 리턴되거나 기록 될 수 있습니다.

객체가 최소 10 -10 길이 단위 떨어져 있거나 그와 많이 교차 한다고 가정 할 수 있으므로 부동 소수점 유형의 제한에 대해 걱정할 필요가 없습니다.

이것은 코드 골프이므로 가장 짧은 대답 (바이트)이 이깁니다.

테스트 사례

목록 기반 입력 형식을 사용하여 0, 디스크 1및 캐비티가있는 선 세그먼트를 2표시하면 다음과 같은 결과가 모두 출력됩니다.

[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

처음에 생각했던 것보다 까다 롭습니다. 라인 / 라인 케이스부터 시작해서 놀라운 수의 에지 케이스가 나옵니다. 공선 세그먼트를 허용하지 않습니까? 일을 훨씬 쉽게 만들 것입니다. ;)
Emil

@Emil 죄송하지만 게시 후 9 시간이 지나면 다른 사람들이 문제를 해결하기 시작했을 수 있다고 가정해야하며 (문제를 해결하는 것을 제외하고) 사양을 변경하는 것은 좋은 생각처럼 보이지 않습니다. 어떻게하는지에 따라 평행 선 세그먼트가 선 충돌에 대해 걱정해야 할 유일한 경우가되어야한다고 생각합니다.
Martin Ender

물론, 나는 당신이 그것을 바꿀 것으로 기대하지 않았습니다. 공선 라인 세그먼트의 다른 변형을 처리하는 것이 지금까지 코드 길이를 두 배로 늘렸다는 사실에 약간 실망했습니다. :) (그렇게 큰 도전!)
Emil

공선 점이 "10 ^ -10만큼 충돌하지 않음"에 속하지 않습니까?
TwiNight

@TwiNight 두 줄이 동일 선상에 있지만 겹치지 않는 경우에는 아닙니다. 예 :[0,[-2,0],[-1,0]], [0,[1,0],[2,0]]
Martin Ender

답변:


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}

함수의 줄 바꿈은 f명확성을위한 것입니다. 명령문 구분 기호로 바꿔야합니다.

마지막으로 복잡한 APL 프로그램을 만든 지 오래되었습니다. 나는 마지막으로는 생각 있지만 복잡했다 심지어 확실하지 않다.

입력 형식 캐비티, 디스크 및 선분에 대한
사용 0을 제외하고는 기본적으로 OP와 동일 합니다.12

주요 업데이트

나는 다른 알고리즘을 사용하여 많은 캐릭터를 골프로 쳤습니다. 더 이상 g황소가 없습니다 !

주요 기능 f은 다음과 같은 경우로 나뉩니다.


2 2≡x: 세그먼트 세그먼트

이 경우 각 선의 끝점에서 벡터를 계산하고 선형 방정식 시스템을 풀어 교차가 벡터 내에 포함되어 있는지 확인하십시오.

주의 사항 :

  • 벡터의 끝점은 벡터의 일부로 간주되지 않습니다 (원점이있는 동안). 그러나 벡터의 끝만 다른쪽에 있으면 입력에 따라 입력이 유효하지 않습니다.
  • 축퇴하지 않은 병렬 세그먼트는 공선 성과 상관없이 항상 false를 반환합니다.
  • 세그먼트 중 하나가 축퇴되면 항상 false를 리턴하십시오. 두 세그먼트가 모두 퇴화되면 항상 true를 리턴하십시오.

예 : (오른쪽 그림의주의 사항 1 참고)


2≡2⌷x: 세그먼트 기타

이 경우 다른 개체는 원형 개체입니다. 거리 확인을 사용하여 세그먼트의 끝 점이 원 안에 있는지 확인하십시오.

디스크 케이스에서, 주어진 세그먼트에 수직 인 직경의 선 세그먼트를 구성하십시오. 세그먼트가 재귀에 의해 충돌하는지 확인하십시오.
공동의 경우, 상기 세그먼트의 구성에서 "시간 0"으로 몰래 축퇴되도록한다. ( 현재 0캐비티와 1디스크에 사용 하는 이유를 참조하십시오 .) 주어진 세그먼트가 퇴화되지 않기 때문에 세그먼트 세그먼트 충돌 감지는 항상 false를 반환합니다.

마지막으로 거리 확인 결과와 충돌 감지 결과를 결합합니다. 캐비티 케이스의 경우 먼저 거리 점검 결과를 무효화하십시오. 그런 다음 (두 경우 모두) 또는 3 개의 결과가 함께 있습니다.

세그먼트 세그먼트주의 사항과 관련하여, 숫자 3이 다루어지고 활용됩니다. 우리가 여기서 수직 세그먼트를 교차 할 때 숫자 2는 문제가되지 않습니다. 숫자 1은 주어진 끝점 중 하나가 구성된 직경에있을 때 디스크 케이스에서만 적용됩니다. 끝 점이 원 안에 있으면 거리 점검으로 처리됩니다. 종점이 원에있는 경우, 구성된 직경이 주어진 세그먼트와 평행하기 때문에, 원점은 원점에 접해야합니다. 한 점만 디스크에 닿아 유효하지 않습니다.

예 :


기본 사례 : 기타

중심 사이의 거리를 계산하십시오. 거리가 반지름의 합보다 작은 경우에만 디스크 디스크 충돌이 발생합니다. 거리가 반경 차이보다 큰 경우에만 디스크 캐비티 충돌이 발생합니다.

캐비티-캐비티 케이스를 처리하려면 거리 확인 결과를 무효화하고 각각의 식별 정수를 사용한 다음 함께 OR하십시오. 일부 논리를 사용하면 식별하는 두 정수가 모두 잘못된 경우 (Cavity-cavity case) 또는 거리 확인이 true를 반환 한 경우에만이 프로세스가 true를 반환 함을 알 수 있습니다.


프로그램이 ASCII가 아닌 유니 코드에 걸쳐있는 문자를 사용하여 작성된 경우 바이트 수는 문자 당 2 바이트의 유니 코드를 인정해야합니다.
COTO

@COTO 나는 유니 코드를 지정하지 않았다. 내가 알고 있어요 멀리로, APL 문자 집합이 바이트에 맞지 않는, 거기에 있는 모든 APL 문자가 포함 된 단일 바이트 코드 페이지는 그래서 인코딩을 사용하여, 바이트 수는 괜찮습니다. 바이트 수는 대부분 "일반"언어로 멀티 바이트 문자열로 물건을 인코딩하거나 Mathematica의 유니 코드 바로 가기를 사용하는 사람들과 관련이 있습니다.
Martin Ender

@ MartinBüttner : APL 용으로 명시 적으로 설계된 것 이외의 텍스트 편집기에서 문자열 당 1 바이트 버전을 합리적으로 또는 실제로 표현할 수는 없지만 아무도 문자 당 1 바이트로 계산합니다. 언어 사양에 256 자 이하의 허용 문자가 있기 때문에?
COTO

@COTO 음과 1 바이트로 인코딩 된 파일을 해석 할 수있는 인코딩 이 존재 하기 때문 입니다. 사람들이 인코딩을 구성해야한다면 그 길을 기꺼이 내려갈 것이라고 생각하지 않습니다. 그렇지 않으면 257 미만의 고유 문자 를 사용 하는 모든 프로그램 이 주장 할 수 있습니다 (PPCG에 대한 거의 모든 대답 일 것입니다). 나는 우리가 수십 년 동안 유니 코딩을 포식하기 위해 APL을 처벌해서는 안된다고 생각합니다. 당시에는 니모닉처럼 작동하는 이상한 펑키 문자로 바이트를 해석하는 것이 합리적이었습니다.
Martin Ender

1
@COTO J는 APL을 기반으로하며 ASCII 문자 만 사용합니다. 그들은 일반적으로 비슷한 점수를 매기므로 유니 코드로 점수를 매겼더라도 아마 당신을 이길 것입니다. 그리고 골프 용으로 설계된 언어는 없으며 AFAIK는 실제로 전문적으로 사용됩니다. 그리고 여기 골프 문제는 녹색 체크 표시에 관한 것이 아니라, 언어 수단을 사용하여 프로그램의 마지막 작은 바이트를 짜내고 동일한 "무게 범주"로 모든 사람을 때리는 것에 관한 것입니다. 어쨌든 간결한 언어를 사용하는 것보다 ;)
Martin Ender

5

자바 스크립트-393 바이트

축소 :

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])

넓히는:

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]);

노트:

  • 기능을 정의 F필수 인수를 허용하고 필수 값을 리턴하는 를 합니다.
  • 입력 형식은 각 기본 요소의 정수 유형 코드가 튜플과 분리된다는 점을 제외하고 OP의 형식과 동일합니다. 예를 들어 F( 0,[[0,0],[2,2]], 0,[[1,0],[2,4]] )또는 F( 1,[[3,0],1], 2,[[0,0],1] ).
  • OP에 제공된 모든 테스트 사례에서 코드 검증
  • 길이가 0 인 선 세그먼트 및 반지름이없는 원을 포함하여 모든 모서리 및 모서리 케이스를 처리해야합니다.

아,이 두 가지 사례를 언급 해 주셔서 감사합니다. 반지름이 0 인 원은 처리 할 필요가 없으며 (포스트는 양의 반지름을 나타냄) 선 세그먼트의 끝이 명확하다는 점도 명확하게 설명합니다.
Martin Ender

4

파이썬, 284

나는이 모든 기하학적 트릭에 비해 예쁜 가비지 알고리즘을 사용하고 있지만 테스트 사례를 통과하는 데 1 분 이상이 걸리더라도 올바른 답변을 얻습니다. 가장 큰 장점은 세 가지 점수 함수 만 작성하면되고 언덕 등반은 모든 에지 사례를 처리한다는 것입니다.

골프 :

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

언 골프 드 :

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

그리고 마지막으로, 다른 누군가가 파이썬에서 이것을 시도하려는 경우의 테스트 스크립트 :

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.