Destruí-los com preguiçosos

21

Introdução

A arena é uma planície pontilhada de arranha-céus, que seus inimigos usam para se esconder. Você e seus inimigos se atiram com lasers. Todos vocês carregam jet packs, permitindo o vôo.

Quais inimigos você pode acertar com seu laser e quais estão escondidos?

Problema

Primeiro, o tamanho de uma arena é dado por um número inteiro nem uma única linha. As seguintes nlinhas contêm nnúmeros inteiros por linha separados por um espaço. Cada número inteiro representa a altura do edifício nesse local. Cada edifício é um sólido retangular, 1 unidade por 1 unidade por unidades de altura.

Em seguida, a sua localização é dada em uma única linha de três números de ponto flutuante x, y, z.

Finalmente, o número de inimigos é dado por um número inteiro mem uma única linha. As mlinhas a seguir contêm três números de ponto flutuante por linha, separados por um espaço. Estes representam os x, ye zcoordenadas de um inimigo. O sistema de coordenadas é definido da seguinte maneira:

  • x é medido da esquerda para a direita na entrada da cidade
  • y é medido de cima para baixo
  • z é medido a partir do zero

Para cada inimigo, se uma linha desobstruída puder ser traçada de você para esse inimigo, produza um número inteiro positivo . Caso contrário, imprima um número inteiro negativo . Separe as saídas com uma nova linha.

Entrada de amostra

Comentários, indicados por '#', estão presentes para ajudá-lo a ver rapidamente o que cada linha faz. Eles não estarão presentes na 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

Saída de amostra

Para a entrada de amostra acima, produzimos o seguinte:

-1
1
1

Suposições

  • 0 n<<100
  • 0 m<<100
  • 0 <= x<=n
  • 0 <= y<=n
  • 0 <= z<n
  • Os jogadores não estarão localizados dentro ou dentro de um canto, borda ou lateral de um edifício
  • Sua linha de visão para um inimigo nunca será tangente ao canto, borda ou lateral de um edifício
  • Um jogador não é uma obstrução
Rainbolt
fonte
Fico feliz em vê-lo fora da sandbox :)
Timtech
7
Se não posso destruir um inimigo, posso me juntar a eles?
John Dvorak
@ user80551 Desculpe, tive que reverter sua edição para o título porque o erro de ortografia foi intencional. Google it.
Rainbolt # 22/14
@Rusher Oh, desculpe, IDK isso
user80551
4
Veja também: youtube.com/watch?v=NKTpWi5itOM
qwr

Respostas:

5

Perl, 301 296 282

Edit 2: Na verdade, competição ou não, não há razão para não jogar um pouco mais. Teste online .

Edit: Parênteses parados, regex mais simples para verificar se há um número inteiro diferente de zero.

Com novas linhas e recuo para facilitar a leitura:

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

Requer 5.14por causa do argumento escalar (referência de matriz) para pop.

user2846289
fonte
Você pode explicar um pouco a sua solução? Ainda não testei e não inclinei o Perl, por isso alguns comentários seriam legais.
WorldSEnder
@ WorldSEnder, o esboço do algoritmo é o seguinte. A linha reta PEconecta dois pontos no espaço 3D, "Player" (X1Y1Z1) e "Inimigo" (X2Y2Z2). Sua projeção no (XY)plano cruza algumas das linhas de grade, isto é, números inteiros x = constou y = consttais como X1 < x < X2or Y1 < y < Y2(assumindo aqui que X1 < X2, por exemplo , mas não é importante). As coordenadas x ydessas interseções podem ser facilmente encontradas e, portanto, zcoordenadas de um ponto na PElinha também.
user2846289
(continuação) Por outro lado, para qualquer x ycoordenada, sabemos a altura hdo edifício (em vez disso, a altura máxima de até 4 edifícios que compartilham x ypontos). O inimigo pode ser morto se (e somente se) h < zpara todos os "pontos de interseção" mencionados acima. A implementação é uma aritmética básica, além de vários truques com o Perl para fins de golfe. Decifrar os detalhes de como eu fiz isso há um mês agora levará algum tempo :-).
user2846289
Argh, como eu vejo, há um bug na última (5a) revisão: índices para os elementos da @amatriz na grepexpressão devem aparecer na ordem em 0,3,0,4,1,5,2vez de 3,0,3,1,4,2,5- desculpe.
user2846289
OK, antes tarde do que nunca, e para terminar com tudo isso, aqui está a versão comentada.
precisa saber é o seguinte
3

Python 2.7 - 429 420 308 308 caracteres

Pensei nesse desafio mais como um problema de matemática do que um problema de golfe de código, portanto, não seja muito duro comigo se eu perder algumas otimizações óbvias. De qualquer forma, aqui está o 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)

Isso deve funcionar para casos de borda e canto (trocadilhos não intencionais) e é bastante sólido. Ouput para o exemplo fornecido:

-1
1
1

E aqui está uma explicação "curta":

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

Eu acho que isso é cheio de falhas. Btw salvei caracteres no aninhamento (o primeiro nível é um espaço, a segunda guia, depois uma guia e um espaço ...). Espero que depois de toda essa resposta possa apontar o caminho para fazê-lo.

WorldSEnder
fonte
Acabei de perceber que a entrada de amostra era inválida porque um dos inimigos estava localizado diretamente no chão, que é tecnicamente o topo de um edifício de altura zero, que prometi que não aconteceria. Seu envio passa no caso de teste corrigido, mas falha neste - ideone.com/8qn3sv . Você pode verificar meu caso de teste? Talvez esteja faltando alguma coisa ou talvez meu sistema de coordenadas não esteja claro.
Rainbolt # 22/14
Não, é só que o vetor está indo bem nas curvas ... agora eu sei porque você prometeu Assunção 6 e 7 :)
WorldSEnder
btw, eu saída de uma bóia negativo, mas que pode ser corrigido com 2 caracteres extras ( print 1-2*...em vez de print.5-...) por isso não é tão grande de uma diferença que eu acho
WorldSEnder
Você passou nos poucos testes que fiz. Bom trabalho! Você ainda deve seguir em frente e fazê-lo imprimir números inteiros para se manter alinhado com as especificações.
Rainbolt
11
Aceitando sua resposta até que alguém encontre uma solução melhor. Eu não acho que eles vão. Muito raramente alguém revisita velhos desafios resolvidos. Você deveria jogar mais! Parece que você conhece suas coisas. :)
Rainbolt
2

C - 2468

Não é um jogador de golfe, mas espero que seja um ponto de partida para implementações mais interessantes. A implementação de intersecté fortemente baseada em Adrian Boeing . Seu pseudo-código estava incompleto, mas sua explicação sobre a matemática era inestimável. A idéia básica é que você pegue uma linha do jogador até o alvo e prenda-a contra todas as paredes de cada prédio, atualizando o comprimento de cada parede. O comprimento restante é a parte dentro do edifício, portanto, se for zero, não haverá interseção.

#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
fonte
Tentei alguns casos de teste e você passou em todos eles. Aqui está a ideone que qualquer outra pessoa pode usar para verificar - ideone.com/MTXpzF
Rainbolt