Destrúyelos con lazers

21

Introducción

La arena es una llanura salpicada de rascacielos, que tus enemigos usan para cubrirse. Tú y tus enemigos se disparan con láser. Todos ustedes llevan mochilas propulsoras, permitiendo el vuelo.

¿A qué enemigos puedes golpear con tu láser y cuáles se esconden?

Problema

Primero, el tamaño de una arena está dado por un número entero nen una sola línea. Las siguientes nlíneas contienen nenteros por línea separados por un espacio. Cada número entero representa la altura del edificio en esa ubicación. Cada edificio es un sólido rectangular, 1 unidad por 1 unidad por unidades de altura.

A continuación, su ubicación se da en una sola línea como tres números de punto flotante x, y, z.

Finalmente, el número de enemigos viene dado por un número entero men una sola línea. Las siguientes mlíneas contienen tres números de coma flotante por línea separados por un espacio. Estos representan el x, yy las zcoordenadas de un enemigo. El sistema de coordenadas se define de la siguiente manera:

  • x se mide de izquierda a derecha en la entrada de la ciudad
  • y se mide de arriba a abajo
  • z se mide desde cero

Para cada enemigo, si se puede trazar una línea sin obstrucciones hacia ese enemigo, genera un número entero positivo . De lo contrario, genera un entero negativo . Salidas separadas con una nueva línea.

Entrada de muestra

Los comentarios, indicados con '#', están presentes para ayudarlo a ver rápidamente qué hace cada línea. No estarán presentes en la entrada real.

5              # Size of the map
0 0 0 0 0      # Buildings
0 0 0 0 0      # Buildings
4 4 4 4 4      # Buildings
0 0 0 0 0      # Buildings
0 0 0 0 0      # Buildings
2.5 0.0 4.0    # Your location
3              # Number of enemies
2.5 5.0 0.1    # Enemy location
2.5 5.0 5.0    # Enemy location
0.0 2.7 4.5    # Enemy location

Salida de muestra

Para la entrada de muestra anterior, mostramos lo siguiente:

-1
1
1

Supuestos

  • 0 n<<100
  • 0 m<<100
  • 0 <= x<=n
  • 0 <= y<=n
  • 0 <= z<n
  • Los jugadores no estarán ubicados dentro o dentro de una esquina, borde o costado de un edificio
  • Su línea de visión hacia un enemigo nunca será tangente a la esquina, el borde o el costado de un edificio.
  • Un jugador no es una obstrucción.
Perno de lluvia
fuente
Me alegra verlo fuera de la caja de arena :)
Timtech
77
Si no puedo destruir a un enemigo, ¿puedo unirme a ellos?
John Dvorak
@ user80551 Lo siento, tuve que revertir su edición al título porque la falta de ortografía fue intencional. Buscalo en Google.
Rainbolt
@Rusher Oh, lo siento, IDK que
user80551
44
Relacionado: youtube.com/watch?v=NKTpWi5itOM
qwr

Respuestas:

5

Perl, 301 296 282

Edición 2: En realidad, competencia o no, no hay razón para no jugar un poco más. Pruébelo en línea .

Editar: Par de paréntesis desaparecidos, expresiones regulares más simples para verificar si hay un número entero distinto de cero.

Con nuevas líneas y sangría para facilitar la lectura:

sub i{<>=~/\S+/g}
@b=map[i],@r=0..<>-1;
print.1<=>(map{
    @a[1,0,2,4,3]=@a;
    @b=map{$i=$_;[map$b[$_][$i],@r]}@r;
    grep$a[3]
        &&($k=(($x=$_)-$a[0])/$a[3])**2<=$k
        &&pop[sort map@{$b[$_]}[$x-!!$x,$x],
                   ($_=$a[1]+$k*$a[4]),$_-/^\d+$/]
           >=$a[2]+$k*$a[5]
    ,@R=@r
}@a=map$_-shift@v,i,@u=@v=@$_),$/for([i])x<>

Requiere 5.14debido al argumento escalar (referencia de matriz) a pop.

usuario2846289
fuente
¿Puedes explicar tu solución un poco? No lo he probado y todavía no he prestado a Perl, por lo que algunos comentarios serían buenos.
WorldSEnder
@WorldSEnder, el esquema del algoritmo es el siguiente. La línea recta PEconecta dos puntos en el espacio tridimensional, "Jugador" (X1Y1Z1) y "Enemigo" (X2Y2Z2). Su proyección en el (XY)plano cruza algunas de las líneas de la cuadrícula, es decir, enteros x = consto y = constcomoX1 < x < X2 o Y1 < y < Y2(suponiendo aquí que X1 < X2, por ejemplo , pero no es importante). Las coordenadas x yde estas intersecciones se pueden encontrar fácilmente y, por lo tanto, también las zcoordenadas de un punto en PElínea.
user2846289
(continuado) Por otro lado, para cualquier x ycoordenada, conocemos la altura hdel edificio (más bien, la altura máxima de hasta 4 edificios que comparten el x ypunto). Se puede disparar al enemigo si (y solo si) h < zpara todos los "puntos de intersección" mencionados anteriormente. La implementación es una aritmética básica, así como varios trucos con Perl para jugar al golf. Descifrar los detalles de cómo lo hice hace un mes llevará algún tiempo ahora :-).
user2846289
Argh, como veo, hay un error en la última (quinta) revisión: los índices de los elementos de la @amatriz en grepexpresión deberían aparecer en el orden0,3,0,4,1,5,2 lugar de 3,0,3,1,4,2,5- lo siento.
usuario2846289
OK, más vale tarde que nunca, y para terminar con todo esto, aquí está la versión comentada.
user2846289
3

Python 2.7 - 429 420 308 308 caracteres

Pensé en este desafío más como un problema matemático que como un problema de código de golf, así que no seas demasiado duro conmigo si pierdo algunas optimizaciones obvias. De todos modos, aquí está el código:

b=lambda:raw_input().split()
m=map
d=range(input())
h=[m(int,b())for _ in d]
x,y,z=m(float,b())
for e,f,g in[m(float,b())for _ in[1]*input()]:o=lambda x,y,u,v,i,j:i<=x+u/v*(j+1-y)<=i+1<[]>z+(g-z)/v*(j+1-y)<=max(h[i][j:j+2])if v else 0;print 1-2*any(o(x,y,e-x,f-y,j,i)+o(y,x,f-y,e-x,i,j)for j in d for i in d)

Esto debería funcionar para casos extremos y de esquina (juego de palabras involuntario) y es bastante sólido. Ouput para el ejemplo proporcionado:

-1
1
1

Y aquí hay una explicación "corta":

fast_read = lambda : raw_input().split() # define a helper
# m = map another helper
grid_range = range(input())
houses = [map(int, fast_read()) for _ in grid_range]
# 'map(int,...)' is a shorter version of '[int(a) for a in ...]'
pos_x,pos_y,pos_z = map(float, fast_read()) # read the player position
# the following loops through all enemy coordinates
for ene_x, ene_y, ene_z in [map(float,fast_read()) for _ in[1]*input()]:
    vec_z = ene_z - pos_z
    # is_hit macro uses vector math to detemine whether we hit a specific wall
    # wallhit -> 1
    # no wallhit -> 0
    is_hit = lambda pos_x, pos_y, vec_x, vec_y, co_x, co_y:\
        (co_x <= pos_x + vec_x/vec_y * (co_y + 1 - pos_y) <= co_x + 1 # check if hit_x is good
        < [] > # an effective and
        pos_z + (ene_z - pos_z)/vec_y * (co_y + 1 - pos_y) <= max(houses[co_x][co_y:co_y + 2]) # check if hit_z is good
        if vec_y else 0) # if vec_y is 0 we can't hit the wall parallel to y
    print (.5 - # can hit -> 0.5 - 0 = 0.5, hit -> 0.5 - 1 = -0.5
            any( # if we hit any wall
                # we swap x and y-coordinate because we read them "incorrect"
                is_hit(pos_x, pos_y, ene_x-pos_x, ene_y-pos_y, cur_y, cur_x) # check for hit in x-direction
                + # effective 'or'
                is_hit(pos_y, pos_x, ene_y-pos_y, ene_x-pos_x, cur_x, cur_y) # check for hit in y-direction
                    for cur_y in grid_range # loop y
                for cur_x in grid_range)) # loop x

Supongo que esto está lleno de defectos. Por cierto, guardé caracteres al anidar (el primer nivel es un espacio, el segundo una pestaña, luego una pestaña y un espacio ...). Espero que después de todo esta respuesta pueda señalar la forma de hacerlo.

WorldSEnder
fuente
Me acabo de dar cuenta de que la entrada de muestra no era válida porque uno de los enemigos estaba ubicado directamente en el suelo, que técnicamente es la parte superior de un edificio de altura cero, lo que prometí que no sucedería. Su envío pasa el caso de prueba corregido, pero falla este: ideone.com/8qn3sv . ¿Puedes revisar mi caso de prueba? Podría estar perdiendo algo o tal vez mi sistema de coordenadas no está claro.
Rainbolt
No, es solo que el vector está pasando por las esquinas ... ahora sé por qué prometiste la Asunción 6 y 7 :)
WorldSEnder
por cierto, produzco un flotador negativo, pero eso se puede arreglar con 2 caracteres adicionales (en print 1-2*...lugar de print.5-...) Así que no es una gran diferencia, supongo
WorldSEnder
Pasaste las pocas pruebas que se me ocurrieron. ¡Buen trabajo! Todavía debe seguir adelante y hacer que imprima enteros para mantenerse en línea con la especificación.
Rainbolt
1
Aceptar su respuesta hasta que alguien encuentre una mejor solución. No creo que lo hagan. Muy raramente alguien vuelve a visitar viejos desafíos resueltos. ¡Deberías jugar más al golf! Parece que sabes tus cosas. :)
Rainbolt
2

C - 2468

No se juega golf en absoluto, pero espero que sea un punto de partida para implementaciones más interesantes. La implementación de intersectse basa en gran medida de Adrian Boeing . Su pseudocódigo estaba incompleto, pero su explicación de las matemáticas era invaluable. La idea básica es tomar una línea desde el jugador hasta el objetivo y recortarla contra todas las paredes de cada edificio, actualizando la longitud de cada pared. La longitud restante es la porción dentro del edificio, por lo que si es cero, no hubo intersección.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    float x;
    float y;
    float z;
} vec3;

float
dot(vec3 a, vec3 b)
{
    return a.x * b.x + a.y * b.y + a.z * b.z;
}

vec3
scale(float s, vec3 a)
{
    vec3 r;
    r.x = s * a.x;
    r.y = s * a.y;
    r.z = s * a.z;
    return r;
}

vec3
add(vec3 a, vec3 b)
{
    vec3 r;
    r.x = a.x + b.x;
    r.y = a.y + b.y;
    r.z = a.z + b.z;
    return r;
}

int
intersect(vec3 a, vec3 b, vec3 *normals, vec3 *points, int nnormals)
{
    vec3 ab = add(b, scale(-1, a));
    float tfirst = 0;
    float tlast = 1;
    int i;
    for(i = 0; i < nnormals; i++)
    {
        float d = dot(normals[i], points[i]);
        float denom = dot(normals[i], ab);
        float dist = d - dot(normals[i], a);
        float t = dist / denom;
        if(denom > 0 && t > tfirst)
        {
            tfirst = t;
        }
        else if(denom < 0 && t < tlast)
        {
            tlast = t;
        }
    }
    return tfirst < tlast ? 1 : 0;
}

const vec3 N = {0,-1,0};
const vec3 S = {0,1,0};
const vec3 W = {-1,0,0};
const vec3 E = {1,0,0};
const vec3 D = {0,0,-1};

int
main(void)
{
    vec3 normals[5];
    vec3 player;
    vec3 *targets;
    int i;
    int j;
    vec3 *buildings;
    vec3 *b;
    int nbuildings = 0;
    int n;
    int m;
    char line[300];
    normals[0] = N;
    normals[1] = S;
    normals[2] = W;
    normals[3] = E;
    normals[4] = D;
    fgets(line, 300, stdin);
    n = atoi(line);
    /*5 sides for each building*/
    buildings = calloc(n * n * 5, sizeof(*buildings));
    b = buildings;
    for(i = 0; i < n; i++)
    {
        char *z;
        fgets(line, 300, stdin);
        for(j = 0; j < n && (z = strtok(j ? NULL : line, " \n")) != NULL; j++)
        {
            vec3 bottom;
            vec3 top;
            if(z[0] == '0') continue;
            nbuildings++;
            bottom.x = j;
            bottom.y = i;
            bottom.z = 0;
            top.x = j + 1;
            top.y = i + 1;
            top.z = atoi(z);
            b[0] = top;
            b[1] = bottom;
            b[2] = top;
            b[3] = bottom;
            b[4] = top;
            b += 5;
        }
    }
    fgets(line, 300, stdin);
    player.x = atof(strtok(line, " "));
    player.y = atof(strtok(NULL, " "));
    player.z = atof(strtok(NULL, " \n"));
    fgets(line, 300, stdin);
    m = atoi(line);
    targets = calloc(m, sizeof(*targets));
    for(i = 0; i < m; i++)
    {
        int hit = 1;
        fgets(line, 300, stdin);
        targets[i].x = atof(strtok(line, " "));
        targets[i].y = atof(strtok(NULL, " "));
        targets[i].z = atof(strtok(NULL, " \n"));
        for(j = 0; j < nbuildings; j++)
        {
            b = &buildings[j * 5];
            if(intersect(player, targets[i], normals, b, 5) == 1)
            {
                hit = 0;
                break;
            }
        }
        printf("%d\n", hit ? 1 : -1);
    }
    free(buildings);
    free(targets);
    return 0;
}
laindir
fuente
Probé algunos casos de prueba y los pasaste todos. Aquí está la ideona que cualquier otra persona puede usar para verificar: ideone.com/MTXpzF
Rainbolt