Contar padrões comuns do Jogo da Vida

21

A tarefa aqui é ler de um .rlearquivo Golly ou de texto sem formatação (sua escolha) cujo nome de arquivo seja fornecido (no STDIN ou como um argumento de linha de comando) e identificar e contar os padrões comuns na grade codificada nele.

Como alternativa, você pode optar por fornecer o conteúdo do arquivo diretamente sobre STDIN.

Seu programa deve ser capaz de identificar e distinguir pelo menos as quinze naturezas-mortas estritas mais comuns e os cinco osciladores mais comuns , além de planadores .

Todas as fases desses osciladores devem ser reconhecidas, assim como as quatro fases do planador.

Ele deve gerar uma lista contendo a contagem final de cada padrão, com o nome e a quantidade de cada padrão em uma linha separada. Seu programa pode incluir na lista de saída todos esses padrões ou apenas aqueles nos quais pelo menos um foi encontrado.

Padrões que fazem parte de outros padrões contados não devem ser contados. (por exemplo, a fase de 8 células de um farol também não deve ser contada como dois blocos, e um empate de navio também não deve ser contado como dois navios)

Você pode supor que a entrada já tenha estabilizado e não contém padrões que não estejam no conjunto mencionado acima. Você também pode supor que a grade de entrada caiba dentro de uma caixa de 1024x1024.

Isso é , então o programa mais curto vence.

Descrição de formato de arquivo RLE

Um arquivo RLE contém uma grade de vida codificada na duração. Todas as linhas que começam com #são comentários e devem ser ignoradas.

A primeira linha não-vazia e sem comentários é do formulário x=<width>,y=<height>,rule=<rule>. Para os fins desta tarefa, a regra sempre será B3/S23. Pode conter espaços que devem ser removidos antes do processamento desta linha (é claro, não é necessário processar esta linha).

As linhas sem comentários após o primeiro devem ser tratadas como uma única sequência. Este deve consistir apenas de dígitos decimais, os personagens $, be o, e quebras de linha, e não vai terminar com um dígito. As quebras de linha devem ser ignoradas, mas você pode assumir que as quebras de linha não interromperão as seqüências de dígitos.

Isso pode ser encerrado por um único !.

brepresenta uma célula morta, orepresenta uma célula viva e $representa o fim de uma linha. Qualquer número decimal indica que o símbolo a seguir deve ser tratado como repetição várias vezes.

Codificação de padrão de texto sem formatação

A outra opção é ler o padrão em outro formato de texto sem formatação descrito aqui. Nesta codificação, as células off são representadas com hífens e as células são representadas com O maiúsculo, com novas linhas separando linhas.

Você pode supor que todas as linhas sem comentários serão preenchidas com o mesmo comprimento de hífens.

As linhas que começam com !são comentários e devem ser ignoradas.

Alguns casos de teste

RLE:

#This is a comment
x = 35, y = 16, rule = B3/S23
bo$2o$obo5$22bo$22bo$22bo2$18b3o3b3o2$22bo$22bo10b2o$22bo10b2o!

Texto simples:

!This is a comment
-O---------------------------------
OO---------------------------------
O-O--------------------------------
-----------------------------------
-----------------------------------
-----------------------------------
-----------------------------------
----------------------O------------
----------------------O------------
----------------------O------------
-----------------------------------
------------------OOO---OOO--------
-----------------------------------
----------------------O------------
----------------------O----------OO
----------------------O----------OO

Resultados:

Glider 1
Blinker 4
Block 1

RLE:

x = 27, y = 15, rule = B3/S23
5b2o$5b2o9$11bo$o9bobo$o9bobo$o10bo12b3o!
#Here's a comment at the end

Texto simples:

-----OO--------------------
-----OO--------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
---------------------------
-----------O---------------
O---------O-O--------------
O---------O-O--------------
O----------O------------OOO
!Here's a comment at the end

Resultados:

Block 1
Blinker 2
Beehive 1

RLE:

#You may have multiple comments
#As shown here
x = 13, y = 11, rule = B3/S23
2o$2o2$12bo$12bo$12bo$2b2o$2b2o4b2o$7bo2bo$7bobo$8bo!

Texto simples:

!You may have multiple comments
!As shown here
OO-----------
OO-----------
-------------
------------O
------------O
------------O
--OO---------
--OO----OO---
-------O--O--
-------O-O---
--------O----

Resultados:

Block 2
Blinker 1
Loaf 1

RLE:

# Pentadecathlon
# Discovered by John Conway
# www.conwaylife.com/wiki/index.php?title=Pentadecathlon
x = 10, y = 3, rule = B3/S23
2bo4bo2b$2ob4ob2o$2bo4bo!

Texto simples:

! Pentadecathlon
! Discovered by John Conway
! www.conwaylife.com/wiki/index.php?title=Pentadecathlon
--O----O--
OO-OOOO-OO
--O----O--

Resultados:

Pentadecathlon 1

Bônus

Se você suporta os dois formatos de entrada (usando a extensão de arquivo [ .rlepara arquivos rle e .cellstexto sem formatação - como outras extensões devem ser lidas é indefinida] ou um sinalizador de linha de comando para distinguir entre elas), você pode subtrair 5% da sua pontuação.

SuperJedi224
fonte
Que talOOO.OO\n....OO
Akangka
@ChristianIrwan Bem, esse não é um padrão estável, então você não receberia como entrada de qualquer maneira.
precisa saber é o seguinte

Respostas:

13

Haskell, 2417 bytes

Isso levou um bom tempo e ainda existem alguns bugs, mas eu tenho vários truques trabalhando, então valeu a pena.

Notas:

  • Ele aceita apenas o formato de texto simples, passado para STDIN
  • Leva algo como tempo O (n ^ 20)
  • Eu assumi que o número de caracteres nas linhas sem comentário é constante (dentro de uma entrada específica), como é nos exemplos
  • O truque mais louco foi como os padrões são descompactados, extraindo os elementos nas posições (número da coluna) módulo (o comprimento da saída) para construir a matriz.

Combina algumas ideias-chave:

  • padrões e simetrias podem ser pré-computados
  • um único padrão pode ser compactado em um número inteiro, com dimensões conhecidas
  • encontrar todas as submatrizes é fácil
  • contar igualidades é fácil

Aqui está o código:

r=[1..20]
a!0=a!!0
a!x=tail a!(x-1)
u[_,x,y,l]=[[odd$div l$2^i|i<-[0..y],mod i x==j]|j<-[0..x-1]]
main=interact(\s->let q=[map(=='O')l|l<-lines s,l!0/='!']in let g=[i|i<-[[[0,3,11,3339,0,4,11,924,0,4,11,3219,0,3,11,1638,1,4,15,19026,1,4,15,9636,2,3,11,1386,2,4,11,1686,3,7,48,143505703994502,3,7,48,26700311308320,3,7,48,213590917399170,3,7,48,8970354435120,4,2,3,15,5,3,8,171,5,3,8,174,5,3,8,426,5,3,8,234,6,4,15,36371,6,4,15,12972,6,4,15,51313,6,4,15,13644,6,4,15,50259,6,4,15,12776,6,4,15,51747,6,4,15,6028,7,4,15,26962,7,4,15,9622,7,4,15,19094,7,4,15,27044,8,5,24,9054370,8,5,24,2271880,9,4,15,51794,9,4,15,13732,9,4,15,19027,9,4,15,9644,10,4,19,305490,10,5,19,206412,10,5,19,411942,10,4,19,154020,11,3,8,427,11,3,8,238,12,6,35,52217012547,12,6,35,3306785328,13,3,8,170,14,3,8,428,14,3,8,458,14,3,8,107,14,3,8,167,14,3,8,482,14,3,8,302,14,3,8,143,14,3,8,233,14,3,8,241,14,3,8,157,14,3,8,286,14,3,8,370,14,3,8,181,14,3,8,115,14,3,8,346,14,3,8,412,15,4,15,51219,15,4,15,12684,15,4,15,52275,15,4,15,13260,16,1,2,7,16,3,2,7,17,3,29,313075026,17,10,29,139324548,17,3,23,16252911,17,8,23,16760319,17,5,49,152335562622276,17,10,49,277354493774076,17,7,69,75835515713922895368,17,10,69,138634868908666122360,17,9,89,135722011765098439128942648,17,10,89,58184575467336340020006960,17,5,59,160968502306438596,17,12,59,145347113669124612,17,5,59,524156984170595886,17,12,59,434193401052698118,17,5,69,164495599269019571652,17,14,69,222245969722444385292,17,5,69,517140479305239282702,17,14,69,222262922122170485772,17,3,47,83020951028178,17,16,47,39740928107556,17,3,35,62664969879,17,12,35,40432499049,17,3,41,1581499314234,17,14,41,1293532058322,17,3,41,4349006881983,17,14,41,3376910168355,17,3,47,92426891685930,17,16,47,83780021865522,17,3,47,79346167206930,17,16,47,11342241794640,18,13,168,166245817041425752669390138490014048702557312780060,18,15,224,1711376967527965679922107419525530922835414769336784993839766570000,18,13,168,141409121010242927634239017227763246261766273041932,19,2,7,126,19,4,7,231,19,4,7,126,19,2,7,189,19,4,15,24966,19,4,15,18834,19,4,15,10644,19,4,15,26646]!p|p<-[h..h+3]]|h<-[0,4..424]],j<-[[[q!y!x|x<-[a..a+c]]|y<-[b..b+d]]|c<-r,d<-r,a<-[0..(length$q!0)-c-1],b<-[0..length q-d-1]],u i==j]in show[(words"aircraftcarrier barge beehive biloaf1 block boat eater1 loaf longbarge longboat mango ship shiptie tub glider beacon blinker pentadecathlon pulsar toad"!(e!0),sum[1|f<-g,e!0==f!0])|e<-g])

Aqui está o código do Mathematica usado para compactar uma matriz de 0,1 no formato posteriormente descompactado pelo programa haskell:

rotate[m_]:=Transpose[Map[Reverse,m]]
findInversePermutation[m_]:=Block[{y=Length[First[m]], x=Length[m]}, InversePermutation[FindPermutation[Flatten[m], Flatten[Table[Table[Flatten[m][[i+1]], {i, Select[Range[0, x * y - 1], Mod[#, x]==j&]}], {j, 0, x - 1}]]]]]
enumShape[m_]:=Partition[Range[1, Length[Flatten[m]]], Length[m[[1]]]]
pack[m_]:={Length[rotate[rotate[m]]], Length[Flatten[rotate[rotate[m]]]], FromDigits[Permute[Flatten[rotate[rotate[m]]], findInversePermutation[enumShape[rotate[rotate[m]]]]], 2]}

Aqui está uma descrição muito mais completa do código:

range = [1..16]          -- all of the patterns fall within this range

list ! 0        = list !! 0           -- this is a simple generic (!!)
list ! position = (tail list) ! (position - 1)

unpack [_, unpackedLength, unpackedSize, packed] = [ [odd $ div packed (2^i) | i <- [0..unpackedSize], (mod i unpackedLength) == j] | j <- [0..unpackedLength - 1]]

main=interact doer

doer input = show $ tallyByFirst (words nameString) foundPatterns -- this counts equalities between the list of patterns and the submatrices of the input
  where
    parsed = parse input -- this selects the non-comment lines and converts to an array of Bool's
    foundPatterns = countOccurrences partitioned subArrays
    subArrays     = allSubArrays parsed
    partitioned   = modPartition compressed 428 4 -- this partitions the compressed patterns into the form [name number, x length, x length * y length, packed integer]

countOccurrences patterns subArrays = [pattern | pattern <- patterns, subArray <- allSubArrays q, unpack pattern == subArray]

subArray m offsetX subSizeX offsetY subSizeY = [[m ! y ! x | x <- [offsetX..offsetX + subSizeX]] | y <- [offsetY..offsetY + subSizeY]]

allSubArrays m = [subArray m offsetX subSizeX offsetY subSizeY | subSizeX <- range, subSizeY <- range, offsetX <- [0.. (length $ head m) - subSizeX - 1], offsetY <- [0..(length m) - subSizeY - 1]]

tallyByFirst names list = [ (names ! (a ! 0), sum [1 | a <- list, (head a) == (head b)]) | b <- list]

parse string = [map (=='O') line | line <- lines string, head line /= '!']

modPartition list chunksize = [ [list ! position | position <- [offset..offset + chunksize - 1]] | offset <- [0, chunksize..(length list) - chunksize]]
Michael Klein
fonte
Bem-vindo ao PPCG! Ainda não tentei isso, mas com certeza parece impressionante. +1!
a spaghetto
Isso é mais do que impressionante, +1!
cat