Área de um polígono ASCII

31

Você deve escrever um programa ou função que receba uma string representando um polígono asciótico como entrada e saída para retornar a área do polígono.

A entrada é uma sequência que consiste nos caracteres _ / \ L V spacee newlinedefine um polígono simples (o que significa que não há segmentos extras, nem toque automático nem interseção automática).

A área de uma célula de um caractere é 2

  • _divide a célula em tamanhos 0e2
  • \divide a célula em tamanhos 1e1
  • /divide a célula em tamanhos 1e1
  • Ldivide a célula em tamanhos 0e2
  • Vdivide a célula em tamanhos 1e 1(Os dois lados do Vsempre estarão no mesmo lado do polígono, para que sejam tratados juntos na lista.)

Cada caractere conecta os dois cantos de sua célula de caracteres que você espera (por exemplo, no canto superior esquerdo e no canto direito V).

Um exemplo com a área 7 ( 1+2+1na segunda linha e 1+1+1na terceira):

 _
/ \
V\/

Entrada

  • A entrada formará um retângulo, ou seja, haverá o mesmo número de caracteres entre as novas linhas.
  • Pode haver espaço em branco extra em qualquer lado do polígono.
  • A nova linha à direita é opcional.

Saída

  • Um único número inteiro positivo, a área do polígono.

Exemplos

As saídas estão após a última linha de suas entradas.

  _  
  V  

1

/L
\/

3



    /VV\
    L  /
     L/
14

  ____/\ 
  \    /
/\/   /
\____/

32  

   /V\
  /   \__ 
  \     /
/\/   /V
L____/

45

Este é o código-golfe, portanto a entrada mais curta vence.

randomra
fonte
seu terceiro exemplo deve ser 14
Optimizer
@Optimizer Obrigado, corrigido.
Aleatório
É a falta de ^ intencionalmente?
Robau
@ RobAu Sim, isso não parece bom o suficiente.
Random # 28/15

Respostas:

5

CJam, 48 43 29 bytes

qN-{i_9%2%U!^:U;J%D%1U2*?}%:+

Atualização : Jogava muito golfe usando matemática e o truque do estado * 2 da resposta do orlp.

Como funciona (desatualizado, atualizando em breve)

Dividimos a entrada na nova linha e, em seguida, para cada parte, mantemos um contador de ocorrências de caracteres de limite L\/. Este contador% 2 nos dirá qual das duas partições equivale a escolher para todos os caracteres. Então encontramos o índice de cada caractere na string L _. \/Vdará -1referência ao último elemento em uma matriz. Depois de obter o índice, usamos 4558Zb2/para criar a matriz [[2 0] [0 2] [0 2] [1 1]]e, em seguida, escolhemos a contagem correta usando o contador.

qN/0f{                                  }      e# Split the input on newline and for each
      \{                             }/        e# swap the 0 to back and for each char in
                                               e# the line, run this loop
        _"L _"#                                e# Copy the char and get index of it in
                                               e# this string "L _"
               4558Zb                          e# This is basically 4558 3base
                                               e# which comes to be [2 0 0 2 0 2 1 1]
                     2/=                       e# Group into pairs of 2 and choose the
                                               e# correct one.
                        2$=                    e# Based on the counter, choose the correct
                                               e# partition amount
                           @@"\/L"&,+          e# Increment the counter if the char is one
                                               e# of \, / and L
                                       ;       e# Pop the counter after each line loop
                                         :+    e# Sum all the numbers to get area

Experimente online aqui

Optimizer
fonte
22

Pyth, 47 46 45 36 30

FNs.zx=Z}N"\/L"aY|}N"\/V"yZ;sY

Explicação:

FNs.z            For every character in input, except newlines...
  x=Z}N"\/L"     Swap state if /, \, or L.
  aY|}N"\/V"yZ;  Append 1 if /, \, or V, else 2 times the state to Y.
sY               Sum Y and print.

Temos dois estados "no polígono" e "fora do polígono". Os seguintes caracteres fazem o seguinte ao lê-los da parte superior esquerda para a parte inferior direita:

/ \     swap state, add one to area
V                   add one to area
_ space             if in polygon, add two to area
L       swap state, if in polygon, add two to area

Observe que "adicione um à área" e "se estiver no polígono, adicione dois à área" são mutuamente exclusivos.

orlp
fonte
Estou realmente confuso sobre como x=funciona. Isso está documentado em algum lugar?
Jakube
@Jakube É atribuição aumentada.
orlp
@Jakube É como +=ou *=ou qualquer outra coisa. Nesse caso, xestá sendo usado como xor, portanto é exatamente o mesmo que o do Python ^=.
Isaacg
14

Retina , 293 + 15 = 308 314 385 bytes

;`\s
_
;`\\
/
;`.+
o$0iio
;+`(o(?=/.*(i)|L.*(ii)|V.*(io)|_)|i(?=/.*(io)|L.*(o)|_.*(ii)|V.*(i))).
$1$2$3$4$5$6$7$8
;`o
<empty>
;`ii$
#:0123456789
;+`^(?=i)(i*)\1{9}(?=#.*(0)|i#.*(1)|ii#.*(2)|iii#.*(3)|iiii#.*(4)|iiiii#.*(5)|iiiiii#.*(6)|iiiiiii#.*(7)|iiiiiiii#.*(8)|iiiiiiiii#.*(9))
$1#$2$3$4$5$6$7$8$9$10$11
:.*|\D
<empty>

Cada linha entra em um arquivo separado, então adicionei 13 à contagem de bytes. Como alternativa, você pode colocar tudo isso em um único arquivo como está e usar o -ssinalizador. O <empty>suporte para arquivos ou linhas realmente vazios.

Infelizmente, preciso de 187 bytes apenas para converter o resultado de unário para decimal. Eu acho que realmente deveria implementar isso em breve .

Explicação

Retina é uma linguagem baseada em regex (que eu escrevi exatamente para poder fazer coisas assim com regex). Cada par de arquivos / linhas define um estágio de substituição, sendo a primeira linha o padrão e a segunda linha a sequência de substituição. Os padrões podem ser precedidos por uma `cadeia de configuração delimitada, que pode conter os modificadores habituais de regex, além de algumas opções específicas da Retina. Para o programa acima, as opções relevantes são ;: suprime a saída desse estágio e +aplica a substituição em um loop até que o resultado pare de mudar.

A idéia da solução é contar cada linha separadamente, porque sempre podemos decidir pelos caracteres já encontrados se estamos dentro ou fora do polígono. Isso também significa que posso unir a coisa toda em uma única linha, porque o início e o fim de uma linha estão sempre fora do polígono. Também podemos observar que _e espaço são completamente idênticos para um algoritmo de varredura de linha, assim como \e /. Então, como um primeiro passo eu substituir todas as novas linhas e espaços, _e todos \por /simplificar algum código mais tarde.

Estou acompanhando o atual estado interno / externo com os personagens ie o, ao mesmo tempo, usando os is para registrar a área. Para fazer isso, começo acrescentando um oà linha unida para marcar que estamos fora do polígono. Também estou adicionando um iiono final da entrada, que usarei como uma pesquisa para gerar novos caracteres.

Então, o primeiro substituto grande simplesmente substitui um iou oseguido por um /V_Lcom o próximo conjunto de caracteres, inundando e contabilizando a coisa toda. A tabela de substituição é a seguinte, onde as colunas correspondem ao último caractere nessa linha e as linhas ao próximo caractere (onde Sé para espaço e <>para uma sequência vazia). Incluí todos os caracteres da entrada para mostrar as equivalências das quais já usei:

     i     o

/    io    i
\    io    i
L    o     ii
V    i     io
_    ii    <>
S    ii    <>

Observe que o caractere final sempre indica se, após o caractere, estamos dentro ou fora do polígono, enquanto o número de is corresponde à área que precisa ser adicionada ao polígono. Como exemplo, aqui estão os resultados das quatro primeiras iterações na entrada do último exemplo (isso foi gerado por uma versão antiga que realmente inundou cada linha separadamente, mas o princípio ainda é o mesmo):

o   /V\
o  /   \___
o  L     _/
o/\/   /V
oL__ _/
o   V

o  /V\
o /   \___
o L     _/
oi\/   /V
oii__ _/
o  V

o /V\
o/   \___
oL     _/
oiio/   /V
oiiii_ _/
o V

o/V\
oi   \___
oii     _/
oiioi   /V
oiiiiii _/
oV

oiV\
oiii  \___
oiiii    _/
oiioiii  /V
oiiiiiiii_/
oio

Por fim, acabo de me livrar de todos os osebras de linha removendo tudo o que corresponde [^i], e o restante é a conversão de decimal para unário, o que é bastante chato.

Martin Ender
fonte
4

Perl, 65 58 bytes

map{map{$b^=2*y,/\\L,,;$a+=y,/\\V,,||$b}split//}<>;print$a
  • Alterne $ b entre 0 e 2 ao ver / \ ou L.
  • Adicione 1 a $ a ao ver / \ ou V.
  • Adicione $ b a $ a ao ver mais alguma coisa.
Helios
fonte
Solução agradável, Perl é surpreendentemente compacto.
Orlp 28/04
1
Processamento de entrada pode ser simplificada para mais alguns ganhos:$/=\1;$-^=2*y,/\\L,,,$a+=y,/\\V,,||$-for<>;print$a
nutki
4

GNU sed, 290 + 1

O +1 é responsável pela -rmudança passada para sed. Comentários e espaços em branco adicionais não são contados na pontuação.

Não olhei em detalhes, mas acho que isso provavelmente é semelhante à resposta de Martin na Retina :

:                      # label to start processing next (or first) line
s/[0-9]//g             # remove the count of colons from previous lines
H                      # append the current line to the hold space
g                      # copy the hold space to the pattern space
y^_\\^ /^              # Replace '_' with ' ' and replace '\' with '/'
s/(\n| +$)//g          # strip newlines and trailing space
:o                     # start of "outside loop"
s/(^|:) *V/\1:/        # replace leading spaces and "V" with ":"
to                     #   if the above matches, stay outside
s/(^|:) *[|/]/\1:/     # replace leading spaces and "|" or "/" with ":"
ti                     #   if the above matches, go inside
s/(^|:) *L/\1::/       # replace leading spaces and "L" with "::"
:i                     # start of "inside" loop
s/: /:::/              # replace space with "::"
ti                     #   if the above matches, stay inside
s/:V/::/               # replace "V" with ":"
ti                     #   if the above matches, stay inside
s/:[|/]/::/            # replace "|" or "/" with ":"
to                     #    if the above matches, go outside
s/:L/:/                # remove "L"
to                     #    if the above matches, go outside
h                      # copy current string of colons to hold buffer
:b                     # start of colon count loop
s/:{10}/</g            # standard sed "arithmetic" to get string length
s/<([0-9]*)$/<0\1/
s/:{9}/9/
s/:{8}/8/
s/:{7}/7/
s/:{6}/6/
s/:{5}/5/
s/::::/4/
s/:::/3/
s/::/2/
s/:/1/
s/</:/g
tb                     # once arithmetic done, pattern buffer contains string length
N                      # append newline and next line to pattern buffer
b                      # loop back to process next line

visão global

  • Substitua cada unidade de área por dois pontos :
  • Conte o número de dois pontos

Notas

  • sed is line oriented so needs some work to process multiple lines at once. The N command does this by appending a newline then the next line to the current pattern space. The difficulty with N is that once it gets to the input stream EOF, it quits sed completely without any option to do further processing. To get around this, we count the current set of colons at the end of each line, just before reading in the next line.

Output:

$ echo '   /V\
  /   \__ 
  \     /
/\/   /V
L____/' |sed -rf polyarea.sed
45
$
Digital Trauma
fonte
3

C, 93 96 108 bytes

Editar: levou em conta as sugestões nos comentários, converteu o tempo em um loop for de instrução única e removeu completamente a variável "i".

int s,t;main(c,v)char**v;{for(;c=*v[1]++;t+=s+(c>46^!(c%19)^s))s^=c>13^c%9>4;printf("%d",t);}

Post original:

Parecia um problema divertido e simples o suficiente para finalmente criar uma conta aqui.

main(c,v)char**v;{int i,t,s;i=t=s=0;while(c=v[1][i++]){s^=c>13^c%9>4;t+=s+(c>46^!(c%19)^s);}printf("%d",t);}

O texto do polígono deve ser passado como o primeiro argumento da linha de comando; isso deve funcionar com ou sem qualquer quantidade de novas linhas / espaços em branco.

Isso apenas lê no polígono, um caractere de cada vez, s alterna atualmente dentro ou fora do polígono em '/', 'L' ou '\', e t aumenta 1 em '/', 'V', e '\', ou por 2 se dentro / 0 se fora em 'L', '_', espaço e nova linha.

This is my first time trying my hand at any sort of "golfing" (or C, to the extent it differs from C++), so any criticisms are appreciated!

Jonathan Aldrich
fonte
Welcome and good job! You may be able to skip the i=t=s=0; I think C initializes all ints to 0 anyway. Also, see if you can turn the while loop into a for loop; that often saves a few bytes.
Ypnypn
Using the idea of the for loop above I think you can do something like this: ...int i,t,s;for(i=t=s=0;c=v[1][i++];t+=s+(c>46^!(c%19)^s))s^=c>13^c%9>4;... which should save 4 bytes; one {, one } and two ;
DaedalusAlpha
Also as mentioned above, apparently global variables are automatically set to 0, so if int i,t,v; was to be put in front of main instead of inside we could get rid of i=t=s=0 altogether saving another 7 bytes.
DaedalusAlpha
3

POSIX sed, 245 244

POSIX sed, no extensions or extended regexps. Input is limited to the maximum hold space size of sed - POSIX mandates at least 8192; GNU manages more. This version assumes that there will be no blank lines before or after the shape; an extra 10 bytes of code, indicated in the expansion, can accommodate that if it's a requirement (original question doesn't specify).

H
/^\([L\\]_*\/\|V\| \)*$/!d
x
s/[_ ]/  /g
s/^/!/
s/$/!/
:g
s/\([^V]\)V/V\1/
tg
y/V/ /
s/L/!  /g
s,[/\\], ! ,g
s/![^!]*!//g
:d
/ /{
s/     /v/g
s/vv/x/g
/[ v]/!s/\b/0/2
s/  /b/g
s/bb/4/
s/b /3/
s/v /6/
s/vb/7/
s/v3/8/
s/v4/9/
y/ bvx/125 /
td
}

Expanded and annotated

#!/bin/sed -f

# If leading blank lines may exist, then delete them
# (and add 8 bytes to score)
#/^ *$/d

# Collect input into hold space until we reach the end of the figure
# The end is where all pieces look like \___/ or V
H
/^\([L\\]_*\/\|V\| \)*$/!d

x

# Space and underscore each count as two units
s/[_ ]/  /g

# Add an edge at the beginning and end, so we can delete matching pairs
s/^/!/
s/$/!/
# Move all the V's to the beginning and convert each
# to a single unit of area
:gather
s/\([^V]\)V/V\1/
tgather
y/V/ /

# L is a boundary to left of cell; / and \ in middle
s/L/!  /g
s,[/\\], ! ,g

# Strip out all the bits of outer region
s/![^!]*!//g

# Now, we have a space for each unit of area, and no other characters
# remaining (spaces are convenient because we will use \b to match
# where they end).  To count the spaces, we use roman numerals v and x
# to match five and ten, respectively.  We also match two (and call
# that 'b').  At the end of the loop, tens are turned back into spaces
# again.
:digit
/ /{
s/     /v/g
s/vv/x/g
/[ v]/!s/\b/0/2
s/  /b/g
s/bb/4/
s/b /3/
s/v /6/
s/vb/7/
s/v3/8/
s/v4/9/
y/ bvx/125 /
tdigit
}

# If trailing blank lines may exist, then stop now
# (and add 2 bytes to score)
#q
Toby Speight
fonte
1

C, 84 bytes

a;i;f(char*s){for(;*s;a+=strchr("\\/V",*s++)?1:i+i)i^=!strchr("\nV_ ",*s);return a;}

Mudamos de lado sempre que vemos \, /ou L; sempre adicionamos um para \\, /ou V, mas adicionamos 2 (se dentro) ou 0 (se fora) para espaço, nova linha Lou _.

As variáveis ae iestão a ser assumida zero na entrada - que deve ser redefinido se a função está a ser chamado mais de uma vez.

Ungolfed:

int a;                          /* total area */
int i;                          /* which side; 0=outside */
int f(char*s)
{
    while (*s) {
        i ^= !strchr("\nV_ ",*s);
        a += strchr("\\/V",*s++) ? 1 : i+i;
    }
    return a;
}

Programa de teste:

#include <stdio.h>
int main()
{
    char* s;
    s = "  _  \n"
        "  V  \n";
    printf("%s\n%d\n", s, f(s));
    a=i=0;

    s = "/L\n"
        "\\/\n";
    printf("%s\n%d\n", s, f(s));
    a=i=0;


    s = "    /VV\\\n"
        "    L  /\n"
        "     L/";
    printf("%s\n%d\n", s, f(s));
    a=i=0;

    s = "  ____/\\ \n"
        "  \\    /\n"
        "/\\/   /\n"
        "\\____/";
    printf("%s\n%d\n", s, f(s));
    a=i=0;

    s = "   /V\\\n"
        "  /   \\__ \n"
        "  \\     /\n"
        "/\\/   /V\n"
        "L____/";
    printf("%s\n%d\n", s, f(s));
    a=i=0;

    return 0;
}
Toby Speight
fonte