Java的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;}
当然,这在非高尔夫版本中也存在:
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度为范围的方式重叠。这些范围将被过滤,以免错误地添加甚至不可见的建筑物。
我希望有人能理解这个解释:)
我知道这段代码不是非常有用,我将尽快进行。
那是一项非常具有挑战性的任务。您以为找到了可行的解决方案,但您仍然遥不可及。我认为此解决方案效果很好。速度不是很快,但至少可以运行;)感谢您的困惑!
更新资料
我发现有时间将整个事情分解为一个功能,因此可以转化为lambda。所有函数仅被调用一次,因此可以放入一个方法中。我从列表切换到集合,因为这样可以节省一些其他字符。声明已经放在一起。比较结果已汇总在一起,字符已替换为ascii值。范围比较可以表示为许多三元。到处都有一些技巧可以防止像Double.NEGATIVE_INFINITY这样的长表达式。尽可能进行在线分配。为了节省更多,我从比较角度(度)到比较弧度。整个更改节省了超过500个字符,但我希望将所有字符减少到1000个以下;)
我尽可能删除了泛型,并通过创建一个单元素数组并检查其值来缩短返回比较。我还用PI2和-PI2替换了Double.NEGATIVE_INFINITY,因为它们是角度的上限和下限。现在终于不到1000个字符了!
我合并了用于查找人员位置的循环和用于保存某些字符的建筑物迭代器。不幸的是,这需要我们将返回值从循环中移出并仍然使用中断,但是这次没有标签。我合并了max
和distanceSquared
和min
,newDistanceSquared
因为它们不是同时需要的。我更改Integer.MAX_VALUE
为2e31-1
。我还创建了一个常数half = 0.5
,用于计算建筑物的拐角。在高尔夫版本中这是较短的。总体而言,我们还保存了30个字符!