城市:景点


18

我位于无限二维城市的位置(0,0),该城市被完美地分为以每个格点为中心的块,其中一些包含建筑物。某个点(x,y)上的建筑物占据了整个正方形,并且在(x-.5,y-.5)(x + .5,y + .5)处具有相对的角,包括边界。如果从(0,0)到建筑物中与其他建筑物不相交的点之间存在某些线段,则可以看到建筑物。

例如,我(@)可以看到*以下城市的6座建筑物():

  *
 *
*
*@
x**
 *  y

我看不到x(-1,-1)处标记有的建筑物因为它被相邻的两个障碍物遮挡了;或所述一个标有y(3,-2) ,因为它是由边缘阻碍(1,-1)的建筑物。

输入值

多行字符串或行列表,可以选择用空格填充到矩形中。它仅包含:

  • 一个@(我的位置)
  • 空间
  • *,代表建筑物。

总会有至少一栋建筑物,因此至少有一栋可见的建筑物。

输出量

可见建筑物的数量。

测试用例

*@
1

* *******
 @     * 
7

*****
**@**
*****
4

   *
  **
@ **
2

*      *
 *    * 
@
4

@
 *
  ***
1

感谢@Geobits的标题



关于测试用例3,它被8 *包围,但结果是4。但是这些角似乎没有被其他建筑物阻挡。有没有不包含角点的规则?
LukStorms

1
@LukStorms想象每个星星实际上都是立方体,就像在我的世界中一样。如果您站在其中途,您将只能看到4个街区
蓝色

在授予奖金之前,您是否愿意等到我进入高尔夫球解决方案之前(很快)?:)
Leif Willerts '16

Answers:


4

Unity + C#,589字节

这可能是编写代码时最糟糕的语言(阅读:比Java差),但是Unity带有很多有用的功能来应对这一挑战。

编辑:错过了几个空格,返回列表长度,而不是计数器

打高尔夫球:

using UnityEngine;using System.Collections;public class c:MonoBehaviour{public int h(string[]i){ArrayList k=new ArrayList();for(int y=0;y<i.Length;y++){char[]l=i[y].ToCharArray();int x=0;foreach(char c in l){if(c=='*'){GameObject b=GameObject.CreatePrimitive(PrimitiveType.Cube);b.transform.position=new Vector3(x,y);}if(c=='@')transform.position=new Vector3(x,y);x++;}}for(int n=0;n<3600;n++){RaycastHit h;Physics.Raycast(transform.position,Quaternion.Euler(0,0,n/10)*Vector3.up,out h);if(h.collider!=null){GameObject o=h.collider.gameObject;if(!k.Contains(o))k.Add(o);}}return k.Count;}}

取消高尔夫:

using UnityEngine;
using System.Collections;

public class citiessightlines : MonoBehaviour {

    public ArrayList todelete;   // Anything concerning this array just has to do with cleanup of 
                                 //objects for testing, and doesn't contribute to the byte count.
    void Start()
    {
        todelete = new ArrayList();
    }
    public int calcSight(string[]input)
    {
        todelete = new ArrayList();
        int total = 0;
        ArrayList check = new ArrayList();
        for (int y=0;y < input.Length; y++)
        {
            char[] line = input[y].ToCharArray();
            for (int x = 0; x < line.Length; x++)
            {
                char c = line[x];
                if (c == '*')
                {
                    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.position = new Vector3(x, y);
                    todelete.Add(cube);
                }
                if (c == '@')
                {
                    transform.position = new Vector3(x, y);
                }
            }
        }
        for (int angle=0; angle < 3600; angle++)
        {
            RaycastHit hit;
            Physics.Raycast(transform.position, Quaternion.Euler(0, 0, angle/10) * Vector3.up, out hit);
            if (hit.collider!=null)
            {
                GameObject hitObject = hit.collider.gameObject;
                if (!check.Contains(hitObject)&&hitObject!=this)
                {
                    total += 1;
                    check.Add(hitObject);
                }
           }
        }
        return total;
    }
}

我使用了3600次射线广播,因为它未能通过较低的第5个测试用例。对于更大/更精确的测试用例,它仍然可能会失败。

不幸的是,webgl和桌面版本似乎都中断了,所以我所拥有的只是在github上进行测试的源代码。


read: worse than Java这比Java解决方案 383个字节!
user8397947 '16

@dorukayhan我的意思是,大多数内置组件比Java更为冗长
Blue

我不知道C#,但不能代替你total+=1total++?我认为保存某些字符的另一种方法是创建建筑物的多维数据集并在一条语句中设置其位置。您似乎没有在cube任何地方重用该变量。
Frozn

@Frozn我实际上并没有在我的高尔夫球代码中这样做
Blue

只是看了一下代码,发现您更改了那里的计数。我一直认为高尔夫版本只是较长版本的空白版本,但显然不是这种情况。关于第二部分:我认为你同意。是GameObject b=GameObject.CreatePrimitive(PrimitiveType.Cube);b.transform.position=new Vector3(x,y);。我不知道在C#中是否可行,但是在Java中可以编写GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position=new Vector3(x,y);
Frozn

3

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个字符了!

我合并了用于查找人员位置的循环和用于保存某些字符的建筑物迭代器。不幸的是,这需要我们将返回值从循环中移出并仍然使用中断,但是这次没有标签。我合并了maxdistanceSquaredminnewDistanceSquared因为它们不是同时需要的。我更改Integer.MAX_VALUE2e31-1。我还创建了一个常数half = 0.5,用于计算建筑物的拐角。在高尔夫版本中这是较短的。总体而言,我们还保存了30个字符!


高尔夫不错!我通过所有内置的射线广播走了一条简单的路,但是很高兴知道我有所帮助!(顺便说一句,我也可能会更改为布景)
蓝色
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.