Colocação de baú de Minecraft


20

O videogame Minecraft é sobre colocar e remover diferentes tipos de blocos na rede inteira 3D que compõe o mundo virtual. Cada ponto de rede pode conter exatamente um bloco ou estar vazio (um bloco " aéreo " oficialmente). Neste desafio, nos preocuparemos apenas com um plano 2D horizontal do mundo 3D e um tipo de bloco: baús .

Baús permitem que os jogadores armazenem itens. Quando dois baús são ortogonais adjacentes no mesmo plano horizontal, suas texturas se ligam e um baú duplo com o dobro da capacidade. Nada maior que um baú duplo pode ser feito; não há baús triplos nem quádruplos.

Um bloqueio torácico só pode ser colocado em um ponto de treliça vazio se seus quatro pontos ortogonais adjacentes estiverem todos vazios ou se exatamente um contiver um bloqueio torácico que ainda não faz parte de um baú duplo. Essas regras de posicionamento garantem que nunca haja ambiguidade sobre quais blocos de peito estão vinculados para formar baús duplos.

Por exemplo, suponha que .haja espaço vazio e Cum baú: (os números também são espaço vazio e apenas para fins de identificação).

.......C..
.1.C2.C3..
........5C
.CC4..CC..
..........
  • Um baú pode ser colocado no ponto 1 porque seus 4 vizinhos estão vazios.
  • Um baú pode ser colocado no ponto 2 porque o baú vizinho ainda não faz parte de um baú duplo.
  • Um baú não pode ser colocado no ponto 3 porque haveria ambiguidade sobre como o baú duplo se forma.
  • Um baú não pode ser colocado no ponto 4 porque o baú vizinho já faz parte de um baú duplo.
  • Um baú pode ser colocado no ponto 5. O baú duplo na diagonal não afeta nada.

Assumindo que a área além da grade está vazia, alterar cada uma .da grade para a *se um baú puder ser colocado lá resulta:

******.C**
***C**C.**
*..***..*C
.CC.*.CC.*
*..***..**

Nem todos os *espaços podem ser ocupados com baús ao mesmo tempo, é claro, mas se você tivesse apenas um baú, ele poderia ser colocado em qualquer um deles.

Desafio

Escreva um programa ou função que leva em um .e Cgrade, e muda a cada .um *se um peito poderia ser colocado lá, imprimir ou retornar a grade resultante.

  • A entrada pode ser de stdin ou um arquivo ou como um argumento de string para uma função.

  • Você pode presumir que a entrada está bem formada - ou seja, uma grade de texto perfeitamente retangular, com pelo menos 1 caractere de largura e altura, contendo apenas .e COpcionalmente, você pode assumir que há uma nova linha à direita após a última linha (e pode haver uma na saída )

  • Você pode assumir que o arranjo de baús na entrada é consistente com as regras acima. Nunca haverá ambiguidades sobre quais baús formam baús duplos.

  • Se desejar, você pode usar qualquer uma das três distintas ASCII imprimíveis caracteres no lugar de ., Ce *. Você não pode usar outra coisa no lugar de novas linhas.

  • Todos os baús são baús normais. Baús não presos ou baús ender .

Pontuação

O envio com o menor número de bytes vence.

Para um desafio relacionado ao Minecraft um pouco mais desafiador, tente o Nether Portal Detection .


5
Do ponto de vista da Minecrafting, eu achei isso muito chato no jogo.
Ainda

Ao receber a entrada da grade de stdin ou um argumento de cadeia única, é aceitável considerar as dimensões da grade como uma entrada adicional? ou precisa ser deduzido de novas linhas e comprimento de string?
Level River St

@steveverrill Tem que ser inferido.
Hobbies de Calvin

Por curiosidade, por que todas as respostas, inclusive a minha, têm um voto negativo? Só posso supor que é a mesma pessoa, eles gostariam de explicar?
Level River St

Para um desafio extra, pode-se escrever um programa para encontrar o local ideal para os baús; isto é, encontre uma configuração que permita que o número máximo de baús adicionais seja colocado sem violar as regras, mesmo entre os novos baús.
AJMansfield

Respostas:


11

CJam, 82 76 66 62 58 54 bytes

qN/::~4{[8_]f/[9_]f*z{[{1$8-g)+}*]W%}%}*{_8<\2<8?}f%N*

O formato de entrada espera 0para célula de ar e 8para uma célula torácica. A saída contém 1todas as células que podem ser colocadas com um baú.

UPDATE : Corrigido um erro. Aumentado em 3 bytes :( golfed more :). 4 bytes salvos graças ao @ Sp3000

Exemplo de entrada:

0000000800
0008008000
0000000008
0880008808
0000000000

Saída:

1111110811
1110018010
1008800108
0880088008
1008800110

Acho que já terminei de jogar golfe ...

Explicação

qN/::~                   "This part converts the input into array of integer array";
qN/                      "Split input on new line";
   ::~                   "Parse each character in each row as integer";

4{[8_]f/[9_]f*z{[{1$8-g)+}*]W%}%}*

4{   ...z{       W%}%}*  "Run the logic 4 times, first, columns in correct order, then,";
                         "columns in reverse order, then for rows";
  [8_]f/[9_]f*           "Convert adjacent chests represented by two 8 into two 9";
                         "This happens for all the rows in the columns iterations and";
                         "for all the columns in the rows iterations";
  {               }%     "For each row/column";
   [{        }*]         "Reduce and wrap it back in the array";
     :I8-                "Store the second number in I, remove 8 from it";
         g               "Do signum. Now we have -1 for < 8 number, 0 for 8 and 1 for > 8";
          )+I            "Increment to get 0, 1 & 2. Add it to first number and put I back";

{_8<\2<8?}f%N*           "This part converts the output from previous iterations";
                         "to 3 character based final output and prints it";
{        }f%             "Map each row using the code block";
 _8<   8?                "If the value is greater than 7, make it 8, else:";
    \2<                  "If the value is greater than 1, make it 0, else 1";
            N*           "Join the arrays using new line";

Experimente online aqui


8

Regex do .NET ( Retina ), 434 416 310 + 1 = 311 bytes

Após o último desafio que respondi na regex (o Desafio do Portal Nether vinculado a este desafio), finalmente decidi escrever uma ferramenta de linha de comando, que atua como intérprete para expressões regulares no estilo .NET, para poder responder perguntas com regex sem ser desafiado por não ser um idioma independente. Eu chamei de Retina.

Agora, esse desafio não se presta muito bem a um envio de regex, mas eu só precisava usar o Retina agora. ;) (Além disso, o Sp3000 me desafiou a fazê-lo no chat.) Então, aqui está:

Arquivo Regex

m`(?<=(?=.(.)*).*)(?<=((?<=(?<2>C|C(?(1)!)(\n|(?<-1>.))*)?)C(?=(?<2>C|(\n|(?<-1>.))*(?(1)!)C)?)(()(?(6)!)|(?<=^(?(7)!)(?<-7>.)*C).*\n(.)*()(?(8)!)))?){2}_(?=(?<2>((?(10)!)()|(?(11)!)()(.)*\n.*(?=C(?<-12>.)*(?(12)!)$))(?<=(?<2>C|C(?(1)!)(\n|(?<-1>.))*)?)C(?=(?<2>C|(\n|(?<-1>.))*(?(1)!)C)?))?){2}(?<-2>)?(?(2)!)

Arquivo de substituição

*

O arquivo regex é basicamente apenas o regex, exceto que `permite colocar algumas opções no arquivo, neste caso, simplesmente no modo multilinha. Quando são fornecidos dois arquivos, o Retina assume automaticamente o modo substituir tudo. Esses dois arquivos definem um programa que lê a entrada de STDIN e imprime o resultado em STDOUT.

Você também pode testá-lo no RegexHero e RegexStorm . O regex funciona com e sem nova linha à direita e usa _no lugar de .. (Aparentemente, o RegexStorm ocasionalmente tem problemas se não houver uma nova linha à direita, mas o RegexHero parece lidar bem com ambos os casos.)

Há uma quantidade horrível de duplicação no regex, e eu tenho algumas idéias para reduzi-lo significativamente ... Vou tentar mais tarde e depois adicionar uma explicação. Enquanto isso, deixe-me saber se você pode encontrar alguma entrada que produza um resultado errado.


7

J, 75 73 bytes

((,.|.)0 _1 0 1)(+:@](LF,@:,.~'*.C'{~>.)(2=f)+.[f]*f=.[:+/|.!.0)'C'&=;._2

Usa o formato da pergunta, usando ./ */ Cfor space / usable space / chest, respectivamente.

Editar: corrige um pequeno erro (acidentalmente usei um toro em vez de tratar adequadamente o ambiente como um espaço vazio).

Explicação

## Preparation
              'C'&=;._2  NB. Map ./C to 0/1, turn into matrix
((,.|.)0 _1 0 1)         NB. Compute offsets to shift into each direction
                         NB. (i.e. [[_1 0], [1 0], [0 _1], [0 1]] in any order)


## "Part B"
(2=f)+.[f]*f=.[:+/|.!.0  NB. This part computes a matrix that is 1 for cells that
                         NB. cannot contain a chest:
              [:+/|.!.0  NB. Sum of shifts: shift in each of the four cardinal
                         NB. directions (using the array above) and then sum up.
           f=.           NB. Define this function as `f`; we'll use it some more.
         ]*              NB. Multiply by the "is chest" matrix: this isolates
                         NB. double-chests.
       [f                NB. Sum of shifts--1 for double-chest neighbours.
(2=f)                    NB. Isolate cells with two neighbouring chest.
     +.                  NB. Boolean or--either two neighbouring chests or next
                         NB. to a double-chest.

## Wrap up the result
(+:@] (fmt >.) PartB)    NB. Maximum of the array from the above and twice the "is
 +:@]      >.  PartB     NB. chest" matrix--this is 0,1,2 for '*', '.' or chest,
                         NB. respectively.

## Output formatting
LF,@:,.~'*.C'{~          NB. Format output...
        '*.C'{~          NB. Map 0,1,2 to '*.C' by using the value as index
LF   ,.~                 NB. Append line feed at end of each line
  ,@:                    NB. Ravel into one line

4

C, 193

2 novas linhas desnecessárias para maior clareza. As alterações em relação ao código não-destrutivo incluem: caracteres como códigos ASCII, em vez de caracteres literais; rearranjo de v = 0, strlen e strchr para salvar caracteres (strchr é o mais feio, pois significa que um cálculo que, de outra forma, seria realizado apenas uma vez, é realizado 5 vezes por célula!)

As funções C não aceitam seqüências de caracteres como argumentos ou as retornam como valores; portanto, o melhor que posso fazer é o seguinte: qé um ponteiro para a sequência de entrada. A função modifica a cadeia e, quando a função retorna, a saída é encontrada na cadeia original.

g(char*q){int v,j,w,l;
int f(p,d){int s=0,i=w=strchr(q,10)-q+1,r;for(;w/i;i-=i-1?w-1:2)r=p+i,r>-1&r<l&&q[r]==67&&++s&&d&&f(r,0);v|=s>d;}
for(j=l=strlen(q);j--;f(j,1),46-q[j]||v||(q[j]=42))v=0;}

Para resumir as regras:

um quadrado em branco (que não contém C ou nova linha) pode ser convertido se tiver no máximo 1 vizinho com um C

... E esse vizinho não tem vizinhos com um C.

A função g contém uma função f que se repete da profundidade 1 à profundidade 0. Com apenas 2 níveis de recursão, uma f(r,0)chamada recursiva simples será executada, não sendo necessário f(r,d-1)!

Código não jogado no programa de teste

A sequência de teste de entrada é codificada. getse scanfnão aceitará uma sequência de entrada com novas linhas; eles cortam em pedaços a cada nova linha.

char n[]=".......C..\n...C..C...\n.........C\n.CC...CC..\n..........";

g(char*q){

  int v,j,w,l;

  int f(p,d){                    //p=cell to be checked,d=recursion depth
    int s=0,i=w,r;               //sum of C's found so far=0, i=width
    for(;w/i;i-=i-1?w-1:2)       //For i in   w,1,-1,-w   = down,right,left,up
      r=p+i,                     //r=cell adjacent to p
      r>-1&r<l&&q[r]=='C'&&++s   //If r not out of bounds and equal to C, increment s...
        &&d&&f(r,0);             //...and if recursion depth not yet at zero, try again one level deeper. 
    v|=s>d;                      //If the local s exceeds d, set global v to true to indicate invalid.
  }

  w=strchr(q,10)-q+1;            //width equals index of first newline + 1                   
  l=strlen(q);                   //length of whole string;
  for(j=l;j--;)                  //for l-1 .. 0 
    v=0,                         //clear v
    f(j,1),                      //and scan to see if it should be set
    '.'-q[j]||v||(q[j]='*');     //if the character is a '.' and v is not invalid, change to '*'
}

main(){
  g(n);
  puts(n);
}

Saída com base no exemplo de pergunta

******.C**
***C**C.**
*..***..*C
.CC.*.CC.*
*..***..**

1

JavaScript (ES6) 124 129

Usando caracteres 0 (*), 6 (C), 7 (.)

F=s=>[for(c of(d=[o=~s.search('\n'),-o,1,i=-1],s))
   d.map(j=>t-=s[i+j]==6&&~d.some(k=>s[i+j+k]==6),t=i++)|c<7|t>i&&c
].join('')

Ungolfed e explicou

F=s=>
{
  o=~s.search('\n') // offset to prev row (~ is shorter than +1 and sign does not matter)
  d=[o,-o,1,-1] // array of offset to 4 neighbors
  i=-1
  result = '' // in golfed code, use array comprehension to build the result into an array, then join it
  for (c of s) // scan each char
  {
    t = i++ // set a starting value in t and increment current position in i
    d.forEach(j => // for each near cell, offset in j
    {         
      if (s[i+j]==6) // if cell contains a Chest, must increment t
      {  
        // In golfed code "~some(...)" will be -1(false) or -2(true), using decrement instead of increment
        if (d.some(k=>s[i+j+k]==6)) // look for another Cheast in the neighbor's neighbors
        {
          // more than one chest, position invalid
          t += 2
        }
        else
        {
          t += 1
        }
      }
    })
    if (c < 7 // current cell is not blank
        || t > i) // or t incremented more than once, position invalid
    {
       result += c // curent cell value, unchanged
    }
    else
    {
       result += 0 // mark a valid position 
    }
  }
  return result
}

Teste no console Firefox / FireBug

a='\
7777777677\n\
7776776777\n\
7777777776\n\
7667776677\n\
7777777777\n';

console.log(F(a))

Saída

0000007600
0006006700
0770007706
7667076670
0770007700

1

Perl, 66

Os conflitos no peito correspondentes à regexp terminaram no lado comprido, então não competimos com CJam neste momento.

#!perl -p0
/.
/;$"=".{@-}";s%0%s/\G0/2/r!~/2((.$")?2(.$")?|2$"|$"2)2/s*1%eg

Usa 0 e 2 para espaços vazios e de peito na entrada, 1 para marcar os pontos na saída.

Experimente aqui .


0

Python 2 - 281 bytes

f=lambda x,y:sum(m[y][x-1:x+2])+m[y-1][x]+m[y+1][x]
m=[];o=''
try:
 while 1:m+=[map(int,'0%s0'%raw_input())]
except:a=len(m[0]);l=len(m);m+=[[0]*a]
for y in range(l*2):
 for x in range(1,a-1):
    if y<l:m[y][x]*=f(x,y)
    else:o+=`2if m[y-l][x]else +(f(x,y-l)<5)`
 if y>=l:print o;o=''

(As linhas 8 e 9 são planejadas com um único caractere de tabulação, que o SE converte em 4 espaços. Cada linha deste programa possui 0 ou 1 bytes de espaço em branco à esquerda.)

Entrada: 0para não peito, 2para peito
Ouput: 0para não peito, 2para peito existente, 1para possível novo peito


Deus, isso é horrível. Eu devo estar seriamente sem prática. Eu joguei todos os truques que conheço e ele saiu ... bem, saiu como 281 bytes, perdendo para todas as respostas, exceto a do regex , haha. Sinceramente, sinto que joguei um pouco bem, então acho que meu algoritmo não foi o ideal.

Ungolfed:

def f(x,y):
    """Given x,y coords of the board, return the sum of that point and all
    adjacent points.
    """
    return (sum(board[y][x-1:x+2]) # (x-1,y) + (x,y) + (x+1,y)
            + board[y-1][x]
            + board[y+1][x])
board=[]
output=''
try:
    while True:
        row = '0%s0' % raw_input() # line from stdin with a leading and trailing 0
        board.append(map(int, row)) # convert to list of ints
except:
    pass # exception is thrown when stdin is empty

board_width = len(board[0])
board_height = len(board)

board.append([0]*board_width) # new row of all 0s

for y in xrange(board_height*2):
    # board_height multiplied by 2 so we can use this loop to simulate two
    for x in xrange(1,board_width-1):
        if y < board_height: # "first loop"
            board[y][x] *= f(x,y) # multiply everything on the board by itself + sum
                                  # of neighbours
                                  # empty cells (0) stay 0 no matter what
                                  # lone chests (2 surrounded by 0) become 2*2==4
                                  # double chests (2 touching another 2) are weird:
                                  # - one chest becomes 2*(2+2)==8
                                  # - the other chest becomes 2*(2+8)==20
        else: # "second loop"
            if board[y - board_height][x] != 0:
                output += '2' # anything not equal to 0 is an existing chest
            else:
                valid = f(x, y - board_height) < 5 # if the sum of neighbours > 4, the
                                                   # current cell is either beside a
                                                   # double chest or more than one
                                                   # single chest
                output += '01'[valid]
    if y >= board_height: # only print during the "second loop"
        print output
        output=''
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.