Otimize os tubos de ensaio ASCII

13

Você recebe vários tubos de ensaio ASCII, sua tarefa é reduzir o número de tubos de ensaio usados.

Cada tubo de ensaio é assim:

|  |
|  |
|  |
|~~|
|  |
|  |
|  |
|  |
|__|

Obviamente, ~~é o nível da água. O tubo de ensaio também pode estar vazio; nesse caso, não há ~~caracteres dentro. O tubo único pode conter até 8 unidades de nível de água.

Você recebe um número finito de tubos de ensaio com diferentes níveis de água no interior. Você precisa derramar a água na menor quantidade possível de tubos de ensaio e produzir o resultado.

|  | |  | |  | |  |         |~~| |  |
|  | |  | |  | |  |         |  | |  |
|  | |~~| |  | |  |         |  | |  |
|~~| |  | |  | |  |         |  | |~~|
|  | |  | |  | |  | ------> |  | |  |
|  | |  | |  | |  |         |  | |  |
|  | |  | |~~| |  |         |  | |  |
|  | |  | |  | |  |         |  | |  |
|__| |__| |__| |__|         |__| |__|

 05 + 06 + 02 + 00  ------>  08 + 05

Como você pode ver, os tubos de ensaio são separados com espaço único. Tubos vazios não devem ser mostrados na saída. Este é o código golf, então o código com o menor número de bytes vence.

Casos de teste: http://pastebin.com/BC0C0uii

Feliz golfe!

Jacajack
fonte
Também podemos redistribuir a água? Por exemplo, 7 + 6 seria uma saída válida para o seu exemplo?
Martin Ender
@ MartinEnder Você deve usar a menor quantidade possível de tubos. Eu acho que é aceitável neste caso.
Jacajack
@StewieGriffin eu não vi nada parecido aqui ainda, então se isso é uma duplicata meio Sinto muito
Jacajack
O espaço em branco à direita é permitido?
PurkkaKoodari
Better title - "Otimizador de bebês com tubo de ensaio ASCII"
Optimizer

Respostas:

4

Pitão, 48 45 44 bytes

jCssm+_B,*9\|_X+\_*8;ld\~*9;cUs*L/%2w\~_S8 8

Experimente online.

Imprime um único espaço à direita em cada linha.

PurkkaKoodari
fonte
4

JavaScript (ES6), 159 148 bytes

s=>s.replace(/~~|\n/g,c=>1/c?i++:n+=7-i,n=i=-1)&&`012345678`.replace(/./g,i=>`|${g(+i)}| `.repeat(n>>3)+`|${g(~n&7^i)}|
`,g=i=>i?i>7?`__`:`  `:`~~`)

Produz um avanço de linha à direita. Editar: salvou 11 bytes com alguma ajuda do @Arnauld.

Neil
fonte
s.replace(/~~/g,(_,i)=>n+=9-i/s.indexOf`\n`|0,n=0)deve salvar 4 bytes. Você pode querer inicializar n para -1 e usar n>>3e ~n&7^isalvar mais um byte.
Arnauld
@ Arnauld Obrigado pela -1ideia, mas pude aprimorá -la replace.
Neil
1
Agradável! Eu nunca percebi que 1/"\n"era verdade.
Arnauld
@ Arnauld Bem, foi apenas um byte extra de cereja no topo do bolo ...
Neil
3

Perl, 150 bytes

149 bytes de código + -nsinalizador.

$l+=9-$.for/~~/g}if($l){$%=($v=$l/8)+($r=$l!=8);say"|~~| "x$v.($@="|  | ")x$r;say$:=$@x$%for$l%8..6;say$@x$v."|~~|"x$r;say$:for 2..$l%8;say"|__| "x$%

Não vou explicar todo o código, apenas algumas coisas:
$l+=9-$.for/~~/gconta quanta água há na entrada.
A segunda parte do código imprime a saída. A idéia é colocar o maior número possível de tubos totalmente cheios e um último que contenha a água que resta (se houver). Assim, o algoritmo está em 4 partes: imprime a primeira linha de água (o topo de tubos): say"|~~| "x$v.($@="| | ")x$r. Em seguida, imprima partes vazias de tubos até chegar ao nível de água do último tubo: say$:=$@x$%for$l%8..6. Em seguida, imprima o nível onde a água último tubo é: say$@x$v."|~~|"x$r. Em seguida, imprima todos os restantes níveis de "vazio": say$:for 2..$l%8;. E, finalmente, imprimir a linha de fundo: say"|__| "x$%.
Os nomes de variáveis tornam difícil de ler ( $%, $@, $:), mas permite a palavras-chave como xefor para ser escrito após a variável sem espaço.

Para executá-lo:

perl -nE '$l+=9-$.for/~~/g}if($l){$%=($v=$l/8)+($r=$l!=8);say"|~~| "x$v.($@="|  | ")x$r;say$:=$@x$%for$l%8..6;say$@x$v."|~~|"x$r;say$:for 2..$l%8;say"|__| "x$%' <<< "|  | |  | |  | |  |
|  | |  | |  | |  |
|  | |~~| |  | |  |
|~~| |  | |  | |  |
|  | |  | |  | |  |
|  | |  | |  | |  |
|  | |  | |~~| |  |
|  | |  | |  | |  |
|__| |__| |__| |__| "

Não estou muito satisfeito com a duração dessa resposta. Tentei tirar o melhor proveito do meu algoritmo, mas uma abordagem diferente provavelmente poderia ser mais curta. Vou tentar trabalhar em breve.

dada
fonte
@JamesHolderness Tentei todos os casos de teste (e tentei novamente agora porque você estava me duvidando) e isso me parece bom. "O último" é aquele com 3 tubos: 2 com nível de água em 4 e 1 com água em nível 2, certo? Se assim for, então eu tentei e dá a mesma saída que a pastbin #
Dada
@JamesHolderness Oh, certo, isso explica muito! Obrigado :)
Dada
3

Befunge, 144 138 bytes

9>1-00p>~$~2/2%00gv
 |:g00_^#%4~$~$~+*<
$< v01!-g01+*8!!\*!\g00::-1</8+7:<p01-1<9p00+1%8-1:_@#:
_ ~>g!-1+3g:"|",,," |",,:>#^_$55+,10g:#^_@

Experimente online!

As duas primeiras linhas processam a entrada, basicamente ignorando tudo, exceto o primeiro caractere em cada tubo que pode ser um marcador de nível. Pegamos o valor ASCII desse caractere, dividimos por 2 e mod 2 (fornecendo 1 ou 0, dependendo de estarmos em um marcador de nível ou não), multiplicamos pelo número da linha (contagem decrescente de 8, fornecendo-nos o valor do nível para esse tubo) e adicione-o a um total em execução.

A saída é tratada nas duas segundas linhas, começando essencialmente na extremidade direita da terceira linha. Primeiro calculamos o número de tubos tomando o nível total de água mais 7 dividido por 8. Em seguida, ao iterar sobre as linhas de todos os tubos, calculamos o caractere a ser exibido dentro de um tubo específico ( t , contando até 0) por um dada linha ( r , contagem decrescente de 8 a 0) da seguinte forma:

last_level = (total_water - 1)%8 + 1
level      = last_level*!t + 8*!!t
char_type  = !(level - r) - !r

O char_type calculado é -1 para a linha mais baixa (a base do tubo), 0 para qualquer outra área que não esteja no nível da água e 1 para o nível da água. Portanto, ele pode ser usado como uma simples pesquisa de tabela para o caractere apropriado a ser produzido (você pode ver esta tabela no início da linha 4).

James Holderness
fonte
2

Haskell, 186 bytes

import Data.Lists
z=[8,7..0]
f x|s<-sum[i*length j-i|(i,j)<-zip z$splitOn"~~"<$>lines x],s>0=unlines$(\i->(#i)=<<(min 8<$>[s,s-8..1]))<$>z|1<2=""
l#i|i==l="|~~| "|i<1="|__| "|1<2="|  | "

Exemplo de uso:

*Main> putStr $ f "|  | |  | |  | |  |\n|  | |  | |  | |  |\n|  | |~~| |  | |  |\n|~~| |  | |  | |  |\n|  | |  | |  | |  |\n|  | |  | |  | |  |\n|  | |  | |~~| |  |\n|  | |  | |  | |  |\n|__| |__| |__| |__|"
|~~| |  | 
|  | |  | 
|  | |  | 
|  | |~~| 
|  | |  | 
|  | |  | 
|  | |  | 
|  | |  | 
|__| |__| 

Coloca um espaço à direita em cada linha. Como funciona:

              lines x      -- split the input string at newlines             
      splitOn"~~"<$>       -- split every line on "~~"
    zip z                  -- pair every line with its water level, i.e.
                           -- first line = 8, 2nd = 7 etc.
   [i*length j-i|(i,j)   ] -- for each such pair take the number of "~~" found
                           -- times the level
 s<-sum                    -- and let s be the sum, i.e. the total amount of water

  s>0                      -- if there's any water at all

          [s,s-8..1]       -- make a list water levels starting with s
                           -- down to 1 in steps of 8
       min 8<$>            -- and set each level to 8 if its greater than 8
                           -- now we have the list of water levels for the output
  \i->(#i)=<<(  )<$>z      -- for each number i from 8,7..0 map (#i) to the
                           -- list of output water levels and join the results
unlines                    -- join output lines into a single string (with newlines)

l#i                        -- pick a piece of tube:
                           --  |__|  if l==0
                           --  |~~|  if l==i
                           --  |  |  else



  |1<2=""                  -- if there's no water in the input, return the
                           -- empty string

A principal dor foi a falta de uma função que conta com que frequência uma substring ocorre em uma string. Há countno Data.Text, mas importá-lo leva a um monte de conflitos de nomes que são muito caros para resolver.

nimi
fonte
1

Python, 261 bytes

i=input().split('\n')
t=0
R=range(9)[::-1]
for n in R:t+=i[n].count('~')/2*(8-n)
m=t%8
e=t/8
o=t/8+1
T='|~~| '
b='|  | '
B='|__| '
n='\n'
if m:
 print T*e+b
 for n in R:
    if n==m:print b*e+T
    else:print b*o
 print B*o
elif t<1:1
else:print T*e+(n+b*e)*7+(n+B)*e

Sinto que sinto falta de algo. Além disso, se várias linhas novas forem aceitáveis ​​para a saída em branco, eu poderia perder alguns bytes. Toma entrada como '| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n| | | | | |\n|__| |__| |__|'.

nedla2004
fonte
1

Ruby , 139 bytes

(138 bytes de código mais um byte para o -n)

n||=0;b=gsub(/~~/){n=n+9-$.}[0,5];END{8.times{|i|puts (b*(n/8)).tr(?_,i>0??\ :?~)+(n%8>0?b.tr(?_,(8-i==n%8)??~:?\ ):"")};puts b*((n+7)/8)}

Experimente online!

Algumas explicações:

Este programa requer a -ntroca.

n - contador de água.

b- Modelo para construção de tubos; é igual a"|__| "

i - Índice de linha atual durante a construção do tubo.

gsub(/~~/){... }- Abusa gsubsimplesmente de contar o nível da água. gsubna verdade se expande para Kernel.gsub, o que é equivalente a $_.gsub!. Isso manipula desnecessariamente a linha atual ( $_); no entanto, permite uma atribuição mais concisa de b=... em [0,5]vez de b=$_[0,5].

n=n+9-$.- Para medir o nível da água, a expressão usa a variável predefinida $., que carrega o número da linha de entrada atual . Isso me permitiu perder a variável de loop explícita.

b=gsub(/~~/){}[0,5]- armazena em cache a parte inferior do tubo mais à esquerda como um modelo. (Parece-me um pouco com o padrão “Elefante no Cairo” porque a linha de fundo vence).
Como o fundo do tubo nunca mostra água, gsubele não substituirá nada quando estivermos lá; portanto, no final, bsempre é igual "|__| ".

END{... }- É chamado depois que todo o fluxo de entrada foi processado. Eu uso essa fase para construir os tubos alvo.

i>0??\ :?~- é simplesmente uma abreviação de i > 0 ? " " : "~".

Atualização 1: Adicionados detalhes sobre variáveis, gsubtruques e END{}fase.

Atualização 2: (± 0 bytes no total)

  • Use em n||=0vez de n=n||0 (-1 byte)
  • Tomou malus por -n (+1 byte)
Synoli
fonte
0

Python 3, 404 bytes

Este programa cria a saída total pretendida com os níveis de água nos formatos ASCII e numérico.

w,x,y=[],[],[];a,b,s=" ------> ","~","";y=input().split("\n")
for i in [i for i in zip(*y) if "_" in i][::2]:w+=[8-i.index(b)] if b in i else [0]
u=sum(w)
while u:x+=[[8],[u]][u<8];u-=x[-1]
for i,k in enumerate(y):
    s+=k+"%s"%[a," "*9][i!=4]
    for j,l in enumerate(x):
        c=["  ","__"][i==8];s+="|%s| "%(c,b*2)[l==8-i]
    s+="\n"
s+="\n"
for i in w:s+=" %02d  "%i
s+="\b"+a
for i in x:s+=" %02d  "%i
print(s)
dfernan
fonte