자바 8 람다, 1506 개 1002 972 942 문자
이 도전이 매우 흥미로워 서 이기고 싶었습니다. 결과는 (골프하지 않은) 여기에서 볼 수 있습니다 :
import java.util.*;f->{Set<double[]>B=new HashSet(),r,n;double a,M,m,P=Math.PI*2,z=.5;int x=0,y,v=0,i,j,c[],p,q,l=g.length;for(;x<l;x++)for(y=0;y<g[x].length;y++)if(g[x][y]>63)for(;;){c=new int[]{-1};M=2e31-1;for(i=0;i<l;i++)for(j=0;j<g[i].length;j++)if(g[i][j]==42)if((m=(p=x-i)*p+(q=y-j)*q)<M){M=m;c=new int[]{i,j};}if(c[0]<0)break;g[c[0]][c[1]]=0;double[]A={(a=Math.atan2((c[1]-=y)-z,(c[0]-=x)-z))<0?a+P:a,(a=Math.atan2(c[1]+z,c[0]-z))<0?a+P:a,(a=Math.atan2(c[1]+z,c[0]+z))<0?a+P:a,(a=Math.atan2(c[1]-z,c[0]+z))<0?a+P:a};r=new HashSet();M=-P;m=P;for(double d:A){M=d>M?d:M;m=d<m?d:m;}r.add(new double[]{m,M});for(double[]t:B){n=new HashSet();for(double[]h:r)for(double[]u:t[0]<h[0]?t[1]<h[0]?new double[][]{h}:t[1]<h[1]?new double[][]{{t[1],h[1]}}:new double[0][]:t[0]>h[1]?new double[][]{h}:t[1]>h[1]?new double[][]{{h[0],t[0]}}:new double[][]{{h[0],t[0]},{t[1],h[1]}})if(u[0]<u[1])n.add(u);r=n;}B.addAll(r);if(!r.isEmpty())v++;}return v;}
물론 이것은 ungolfed 버전에도 있습니다.
import java.util.*;
public class AngleCheck {
static int getViewableBuildingsC(char[][] grid) {
Set<double[]> blocked = new HashSet(), ranges, newRanges;
double angle, max, min, PI2 = Math.PI * 2, half = 0.5;
int x = 0, y, viewable = 0, i, j, building[], dX, dY, length = grid.length;
for (; x < length; x++) {
for (y = 0; y < grid[x].length; y++) {
if (grid[x][y] > 63) {
for (;;) {
building = new int[]{-1};
max = 2e31-1;
for (i = 0; i < length; i++) {
for (j = 0; j < grid[i].length; j++) {
if (grid[i][j] == 42) {
if ((min = (dX = x - i) * dX + (dY = y - j) * dY) < max) {
max = min;
building = new int[]{i, j};
}
}
}
}
if (building[0] < 0)
break;
grid[building[0]][building[1]] = 0;
double[] angles = {
(angle = Math.atan2((building[1] -= y) - half, (building[0] -= x) - half)) < 0 ? angle + PI2 : angle,
(angle = Math.atan2(building[1] + half, building[0] - half)) < 0 ? angle + PI2 : angle,
(angle = Math.atan2(building[1] + half, building[0] + half)) < 0 ? angle + PI2 : angle,
(angle = Math.atan2(building[1] - half, building[0] + half)) < 0 ? angle + PI2 : angle};
ranges = new HashSet();
max = -PI2;
min = PI2;
for (double d : angles) {
max = d > max ? d : max;
min = d < min ? d : min;
}
ranges.add(new double[]{min, max});
for (double[] reference : blocked) {
newRanges = new HashSet();
for (double[] currentRange : ranges) {
for (double[] subRange : reference[0] < currentRange[0] ?
reference[1] < currentRange[0] ?
// whole range after referencerange
new double[][]{currentRange}
:
reference[1] < currentRange[1] ?
// lower bound inside referencerange, but upper bound outside
new double[][]{{reference[1], currentRange[1]}}
:
// whole range inside referencerange -> nothing free
new double[0][]
:
// greater or equal lower bound
reference[0] > currentRange[1] ?
// whole range before referencerange
new double[][]{currentRange}
:
// ranges overlap
reference[1] > currentRange[1] ?
// range starts before and ends in reference range
new double[][]{{currentRange[0], reference[0]}}
:
// referencerange is in the range -> two free parts, one before, one after this
new double[][]{{currentRange[0], reference[0]}, {reference[1], currentRange[1]}}) {
if (subRange[0] < subRange[1])
newRanges.add(subRange);
}
}
ranges = newRanges;
}
blocked.addAll(ranges);
if (!ranges.isEmpty()) {
viewable++;
}
}
}
}
}
return viewable;
}
}
그래서 그것은 매우 어려워 보이지만 생각보다 쉽습니다. 첫 번째 아이디어는 교차 알고리즘을 사용하여 내 위치에서 건물까지의 선을 교차없이 만들 수 있는지 확인하는 것이 었습니다. 이를 위해 Cohen-Sutherland 알고리즘을 사용하기로 결정하고 건물의 네 구석에 선을 그렸습니다. 이것은 첫 번째 테스트에서 잘 작동했지만 마지막 테스트는 실패했습니다. 나는 곧 모서리를 볼 수 없지만 가장자리의 일부를 볼 수있는 경우라는 것을 알았습니다. @Blue와 같은 일종의 레이 캐스팅에 대해 생각했습니다. 나는 진전이 없었기 때문에 그 도전을 멀리했습니다. 그런 다음 Blue의 답변을 보았고 다음과 같은 간단한 아이디어가 떠 올랐습니다. 각 빌딩 블록은 볼 수없는 각도를 가지고 있습니다. 볼 수있는 것과 다른 건물에 의해 이미 숨겨져있는 것을 추적하면됩니다. 그게 다야!
알고리즘은 다음과 같이 작동합니다. 사람과의 거리가 가장 작은 건물을 결정합니다. 그런 다음 사람에서 건물 모서리까지 네 개의 선이 그려지는 것을 상상해보십시오. 이 중 두 가지는 극단적 인 가치를 지닙니다. 건물을 볼 수있는 최소 및 최대 각도. 우리는 그것들을 범위로 가져 와서 그들이 볼 수 있다는 것을 알고있는 다른 건물과 비교합니다 (처음에는 없음). 범위가 겹치거나 서로 포함되거나 전혀 닿지 않을 수 있습니다. 나는 범위를 비교하고 볼 수있는 건물에 의해 숨겨지지 않은 건물의 새로운 범위를 얻습니다. 통찰력있는 건물과 비교 한 후 남은 것이 있으면 건물도 볼 수 있습니다. 나머지 각도 범위를 범위 목록에 추가하여 다음 거리와 함께 다음 건물과 비교하고 시작합니다.
때로는 범위가 0 도의 범위에서 겹치는 방식으로 겹칠 수 있습니다. 이 범위는 볼 수없는 건물을 실수로 추가하지 않도록 필터링됩니다.
누군가 가이 설명을 이해하기를 바랍니다 :)
나는이 코드가 많이 골프되지 않는다는 것을 알고 있습니다.
그것은 정말 어려운 작업이었습니다. 당신은 당신이 효과가있는 해결책을 찾았다 고 생각했지만 대신 여전히 멀리 있습니다. 이 솔루션은 꽤 잘 작동한다고 생각합니다. 그것은 빠르지는 않지만 적어도 작동합니다.) 그 퍼즐에 감사드립니다!
최신 정보
나는 모든 것을 하나의 기능으로 골프화하는 시간을 발견했다. 따라서 람다로 바뀔 수있다. 모든 함수는 한 번만 호출되었으므로 하나의 메소드에 넣을 수 있습니다. 추가 문자를 저장하기 때문에 목록에서 세트로 전환했습니다. 선언이 합쳐졌습니다. 비교가 이루어지고 문자가 ASCII 값으로 대체되었습니다. 범위 비교는 많은 삼항으로 표현할 수 있습니다. Double.NEGATIVE_INFINITY와 같은 긴 식을 방지하기 위해 여기 저기 몇 가지 트릭이 있습니다. 가능한 경우 인라인 어설 션이 수행됩니다. 조금 더 절약하기 위해 각도를 각도로 비교하는 것에서 라디안을 비교하는 것으로 전환했습니다. 전체 변경 사항은 500자를 초과하지 않았으므로 1000 미만으로 줄 이길 바랍니다.)
가능한 경우 제네릭을 제거하고 하나의 요소 배열을 만들어 반환 비교를 단축하고 대신 값을 확인하십시오. 또한 Double.NEGATIVE_INFINITY를 각도의 상한 및 하한으로 PI2 및 -PI2로 바꿨습니다. 이제는 1000 자 미만입니다!
사람 위치와 건물 반복자를 찾기 위해 루프를 병합하여 일부 문자를 저장했습니다. 불행히도 이것은 루프에서 리턴을 이동시키고 여전히 레이블을 사용하지 않고 휴식을 사용해야합니다. 나는 합병 max
및 distanceSquared
및 min
및 newDistanceSquared
그들이 동시에 필요하지 않습니다한다. 로 변경 Integer.MAX_VALUE
했습니다 2e31-1
. 또한 half = 0.5
건물의 모서리를 계산하는 데 사용되는 상수 를 만들었습니다 . 골프 버전에서는 더 짧습니다. 전반적으로 우리는 또 다른 30자를 저장했습니다!