Città: linee di mira


18

Sono nella posizione (0, 0) di una città infinita bidimensionale, che è perfettamente divisa in blocchi centrati in ciascun punto reticolare, alcuni dei quali contengono edifici. Un edificio in un determinato punto (x, y) occupa l'intero quadrato con angoli opposti in (x-.5, y-.5) e (x + .5, y + .5) , compreso il suo bordo. Un edificio è visibile se c'è un segmento di linea da (0, 0) a un punto nell'edificio che non interseca nessun altro edificio.

Ad esempio, I (il @) può vedere 6 edifici ( *) nella seguente città:

  *
 *
*
*@
x**
 *  y

Non riesco a vedere l'edificio contrassegnato da un x, a (-1, -1) perché è ostruito dai due adiacenti ad esso; o quello contrassegnato con un yat (3, -2) perché è ostruito dal bordo dell'edificio (1, -1) .

Ingresso

Una stringa multilinea, o un elenco di linee, facoltativamente riempito con spazi in un rettangolo. Conterrà solo:

  • un singolo @(la mia posizione)
  • spazi
  • *, che rappresentano gli edifici.

Ci sarà sempre almeno un edificio, e quindi almeno un edificio visibile.

Produzione

Il numero di edifici visibili.

Casi test

*@
1

* *******
 @     * 
7

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

   *
  **
@ **
2

*      *
 *    * 
@
4

@
 *
  ***
1

Grazie a @Geobits per il titolo .



Riguardo al caso di test 3, è circondato da 8 * ma il risultato è 4. Ma quegli angoli non sembrano essere bloccati da altri edifici. Esiste una regola per non includere gli angoli?
LukStorms

1
@LukStorms Immagina che ciascuna delle stelle sia in realtà un cubo, come in Minecraft. Se ti trovassi in mezzo, vedresti solo 4 isolati
Blue

Saresti così gentile da aspettare prima che io entri (molto presto) nella mia soluzione golf prima di assegnare la taglia? :)
Leif Willerts,

Risposte:


4

Unity + C #, 589 byte

Questo è probabilmente il linguaggio peggiore in cui giocare a golf (leggi: peggio di Java), ma Unity ha molte utili funzioni per questa sfida.

EDIT: ha perso un paio di spazi, restituisce la lunghezza della lista, non il contatore

golfed:

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

Ungolfed:

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

Ho usato 3600 raycast perché non riesce il 5 ° caso di test con inferiore. Potrebbe non riuscire ancora per casi di test ancora più grandi / più precisi.

Sfortunatamente, entrambe le build di webgl e desktop sembrano rompersi, quindi tutto quello che ho è il codice sorgente con cui testare su github .


read: worse than JavaQuesto è 383 byte in meno rispetto alla soluzione Java!
user8397947

@dorukayhan Voglio dire che per la maggior parte dei built-in sono più dettagliati di Java
Blue

Non so di C # ma non potresti sostituirlo total+=1con total++? Penso che un altro modo per salvare alcuni personaggi sia quello di creare il cubo dell'edificio e impostarne la posizione in un'istruzione. Sembra che non riutilizzi la cubevariabile da nessuna parte.
Frozn,

@Frozn In realtà non lo sto facendo nel mio codice golf
Blue

Ho appena guardato il codice e ho visto che lì hai cambiato il conteggio. Suppongo sempre che la versione golfata sia solo una versione spogliata di quella bianca più lunga, ma ovviamente non è questo il caso qui. Per quanto riguarda la seconda parte: penso che tu lo faccia. Lo è GameObject b=GameObject.CreatePrimitive(PrimitiveType.Cube);b.transform.position=new Vector3(x,y);. Non so se sia possibile in C # ma in Java si potrebbe GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position=new Vector3(x,y);invece scrivere .
Frozn,

3

Java 8 lambda, 1506 1002 972 942 caratteri

Volevo vincere questa sfida, in quanto è molto interessante. Il risultato (non molto da golf) può essere visto qui:

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

Ovviamente questo esiste anche nella versione 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;
    }
}

Quindi sembra molto difficile ma è molto più facile di quanto si possa pensare. La mia prima idea è stata quella di utilizzare un algoritmo di intersezione per verificare se una linea dalla mia posizione all'edificio può essere fatta senza intersezioni. Per fare questo ho deciso di utilizzare l'algoritmo Cohen-Sutherland e tracciare linee per tutti e quattro gli angoli dell'edificio. Questo ha funzionato abbastanza bene per i primi test, ma l'ultimo ha fallito. Ho presto scoperto che è un caso in cui non puoi vedere gli angoli ma una parte di un bordo. Quindi ho pensato a una sorta di ray casting come ha fatto @Blue. Ho messo via quella sfida, poiché non ho ottenuto alcun progresso. Poi ho visto la risposta di Blue e mi è venuta in mente la seguente semplice idea: ogni blocco di costruzione ha un angolo in cui nient'altro può essere visto. Devo solo tenere traccia di ciò che può essere visto e ciò che è già nascosto da altri edifici. Questo è tutto!

L'algoritmo funziona come segue: determina l'edificio con la distanza minima dalla persona. Quindi immaginiamo quattro linee tracciate dalla persona agli angoli dell'edificio. Due di questi hanno un valore estremo: l'angolo minimo e massimo in cui è possibile vedere l'edificio. Li prendiamo come un intervallo e li confrontiamo con altri edifici di cui sappiamo che possono essere visti (nessuno all'inizio). Gli intervalli possono sovrapporsi, includersi a vicenda o non toccare affatto. Confronto le gamme e ottengo alcune nuove gamme dell'edificio che non sono nascoste dagli edifici visualizzabili. Se rimane qualcosa dopo averlo confrontato con gli edifici in vista, anche l'edificio è visibile. Aggiungiamo l'intervallo di angoli rimanenti all'elenco di intervalli da confrontare e iniziamo con l'edificio successivo con la distanza più lunga successiva.

A volte gli intervalli possono sovrapporsi in modo da finire con un intervallo di 0 gradi. Questi intervalli verranno filtrati per non aggiungere erroneamente un edificio che non è nemmeno visualizzabile.

Spero che qualcuno abbia capito questa spiegazione :)

So che questo codice non è molto giocato a golf, lo farò al più presto.

È stato un compito davvero impegnativo. Pensavi di aver trovato una soluzione che funzionasse ma invece sei ancora lontana. Penso che questa soluzione funzioni abbastanza bene. Non è molto veloce ma almeno funziona;) Grazie per quel puzzle!


Aggiornare

Ho trovato il tempo di giocare a golf in un'unica funzione, che può essere trasformata in lambda. Tutte le funzioni sono state chiamate solo una volta e quindi possono essere inserite in un metodo. Sono passato dagli elenchi ai set in quanto ciò salva alcuni caratteri aggiuntivi. Le dichiarazioni sono state messe insieme. I confronti sono stati messi insieme e i personaggi sono stati sostituiti da lì valore ASCII. Il confronto dei range può essere espresso come molti ternari. Alcuni trucchi qua e là per impedire espressioni lunghe come Double.NEGATIVE_INFINITY sono state fatte. Laddove possibile, vengono eseguite le assegnazioni in linea. Per risparmiare un po 'di più sono passato dal confronto degli angoli in gradi al confronto dei radianti. L'intero cambiamento ha salvato oltre 500 caratteri, spero di ottenere tutto meno di 1000;)

Ho rimosso i generici ove possibile e ho abbreviato il confronto di ritorno creando un array di un elemento e verificando invece il suo valore. Ho anche sostituito Double.NEGATIVE_INFINITY con PI2 e -PI2 in quanto questi sono i limiti superiore e inferiore degli angoli. Ora è finalmente meno di 1000 caratteri!

Ho unito i loop per trovare la posizione delle persone e l'iteratore dell'edificio per salvare alcuni personaggi. Sfortunatamente questo ci impone di spostare il ritorno fuori dal ciclo e di usare ancora una pausa, ma questa volta senza un'etichetta. Mi sono unito maxe distanceSquarede mine newDistanceSquaredpoiché non sono richiesti allo stesso tempo. Ho cambiato Integer.MAX_VALUEin 2e31-1. Inoltre ho creato una costante half = 0.5che viene utilizzata per calcolare gli angoli dell'edificio. Questo è più breve nella versione golf. Nel complesso abbiamo salvato altri 30 personaggi!


Nice Golf! Ho preso un percorso più semplice con tutto il raycasting integrato, ma è bello sapere che ho aiutato! (A proposito probabilmente cambierò anche nei set)
Blue
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.