Kota: Sightlines

18

Saya pada posisi (0, 0) dari kota dua dimensi yang tak terbatas, yang secara sempurna dibagi menjadi blok-blok yang berpusat pada setiap titik kisi, beberapa di antaranya berisi bangunan. Sebuah bangunan pada titik tertentu (x, y) menempati seluruh kotak dengan sudut yang berlawanan pada (x-.5, y-.5) dan (x + .5, y + .5) , termasuk perbatasannya. Sebuah bangunan terlihat jika ada beberapa ruas garis dari (0, 0) ke titik di gedung yang tidak bersinggungan dengan bangunan lain.

Misalnya, saya (yang @) dapat melihat 6 bangunan ( *) di kota berikut:

  *
 *
*
*@
x**
 *  y

Saya tidak dapat melihat bangunan yang ditandai dengan x, at (-1, -1) karena terhalang oleh dua yang berdekatan dengannya; atau yang ditandai dengan yat (3, -2) karena terhalang oleh tepi bangunan (1, -1) .

Memasukkan

String multiline, atau daftar garis, secara opsional diisi dengan spasi menjadi persegi panjang. Ini hanya akan berisi:

  • satu @(posisi saya)
  • Spasi
  • *, yang mewakili bangunan.

Akan selalu ada setidaknya satu bangunan, dan karena itu setidaknya satu bangunan yang terlihat.

Keluaran

Jumlah bangunan yang terlihat.

Uji kasus

*@
1

* *******
 @     * 
7

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

   *
  **
@ **
2

*      *
 *    * 
@
4

@
 *
  ***
1

Terima kasih kepada @Geobits untuk judulnya .

lirtosiast
sumber
Terkait
Martin Ender
Tentang uji kasus 3, dikelilingi oleh 8 * tetapi hasilnya adalah 4. Tapi sudut-sudut itu tampaknya tidak terhalang oleh bangunan lain. Apakah ada aturan untuk tidak memasukkan sudut?
LukStorms
1
@LukStorms Bayangkan setiap bintang sebenarnya adalah kubus, seperti di minecraft. Jika Anda berdiri di tengah-tengah itu, Anda hanya akan dapat melihat 4 blok
Biru
Apakah Anda akan berbaik hati menunggu sebelum saya memasukkan solusi golf saya (segera) sebelum memberikan hadiah? :)
Leif Willerts

Jawaban:

4

Unity + C #, 589 bytes

Ini mungkin bahasa terburuk untuk melakukan kode golf (baca: lebih buruk dari Java), tetapi Unity hadir dengan banyak fitur yang bermanfaat untuk tantangan ini.

EDIT: melewatkan beberapa spasi, mengembalikan panjang daftar, bukan melawan

Golf:

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;}}

Tidak Disatukan:

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;
    }
}

Saya menggunakan 3600 raycast karena gagal test case ke-5 dengan lebih rendah. Mungkin masih gagal untuk kasus uji yang lebih besar / lebih presisi.

Sayangnya, baik webgl dan desktop sepertinya rusak, jadi semua yang saya miliki adalah kode sumber untuk diuji dengan di github .

Biru
sumber
read: worse than JavaIni lebih pendek 383 byte dari solusi Java!
user8397947
@dorukayhan Maksud saya sebagian besar built-in lebih verbose daripada Java
Blue
Aku tidak tahu tentang C # tapi tidak bisa Anda mengganti total+=1dengan total++? Saya pikir cara lain untuk menyimpan beberapa karakter adalah dengan membuat kubus bangunan dan mengatur posisinya dalam satu pernyataan. Anda tampaknya tidak menggunakan kembali cubevariabel di mana pun.
Frozn
@Frozn Saya tidak benar-benar melakukan itu dalam kode golf saya
Biru
Hanya melihat kode dan melihat bahwa Anda mengubah penghitungan di sana. Saya selalu berasumsi bahwa versi golf hanya versi lama dari striped, lebih lama, tapi itu jelas tidak terjadi di sini. Mengenai bagian kedua: Saya pikir Anda lakukan. Ini GameObject b=GameObject.CreatePrimitive(PrimitiveType.Cube);b.transform.position=new Vector3(x,y);. Saya tidak tahu apakah itu mungkin di C # tetapi di Jawa orang bisa menulis GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position=new Vector3(x,y);.
Frozn
3

Java 8 lambda, 1506 1002 972 942 karakter

Saya ingin mengalahkan tantangan ini, karena sangat menarik. Hasilnya (tidak terlalu golf) dapat dilihat di sini:

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;}

Tentu saja ini juga ada dalam versi yang tidak di-serigala:

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;
    }
}

Jadi itu terlihat sangat sulit tetapi itu jauh lebih mudah daripada yang diperkirakan. Ide pertama saya adalah menggunakan beberapa algoritma persimpangan untuk memeriksa apakah garis dari posisi saya ke bangunan dapat dibuat tanpa persimpangan. Untuk melakukan ini, saya memutuskan untuk menggunakan algoritma Cohen-Sutherland dan menggambar garis ke keempat sudut bangunan. Ini bekerja cukup baik untuk tes pertama, tetapi yang terakhir gagal. Saya segera mengetahui, bahwa itu adalah kasus di mana Anda tidak dapat melihat sudut tetapi bagian dari tepi. Jadi saya berpikir tentang semacam casting ray seperti @Blue yang melakukannya. Saya menyingkirkan tantangan itu, karena saya tidak mendapatkan kemajuan. Lalu aku melihat jawaban Blue dan ide sederhana berikut muncul di benakku: Setiap bangunan menghalangi sudut di mana tidak ada hal lain yang bisa dilihat. Saya hanya perlu melacak apa yang bisa dilihat dan apa yang sudah disembunyikan oleh bangunan lain. Itu dia!

Algoritma bekerja sebagai berikut: Ini menentukan bangunan dengan jarak terkecil ke orang. Kemudian kita membayangkan empat garis yang ditarik dari orang tersebut ke sudut-sudut bangunan. Dua di antaranya memiliki nilai ekstrem: Sudut minimum dan maksimum di mana bangunan dapat dilihat. Kami mengambilnya sebagai jangkauan dan membandingkannya dengan bangunan lain yang kami tahu bahwa mereka dapat dilihat (tidak ada di awal). Rentang mungkin tumpang tindih, termasuk satu sama lain atau tidak menyentuh sama sekali. Saya membandingkan rentang dan mendapatkan beberapa rentang baru bangunan yang tidak disembunyikan oleh bangunan yang dapat dilihat. Jika ada sesuatu yang tersisa setelah membandingkannya dengan bangunan yang terlihat, bangunan tersebut juga dapat dilihat. Kami menambahkan rentang sudut yang tersisa ke daftar rentang untuk dibandingkan dan mulai dengan bangunan berikutnya dengan jarak yang lebih jauh berikutnya.

Terkadang rentang mungkin tumpang tindih dengan cara saya berakhir dengan kisaran 0 derajat. Rentang ini akan difilter agar tidak salah menambahkan bangunan yang bahkan tidak dapat dilihat.

Saya harap seseorang mengerti penjelasan ini :)

Saya tahu kode ini tidak terlalu banyak golf, saya akan segera melakukannya.

Itu adalah tugas yang sangat menantang. Anda pikir Anda menemukan solusi yang berfungsi tetapi Anda masih jauh. Saya pikir solusi ini cukup bagus. Itu tidak terlalu cepat tetapi setidaknya itu berfungsi;) Terima kasih untuk teka-teki itu!


Memperbarui

Saya menemukan waktu untuk golf semuanya menjadi satu fungsi, yang dengan demikian dapat diubah menjadi lambda. Semua fungsi hanya dipanggil sekali dan dengan demikian dapat dimasukkan ke dalam satu metode. Saya beralih dari daftar ke set karena ini menghemat beberapa karakter tambahan. Deklarasi telah disatukan. Perbandingan telah disatukan dan karakter digantikan oleh nilai ascii yang ada. Membandingkan rentang dapat dinyatakan sebagai banyak terner. Beberapa trik di sana-sini untuk mencegah ekspresi panjang seperti Double.NEGATIVE_INFINITY dilakukan. Jika memungkinkan, inline assigment dilakukan. Untuk menghemat lebih banyak, saya beralih dari membandingkan sudut dalam derajat ke membandingkan radian. Seluruh perubahan menyelamatkan lebih dari 500 karakter, saya berharap mendapatkan semuanya di bawah 1000;)

Saya menghapus generik jika memungkinkan dan mempersingkat perbandingan pengembalian dengan membuat array elemen satu dan memeriksa nilainya. Saya juga mengganti Double.NEGATIVE_INFINITY dengan PI2 dan -PI2 karena ini adalah batas atas dan bawah sudut. Sekarang akhirnya panjangnya kurang dari 1000 karakter!

Saya menggabungkan loop untuk menemukan lokasi orang dan iterator bangunan untuk menyimpan beberapa karakter. Sayangnya ini mengharuskan kita untuk memindahkan pengembalian dari loop dan masih menggunakan jeda tetapi kali ini tanpa label. Saya bergabung maxdan distanceSquareddan mindan newDistanceSquaredkarena mereka tidak diharuskan pada saat yang sama. Saya berubah Integer.MAX_VALUEmenjadi 2e31-1. Saya juga membuat konstanta half = 0.5yang digunakan untuk menghitung sudut-sudut bangunan. Ini lebih pendek dalam versi golf. Secara keseluruhan kami menyimpan 30 karakter lainnya!

Frozn
sumber
Golf yang bagus! Saya mengambil rute yang lebih mudah dengan semua raycasting bawaan, tetapi senang mengetahui bahwa saya membantu! (BTW saya mungkin akan berubah ke set juga)
Biru