Construa um relógio digital no Wireworld

32

Inspirado por esta questão do Jogo da Vida .

O Wireworld simula "elétrons" que fluem através de "fios", arranjos simples dos quais produzem comportamento típico de porta lógica.

Desafio você a construir um relógio digital no autômato celular Wireworld. Seu relógio deve contar entre 00:00 e 23:59 da maneira usual ou 11:59 com um indicador AM / PM e, em seguida, redefinir.

Sua entrada deve ser visivelmente dividida em duas partes. A parte A deve conter toda a lógica de não exibição, todas as partes envolvidas no incremento e loop dos dígitos. A parte B será a tela e a lógica que a guia. A única conexão entre essas duas partes deve ser de 16 fios, representando os quatro dígitos do tempo no BCD (com um fio opcional para o indicador AM / PM e um fio opcional para uma linha de relógio de sinal, se os sinais não forem contínuos). (EDIT: sempre é possível omitir zero fios)

O tempo do comportamento do relógio deve ser consistente. A simulação deve receber o mesmo número de ticks para cada uma das 1440 transições entre estados. Quaisquer elétrons nos 16 fios devem ser emitidos da parte A ao mesmo tempo e iniciar sua viagem em paralelo.

Esta é uma competição de golfe com código. Sua pontuação é a área da caixa delimitadora alinhada ao eixo ao redor da parte A.

Por analogia, se esse fosse um idioma textual, sua pontuação seria do tamanho da função de gerenciamento de relógio, produzindo quatro saídas de 4 bits, que contêm um loop e a lógica para 4 contadores, não a função que decodifica e imprime essa saída.

Sua parte B pode ser tão grande ou pequena quanto você gostaria. Isso é necessário apenas para que a saída do seu envio possa ser vista por alguém que o esteja executando, pois não há uma maneira fácil de simplesmente "depurar" as saídas de um circuito do mundo wirew. Existem vários circuitos BCD-> 7segment disponíveis online. Sinta-se à vontade para usar o que desejar ou criar o seu próprio se precisar de uma linha de sinal com relógio, e exiba seu indicador AM / PM em uma escala semelhante aos dígitos.

EDIT: Parte B agora é opcional. Se você possui apenas as saídas BCD da sua parte A, sinta-se à vontade para enviá-las. Será mais tedioso confirmar que o relógio funciona, mas posso ler uma linha de bits muito bem em uma simulação em pausa.

Sparr
fonte
Aqui está um pequeno simulador online.
NonlinearFruit
Eu estive trabalhando nisso, mas só o vi na semana passada, então provavelmente sentirei falta da recompensa. Não consigo encontrar uma versão de 4 fios de um wireworld bcd-> 7 segmentos; construir um conversor de 4 para 2 em frente ao popular dispositivo de 2 fios e 7 segmentos (como o que acompanha o golly) pode ser o caminho a percorrer. Um problema com esse dispositivo é que, apesar de bonito, é lento para atualizar, o que aumentará o tamanho da Parte A porque pode gerar números mais rapidamente do que podem ser exibidos e deve ser artificialmente mais lento.
Wyldstallyns
Eu tenho um 150.000 células trabalhando Parte A que eu posso provar obras, mas carece actualmente de uma regra compatível Parte B.
wyldstallyns
Eu não esperava que a parte B fosse difícil. Qual a distância entre seus elétrons na parte A?
Sparr
11
@wyldstallyns Fecha em 16/12/2016 03: 30: 35Z (você pode passar o mouse sobre o 'amanhã' para obter os horários precisos). Tudo de bom para vc. Eu realmente gosto do seu relógio. É uma ideia elegantemente simples e uma excelente execução. Devo admitir que também fiquei surpreso com quanto espaço o meu acabou ocupando no final. E eu ficaria interessado em ver as melhorias que você puder encontrar na sua. Então, boa sorte :) #
10131 niemiro

Respostas:

36

Relógio de travamento

Pontuação - 53.508 (dos quais apenas 36.828 são usados ​​ativamente devido ao design em forma de L)

Clock Running

Gravação de alta qualidade - https://1drv.ms/u/s!ArQEzxH5nQLKhvt_HHfcqQKo2FODLQ
Padrão Golly - https://1drv.ms/u/s!ArQEzxH5nQLKhvwAmwCY-IPiBuBmBw

Princípios Orientadores -

  • Como essa foi a primeira vez que utilizei um autômato celular, evitei juntar grandes componentes pré-fabricados. Uma abordagem válida que eu não tomei seria um somador binário começando em zero e adicionando continuamente um à última saída, seguido por um conversor binário em BCD, desmultiplexador de display, decodificador de 7 segmentos e display de 7 segmentos.
  • Deve ser possível iniciar o relógio a frio. Eu me impus a restrição adicional de que uma única cabeça de elétron colocada em uma célula condutora específica deveria iniciar corretamente o relógio. Eu não queria exigir uma sincronização manual cuidadosa de muitos flip-flops díspares e elementos de tempo individuais antes de iniciar a simulação.

Parte I: O contador de minutos

Matemática

A contagem de 0 a 9 em binário (para o dígito de minutos menos significativo) é a seguinte -

0 - 0000
1 - 0001
2 - 0010
3 - 0011
4 - 0100
5 - 0101
6 - 0110
7 - 0111
8 - 1000
9 - 1001

Lendo que, como colunas, o menos significativo (fluxo de bits de 2 ^ 0 unidades) vai 01010101, o fluxo de 2 ^ 1 unidades vai 0011001100, o fluxo de 2 ^ 2 unidades vai 0000111100 e o fluxo de 2 ^ 3 unidades vai 0000000011.

O primeiro é fácil - basta virar flip 01 para sempre. O terceiro é um fluxo de quatro 1s, seis 0s, fase deslocada em seis zeros. O quarto é um fluxo de oito 0s e dois 1s.

O segundo é um pouco mais difícil, pois possui uma assimetria desagradável. No entanto, percebo que (onde. É operador concat):

0011001100. 0011001100 = 0011001100. NOT (1100110011) = 00110011001100110011 XOR 00000000001111111111 = 5 (0011) XOR 00000000001111111111

(Aliás, como mencionado anteriormente, a maioria do meu relógio é executado em um relógio de 60 batidas. A onda de comprimento duplo 00000000001111111111 é onde entra a necessidade do relógio de 120 batidas).

desenhar

Os fluxos de saída de cima para baixo vão Unidades de Minutos (2 ^ 0, 2 ^ 1, 2 ^ 2, 2 ^ 3) e, em seguida, Dezenas de Minutos (2 ^ 0, 2 ^ 2, 2 ^ 1). Observe que os dois fios inferiores estão cruzados.

Contador de minutos anotado

  1. Relógio principal de 120 batidas.
  2. Onde colocar um elétron para uma partida a frio. Sem nenhuma cauda de elétrons, ele se divide em duas direções, mas o diodo imediatamente acima captura uma delas, fornecendo um bom elétron de bicicleta circulando e contornando o circuito de 120 batidas.
  3. Relógio secundário de 12 batidas.
  4. A bobina do condutor + diodo inicia o relógio secundário de 12 batidas. As palavras não podem descrever o quão complicado esse pedacinho era para sincronizar. Você precisa sincronizar os relógios de 120 e 60 batidas e, em seguida, sincronizar os pseudo-relógios de 12 e halteres de 12 batidas e frequência, seguidos por amarrar o relógio de 24 batidas ao relógio de 120 batidas, caso contrário, o portão XOR não funcionará. .
  5. Mudança de fase.
  6. Chinelo de dedo. Um único elétron na entrada atinge a linha definida primeiro e, depois de um período muito específico, atinge a linha de redefinição, fornecendo precisamente um pulso para dentro, um pulso para fora.
  7. Adicionando humps aqui - na linha de redefinição, aumenta o atraso entre definir e redefinir no flip-flop. Cada corcunda extra dá um pulso extra. O flip-flop abaixo tem nove hips extras, então dez pulsos entre definir e redefinir.
  8. Portão XOR para minha linha complicada de 2 ^ 1 unidades de minutos.
  9. A porta AND-NOT e comprimentos de peça muito específicos significam que cada pulso de elétron que passa dobra novamente e aniquila o elétron atrás. Metade da frequência. Cria um relógio de 24 batidas a partir da fonte secundária de 12 batidas.
  10. Relógio secundário de 60 batidas, que na verdade faz a maior parte do trabalho. É mais fácil iniciar um relógio rápido a partir de um mais lento, então o relógio mais lento (120 batidas) é o mestre, mesmo que seja pouco usado. O relógio de 60 batidas é o coração dessa coisa.
  11. Fio de realimentação que transporta elétrons somente quando o relógio de 60 batidas está correndo. É usado em conjunto com uma porta AND-NOT para impedir que o relógio seja reiniciado repetidamente a partir do master de 120 batidas. Caso contrário, muitas coisas horríveis acontecem e Ctrl-Z é o salvador.
  12. O diodo de onde o relógio de 60 batidas é iniciado.
  13. Todo esse dispositivo é um flip-flop, AND gate e AND-NOT gate combinados. Dá uma trava. Um pulso inicia, um pulso pára.
  14. Laço de fio para calibrar a trava para 10 pulsos ligados, 10 pulsos desligados para uma entrada de um em dez pulsos. Sem ele, temos 12 pulsos ligados e 8 pulsos desligados. Essas presilhas de dez em dez formam os componentes básicos dos blocos de dez minutos da mesma maneira que os chinelos de 6 mícrons (1 pulso) formaram os componentes básicos das unidades de minutos.
  15. O pulso inicial de partida a frio causou todos os tipos de problemas, incluindo dois batimentos fora de fase com os relógios que ele inicia. Isso atrapalha as travas. Esse portão AND captura e descarta pulsos fora de sincronia - em particular o pulso inicial.
  16. Essa é uma parte do design do qual me arrependo um pouco em retrospecto. Ele pega um elétron, o divide em cinco e aniquila os cinco elétrons atrás, levando 111111 a 100000.
  17. Isso pega um elétron e o costura na frente. Duas fases à frente para ser preciso. Leva 100000 e faz 101000. Combinado com a parte 16, obtivemos 111111 -> 100000 -> 101000. Em retrospecto, eu gostaria de ter feito 111111 -> 101010 -> 101000; teria alcançado o mesmo efeito em menos espaço.
  18. Os padrões acima são então empurrados para dentro da trava inferior para obter 20 ligado, 40 desligado. Isso é dividido, a metade é deslocada em 20 unidades e, em seguida, elas formam os dois fluxos de bits de alta ordem das dezenas de minutos.

Parte II: O Contador de Horas

Explicação

A entrada para o contador de horas é um único pulso de elétron, uma vez por hora. O primeiro passo é reduzi-lo a um único pulso de elétron, uma vez a cada doze horas. Isso é alcançado usando várias primitivas "trava e captura".

Uma "trava" é um flip-flop de 6 mícrons conectado a uma porta AND-NOT e AND para fornecer uma trava on / off de 6 mícrons. Uma "captura" pega um fluxo contínuo de elétrons como entrada, permite a primeira passagem e depois aniquila todos os outros elétrons por trás, até que o fluxo termine no ponto em que a captura é redefinida.

A colocação de uma trava, seguida de uma trava, em série, resulta em um elétron em -> gire a trava, um elétron na outra extremidade (restante preso em trava). Em seguida, o segundo elétron in -> desliga a trava, apanha silenciosamente as redefinições. Efeito líquido: o primeiro elétron passa, o segundo elétron é aniquilado e assim por diante, independentemente do tempo de atraso entre esses elétrons .

Agora encadeie dois "trava e trava" em série, e você tem apenas um em cada quatro elétrons passando.

Em seguida, pegue uma terceira "trava e trava", mas desta vez incorpore uma quarta trava inteira e trava na linha SET do flip-flop, entre o portão AND-NOT e o SET do flip-flop. Vou deixar você pensar sobre como isso funciona, mas desta vez apenas um em cada três elétrons passa, independentemente de quanto tempo o atraso entre esses elétrons .

Finalmente, pegue um em quatro elétrons e um em três, combine-os com uma porta AND, e apenas um em cada doze elétrons passará. Esta seção inteira é o rabisco bagunçado de caminhos para o canto superior esquerdo do contador de horas abaixo.

Em seguida, pegue o elétron a cada doze horas e divida novamente em um a cada hora, mas coloque cada um em um fio condutor diferente. Isso é conseguido usando o condutor espiral longo com treze pontos de saída.

Pegue esses elétrons - um a uma hora em diferentes condutores e atinja uma linha SET de flip-flop. A linha RESET no mesmo flip-flop é atingida pelo condutor da próxima hora, emitindo sessenta pulsos por fio por hora.

Finalmente - pegue esses pulsos e passe-os para sete bytes e meio de ROM (Memória Somente Leitura) para emitir os fluxos de bits BCD corretos. Veja aqui uma explicação mais detalhada da ROM do WireWorld: http://www.quinapalus.com/wires6.html

desenhar

Contador de horas anotado

  1. Entrada de um elétron por hora.
  2. Primeira trava.
  3. Primeira captura.
  4. "Trava e trava" incorporada em uma linha externa "Trava e trava".
  5. E portão.
  6. Trava AM / PM (ativada / desativada uma vez a cada doze horas).
  7. Cada laço de fio tem 6x60 = 360 unidades de comprimento.
  8. Flip / Flop virou de lado para criar um perfil menor.
  9. Sete bytes e meio de ROM.

Notas

  1. Devido ao seu design de um elétron por minuto, 6 mícrons, execute a simulação a seis gerações por minuto (uma geração a cada 10 segundos) para um relógio em tempo real.
  2. A linha AM / PM é alta (1) para AM e baixa (0) para PM. Pode parecer uma maneira um tanto incomum de escolher, mas há justificativa. Durante uma partida a frio do relógio, a linha AM / PM é naturalmente baixa (0) inicialmente. Assim que a linha AM / PM é puxada alta (1), isso indica que a contagem começou às 12:00. Toda saída antes deste ponto deve ser desconsiderada, toda saída após esse ponto é considerada significativa.

Links Úteis

niemiro
fonte
requisitos alterados para que saídas sempre com zero possam ser omitidas. os bits 4s e 8s para as dezenas de horas nunca são usados, nem os bits 8s para dezenas de minutos.
Sparr
Sólido! Engenharia verdadeira. Algum dos outros portões lógicos teria sido útil? Estou prestes a força bruta alguns.
Wyldstallyns
11
Esta é bonito
Sparr
11
Oh boa dor que é apenas perto o suficiente agora estou obrigado a tentar o meu otimizar. Tenho repetir padrões que eu poderia encurtar para dar espaço para dobrar os outros em.
wyldstallyns
3
Não sei o quanto você é ativo na meta, portanto, é para informar que eu indiquei esta resposta para o Best of PPCG 2016 .
Peter Taylor
5

Atraso na memória da linha - 51 x 2880 = 146880

Imagem

Reduzir o zoom:

Imagem

A saída sai da parte superior de cada loop.

Eu coloquei todos os estados diretamente no fio com esta lua, deixando gollyos elétrons avançarem entre os bits, para que não tenhamos que seguir o fio com um cursor.

Usei esse método ingênuo para definir um bar e um curso intensivo sobre o mundo do arame, golly e lua.

local g = golly()

local minutes_in_day = 1440 -- 60x24
local interval = 4 -- how often to send electrons

local function bcd4(num)
    num=math.floor(num)
    local t={}
    for b=4,1,-1 do
        t[b]=math.floor(math.fmod(num,2))
        num=(num-t[b])/2
    end
    return table.concat(t)
end

local function makewire(x,y1,y2)
    for y1=1,y2 do g.setcell(x,y1,3) end
end

local function makeloop(x,y,size)
    local len = size/2 - 1
    makewire(x,y+1,len); makewire(x+2,y+1,len) -- main wires
    g.setcell(x+1,y,3); g.setcell(x+1,y+len,3) -- endcape
end

local function paint(x,y,pattern)
    for v in string.gmatch(pattern,".") do
        if v=="1" then g.setcell(x, y, 1); g.setcell(x, y-1, 2) end
        x = x + 4
    end
    g.show(pattern);g.update() -- slows things down but more interesting to watch
    for i=1,interval do g.step() end
end

for x=0,63,4 do makeloop(x,0,minutes_in_day * interval) end

for hour = 0,23 do
      for minute = 0,59 do
         paint( 0, 2, bcd4(hour/10) .. bcd4(hour%10) .. bcd4(minute/10) .. bcd4(minute%10) )
      end
end

Para testar, adicionei esses fios superiores e observei as dicas deles.

Imgur

Aqui está o script para coletar os 4 conjuntos de BCD de 4 fios no globo ocular.

-- watches 16 wires spaced 4 apart starting at (0,-4)
local ticks = 1440 -- set to match the length of your 24 hour loop
local g = golly()
local output = ""
local nums = {  ["0000"] = "0", ["0001"] = "1", ["0010"] = "2", ["0011"] = "3", ["0100"] = "4",
                ["0101"] = "5", ["0110"] = "6", ["0111"] = "7", ["1000"] = "8", ["1001"] = "9",
                ["1010"] = "A", ["1011"] = "B", ["1100"] = "C", ["1101"] = "D", ["1110"] = "E",
                ["1111"] = "F" } -- full set in case we have errors (i did)

for i=0,ticks,1 do
   local text = ""
   for i=0,48,16 do -- set your X here, change the 0 and 48
       local word = ""
       for j=0,15,4 do
            local bit = g.getcell(i+j,-4) -- set your Y here, change -4
            if bit == 0 or bit == 3 then word = word .. "0" else word = word .. "1" end
       end
       text = text .. nums[word]
   end
   g.show(text); output = output..' '..text
   g.update(); g.step();g.step();g.step();g.step()
end
g.note(output)

A resposta final requer a remoção das linhas sempre com zero e o roteamento do restante para as entradas corretas do BCD.

wyldstallyns
fonte
requisitos alterados para que saídas sempre com zero possam ser omitidas. os bits 4s e 8s para as dezenas de horas nunca são usados, nem os bits 8s para dezenas de minutos.
Sparr
2
Esta é uma implementação hilária e impressionante!
Sparr
11
Ok, eu bati com outro relógio funcional às 11 horas. Vou atacar os loops mais longos e mais curtos com truques diferentes.
wyldstallyns
Eu não vou conseguir. Posso economizar 1/4 do tamanho alternando para pulsos de 3 mícrons, mas ele ainda não enrola o suficiente para vencer o niemiro.
wyldstallyns