Jardim do Programador

12

Jardim do Programador

Sendo um desenvolvedor de software profissional, você não pode se arriscar a se expor à luz não artificial do sol, mas também tem uma queda por flores e deseja manter seu jardim em boa forma o ano todo.

Para esse fim, um jardineiro é contratado todos os meses para arrumar o canteiro ao pé de sua casa. No entanto, você precisa garantir que o jardineiro esteja fazendo o trabalho corretamente e calcule um pagamento adequado para o colega que trabalha duro. Naturalmente, uma solução de software é a melhor.

Entrada

Seu programa será alimentado com informações que descrevem o canteiro como parece atual e detalhes dos itens que precisam ser removidos. O programa deve produzir o jardim sem a bagunça e imprimir uma repartição do pagamento dos jardineiros. A entrada pode ser do STDIN ou como um único argumento de linha de comando.

A primeira linha de entrada é do formato

width height unwanted_item_type_count

onde widthestá a largura do canteiro, heighté a altura do canteiro (ambos em caracteres ASCII) e unwanted_item_type_countinforma quantas linhas seguirão contendo uma descrição de um tipo de item a ser removido do jardim.

Cada linha para cada tipo de item indesejado tem o formato

width height string_representation name fee_per_item

onde widthé a largura do item, heighté a altura do item (ambos em caracteres ASCII), string_representationé a representação de string do item sem quebras de linha, nameé um identificador para o tipo de item (os espaços serão substituídos por sublinhados) e fee_per_itemé quanto o jardineiro deve ser pago pela remoção de cada tipo de item.

Por exemplo

3 2 .R.\|/ rouge_flower 3

Representa um tipo de item de nome rouge_flower, que custa 3 para remover e fica assim:

.R.
\|/

Os itens não conterão espaços e nenhum item poderá ter uma borda composta inteiramente por pontos, e a representação da sequência também terá o tamanho exato descrito. Assim, todos os itens a seguir são entradas inválidas:

3 1 ( ) space 0
1 1 . dot 0
2 1 .! bang 0
3 2 .@.\|/. plant 0

Observe que 0 é, no entanto, uma taxa válida (as taxas sempre serão números inteiros maiores que -1).

Observe também que o canteiro é predominantemente composto de pontos ( .) em vez de espaços, e você pode usar com segurança o espaço em branco como delimitação para todas as entradas. O canteiro é sempre delimitado pelos próprios pontos.

Depois que os tipos de itens indesejados são listados, vem a representação ASCII do canteiro de flores de determinada largura e altura.

Resultado

A saída deve ser STDOUT, ou alternativa adequada, se o seu idioma não suportar.

A saída começa com uma impressão do canteiro, mas com todos os itens indesejados removidos (substituídos por pontos), para que você possa ver como deve aparecer e verifique se o jardineiro fez seu trabalho. Cada item no canteiro será cercado por um retângulo de pontos e será um item contíguo (ou seja, não haverá pontos de separação dentro do item). Por exemplo

.....
.#.#.
.....

mostra 2 itens separados

.....
.\@/.
.....

mostra 1 item

......
.#....
....|.
....|.
.o--/.
......

é inválido, pois enquanto a pedra (#) pode ser correspondida, a cobra (você não poderia dizer que era uma cobra?) não pode porque a pedra interfere no contorno exigido dos pontos.

...
\@.
...

Isso também é inválido, pois o caracol está na borda do canteiro e a borda deve sempre ser delimitada por pontos em uma entrada válida.

Depois disso, deve haver uma lista de cada tipo de item indesejado, fornecendo a contagem, o custo por item e os custos de todos os itens (contagem * custo por item), no formato:

<count> <name> at <cost_per_item> costs <cost>

Depois disso, deve haver uma única linha que produza o custo total (a soma dos custos para itens indesejados):

total cost <total_cost>

Exemplo

Para esta entrada dada

25 18 3
4 2 .\/.\\// weeds 5
2 1 \@ snails 2
1 1 # stones 1
.........................
.\@/.................\@..
............\/...........
......O....\\//..^|^.....
.#...\|/.........^|^.....
..................|......
.................\|/.....
..\@.....\/...........#..
........\\//....#........
....*....................
...\|/......\/......\@/..
...........\\//..........
..................*......
.......\@/.......\|/.....
...O.....................
..\|/.......*............
.......#...\|/....\@.....
.........................

O programa deve produzir essa saída

.........................
.\@/.....................
.........................
......O..........^|^.....
.....\|/.........^|^.....
..................|......
.................\|/.....
.........................
.........................
....*....................
...\|/..............\@/..
.........................
..................*......
.......\@/.......\|/.....
...O.....................
..\|/.......*............
...........\|/...........
.........................
3 weeds at 5 costs 15
3 snails at 2 costs 6
4 stones at 1 costs 4
total cost 25

A saída deve ser finalizada por uma quebra de linha.

Este é o código-golfe, que ganhe o menor código.

Caso de teste adicional

Editar: isso costumava conter Unicode, o que não é permitido no canteiro, muito moderno. Isso foi corrigido, desculpe por isso.

25 15 5
5 3 ..@..\\|//.\|/. overgrown_plants 3
5 3 @-o....|...\|/. semi-articulated_plant 4
3 2 .|.\@/ mutant_plants 5
1 1 $ dollars 0
1 1 # stones 1
.........................
........@................
....$..\|/...........@...
............|.......\|/..
...#.......\@/...........
.........................
.........................
......@.......@......@...
.....\|/....\\|//...\|/..
.............\|/.........
.#....................#..
.........$.......|.......
...\/.......\/..\@/..\/..
..\\//.....\\//.....\\//.
.........................

Saída esperada:

.........................
........@................
.......\|/...........@...
....................\|/..
.........................
.........................
.........................
......@..............@...
.....\|/............\|/..
.........................
.........................
.........................
...\/.......\/.......\/..
..\\//.....\\//.....\\//.
.........................
1 overgrown_plants at 3 costs 3
0 semi-articulated_plants at 4 costs 0
2 mutant_plants at 5 costs 10
2 dollars at 0 costs 0
3 stones at 1 costs 3
total cost 16
VisualMelon
fonte
Podemos assumir que a caixa delimitadora de cada item indesejado é exata? Ou seja, nenhuma borda na descrição do item é totalmente pontilhada?
John Dvorak
@JanDvorak sim, isso parece uma restrição bastante razoável. Acrescentarei isso à pergunta e emprestarei sua redação na presunção de que você não se importará que eu faça isso.
VisualMelon
O caracol vai rastejar para outra direção também? \@e @/por exemplo .. Ou eles apontam eternamente para o oeste?
Han Soalone
@SickDimension, os itens indesejados devem ser correspondidos exatamente como estão descritos, rotações e inversões distintas não devem ser correspondidas. Isso não exclui a possibilidade de um caracol rastejar na outra direção, mas ninguém está sendo pago para removê-los no exemplo.
VisualMelon

Respostas:

3

Perl - 636

Definitivamente, há mais golfe que pode ser feito. E provavelmente melhores maneiras de fazê-lo também.

<>;while(<>){if(/ /){chomp;push@v,$_}else{$t.=$_}}for(@v){r(split/ /)}say$t.$y."total cost $u";sub r{my($e,$w,$c,$h,$z)=@_;($i,$f,$q,$d)=(1,0,0,"."x$e);@m=($c=~/($d)/g);@l=split/\n/,$t;while($i>0){($g,$j)=(1,0);for(0..$#l){if($j==0&&$l[$_]=~/^(.*?)\.\Q$m[$j]\E\./){$j++;$l="."x length$1}elsif($j<@m&&$l[$_]=~/^$l\.\Q$m[$j]\E\./){$j++}elsif($j>0){$l[$_-1]=~s!.\Q$m[$j-1]\E.!" ".$m[$j-1]=~s/\./ /gr." "!e;($j,$g)=(0,0)}if($j==@m){$k=$j;for($f=$_;$f>$_-$j;$f--){$k--;$o="."x length$m[$k];$l[$f]=~s/^($l)\.\Q$m[$k]\E\./$1.$o./}($g,$j)=(0,0);$q++}}if($g){$i--}}$t=join("\n",@l)."\n";$t=~s/ /./g;$p=$z*$q;$u+=$p;$y.="$q $h at $z costs $p\n"}

635 caracteres + 1 para a -Cbandeira manipular os euros.

Se você tiver a entrada armazenada, input.txtpoderá executá-la com:

cat input.txt | perl -C -E'<>;while(<>){if(/ /){chomp;push@v,$_}else{$t.=$_}}for(@v){r(split/ /)}say$t.$y."total cost $u";sub r{my($e,$w,$c,$h,$z)=@_;($i,$f,$q,$d)=(1,0,0,"."x$e);@m=($c=~/($d)/g);@l=split/\n/,$t;while($i>0){($g,$j)=(1,0);for(0..$#l){if($j==0&&$l[$_]=~/^(.*?)\.\Q$m[$j]\E\./){$j++;$l="."x length$1}elsif($j<@m&&$l[$_]=~/^$l\.\Q$m[$j]\E\./){$j++}elsif($j>0){$l[$_-1]=~s!\Q$m[$j-1]\E!$m[$j-1]=~s/\./ /gr!e;($j,$g)=(0,0)}if($j==@m){$k=$j;for($f=$_;$f>$_-$j;$f--){$k--;$o="."x length$m[$k];$l[$f]=~s/^($l)\.\Q$m[$k]\E\./$1.$o./}($g,$j)=(0,0);$q++}}if($g){$i--}}$t=join("\n",@l)."\n";$t=~s/ /./g;$p=$z*$q;$u+=$p;$y.="$q $h at $z costs $p\n"}'

Aqui está a versão deparsed. Passei por isso e adicionei alguns comentários para ajudar a explicar as coisas. Talvez eu torne os nomes das variáveis ​​mais legíveis em algum momento. Pode haver alguns casos extremos com os quais isso não funciona, mas funciona com os exemplos pelo menos.

BEGIN { # These are the features we get with -C and -E flags
    $^H{'feature_unicode'} = q(1); # -C gives us unicode
    $^H{'feature_say'} = q(1); # -E gives us say to save 1 character from print
    $^H{'feature_state'} = q(1);
    $^H{'feature_switch'} = q(1);
}
<ARGV>; # throw away the first line
while (defined($_ = <ARGV>)) { # read the rest line by line
    if (/ /) { # if we found a space (the garden doesn't have spaces in it)
        chomp $_; # remove the newline
        push @v, $_; # add to our array
    }
    else { # else, we construct the garden
        $t .= $_;
    }
}
foreach $_ (@v) { # call the subroutine r by splitting our input lines into arguments
    r(split(/ /, $_, 0)); # the arguments would be like r(3,2,".R.\|/","rouge_flower",3)
}
say $t . $y . "total cost $u"; # print the cost at the end

# this subroutine removes weeds from the garden and counts them
sub r {
    BEGIN {
        $^H{'feature_unicode'} = q(1);
        $^H{'feature_say'} = q(1);
        $^H{'feature_state'} = q(1);
        $^H{'feature_switch'} = q(1);
    }
    my($e, $w, $c, $h, $z) = @_; # get our arguments
    ($i, $f, $q, $d) = (1, 0, 0, '.' x $e); # initialize some variables
    @m = $c =~ /($d)/g; # split a string like this .R.\|/ into .R. and \|/
    @l = split(?\n?, $t, 0); # split the garden into lines to process line by line
    while ($i > 0) {
        ($g, $j) = (1, 0);
        foreach $_ (0 .. $#l) { # go through the garden
            if ($j == 0 and $l[$_] =~ /^(.*?)\.\Q$m[$j]\E\./) { # this matches the top part of the weed. \Q and \E make it so the weed isn't intepreted as a regex. Capture the number of dots in front of it so we know where it is
                ++$j;
                $l = '.' x length($1); # this is how many dots we have
            }
            elsif ($j < @m and $l[$_] =~ /^$l\.\Q$m[$j]\E\./) { # capture the next line
                ++$j;
            }
            elsif ($j > 0) { # if we didn't match we have to reset
                $l[$_ - 1] =~ s[.\Q$m[$j - 1]\E.][' ' . $m[$j - 1] =~ s/\./ /rg . ' ';]e; # this line replaces the dots next to the weed and in the weed with spaces
                # to mark it since it didn't work but the top part matches
                # that way when we repeat we go to the next weed
                ($j, $g) = (0, 0);
            }
            if ($j == @m) { # the whole weed has been matched
                $k = $j;
                for ($f = $_; $f > $_ - $j; --$f) { # remove the weed backwards line by line
                    --$k;
                    $o = '.' x length($m[$k]);
                    $l[$f] =~ s/^($l)\.\Q$m[$k]\E\./$1.$o./; 
                }
                ($g, $j) = (0, 0);
                ++$q;
            }
        }
        if ($g) {
            --$i; # all the weeds of this type are gone
        }
    }
    $t = join("\n", @l) . "\n"; # join the garden lines back together
    $t =~ s/ /./g; # changes spaces to dots 
    $p = $z * $q; # calculate cost
    $u += $p; # add to sum
    $y .= "$q $h at $z costs $p\n"; #get message
}

Sinta-se livre para sugerir melhorias!

hmatt1
fonte
Bom trabalho, e tenho medo de ter pecado, especifiquei que o canteiro seria ASCII e, em seguida, fiquei muito animado e coloquei Unicode no exemplo - vou mudar o exemplo para que seja apenas ASCII, desculpe por trabalhar para você.
VisualMelon
1
@VisualMelon, foi interessante aprender a fazer com que um one-liner trabalhe com unicode. Eu não sabia sobre a -Cbandeira antes disso. Vou deixá-lo lá para ser compatível de qualquer maneira, uma vez que é apenas uma diferença de 1 caractere.
precisa saber é o seguinte
0

Python 3, 459 bytes

    from re import*
E=input()
W,H,C=map(int,E[0].split())
B,T,O='\n'.join(E[~H:]),0,''
for L in E[1:~H]:
 w,h,s,n,c=L.split();w,h,c=map(int,(w,h,c));r,t='.'*(w+2),0;a=[r]+['.%s.'%s[i:i+w]for i in range(0,w*h,w)]+[r]
 for L in['(%s)'%'\\n'.join('.{%d})%s(.*'%(i,escape(b))for b in a)for i in range(W)]:t+=len(findall(L,B));B=sub(L,r.join('\\%d'%b for b in range(1,h+4)),B,MULTILINE)
 O+='%d %s at %d costs %d\n'%(t,n,c,t*c);T+=t*c
print(B+O+'total cost',T)

Assume que a entrada será fornecida como uma lista de strings.

Triggernometria
fonte
Eu gosto do ~Htruque; Não posso testar isso agora, mas vou tentar fazê-lo hoje mais tarde.
VisualMelon
Parece que não consigo fazer isso funcionar corretamente ( ValueError: not enough values to unpack (expected 3, got 1), python 3.6.6); você pode fornecer um link TIO ou alguma descrição sobre como executá-lo. Eu acho que pode estar distorcendo as regras, assumindo que a entrada está toda em uma linha, mas eu não fui totalmente claro sobre isso na questão, então não vou reclamar.
VisualMelon
@VisualMelon - bah, provavelmente copiei / pasta'd algo incorretamente. Não consigo acessar o TIO no trabalho, por isso vou verificar meu trabalho e postar um link ainda hoje.
Triggernometry