Interpretar um diagrama de circuitos


12

Seu desafio é interpretar um diagrama de circuitos, completo com portas lógicas.

Portas lógicas (na verdade, você não precisa saber o que elas fazem para completar esse desafio):

  • e portão: a
  • ou portão: o
  • portão nand: A
  • nem portão: O
  • xor portão: x
  • portão xnor: X
  • não portão: ~

Cada portão, exceto o último, recebe duas entradas. As entradas são de um .no canto superior esquerdo e inferior esquerdo do quadrado 3 por 3 centralizado no portão. Para não, a entrada está diretamente à sua esquerda. A saída é .diretamente para a direita.

Os fios são representados por -|\/.=

  • - entra em contato com dois fios, um à direita e outro à esquerda: c-c
  • | entra em contato com dois fios, um acima e outro abaixo:

    c
    |
    c
    
  • /e \trabalhe da seguinte maneira:

    c        c
     \      /
      c    c
    
  • . entra em contato com todos os fios ao redor:

    ccc
    c.c
    ccc
    
  • =é especial; conecta fios adjacentes através dele:

    -=-
    

    conecta os dois fios. Na sequência

    \|/
    -=-
    /|\
    

    cada fio oposto está conectado um ao outro, mas não aos outros (é aqui que difere .).

  • Para que a corrente flua, dois fios devem ser conectados ao outro, portanto |-, a corrente não flui.

Exemplo de fiação:

      .-.
     =   \
 .--. .---=---
-.   =     .--
 .--. .-------

a entrada é dividida em dois fios e depois dividida em três. Nesta divisão, o fio inferior move-se para o meio e a divisão para baixo do fio superior aparece na parte inferior. Em seguida, a parte superior dos três fios é movida para o meio.

Exemplo de fiação com portões:

--.
   o..~..
--.      o.---
   a.---.
--.

Formato de entrada:

  • cada fio de entrada será identificado com um dígito. No final (logo antes da nova linha), cada saída será rotulada com :(e o fio sempre entrará direto nela, -:ou seja, .:ou =:)
  • a entrada sempre será válida; não haverá laços ou fios unidos sem um portão. Observe que pode haver fios com pontas soltas.
  • = será usado somente quando necessário.

Formato de saída:

  • cada entrada é referenciada com o número correspondente.
  • uma expressão é emitida. Por exemplo, se os fios computarem a entrada 1 e a entrada 2, a saída será 1a2.
  • quaisquer funções emitidas devem corresponder às portas lógicas no início.
  • para não mostrar, coloque o ~antes no local correto.
  • para várias funções, use parênteses para mostrar a ordem de execução. Parênteses também podem ser usados ​​quando há apenas uma única função. Por exemplo,

    1-.~.-.
           A.~.-:
          .
    2-.  /
       x.
    3-.
    

    tem uma saída possível de ~((2x3)A(~1))

  • várias saídas devem ser separadas por uma nova linha (ou equivalente)

Entrada de amostra:

1--.---.
    x.  \
2--.  x.-=---------.
     .    .~.       X.--.
3---. .      a.----.   /
       O.~.-.       \ /
      .              =
4-.  /              / .-:
   X.              .----:
5-.

Uma saída correspondente possível:

(~1)a(~(3O(4X5))
((1x2)x3)X((~1)a(~(3O(4X5))))

Oooohhhh, interessante! Vou tentar.
cjfaure

5
Estou prevendo um exolang interessante saindo disso, se o estendermos de forma a torná-lo completo em Turing.
Victor Stafusa

No caso de um "erro do compilador" (ou seja, fiação de entrada malformada), o que o intérprete deve fazer?
Victor Stafusa

E, se eu conectar diretamente duas entradas? Ou conectar diretamente duas saídas? Ou inserir linhas abertas na saída?
Victor Stafusa

1
@ Victor Isso já é semelhante. Mas eu fui em frente e criei outro
Justin

Respostas:


4

Python 2488 1567 806 706 697 657 653

Yay para gzip + exec!

import zlib,base64;exec zlib.decompress(base64.b64decode('eNp1U8FuqzAQvPMV7sm2gBSuuFupX9BLD5UoBxNMMAkEgQmJVPXb364Daiu9ntaznt2dWYzthvPo2HSbgsrU7E3so0FmAWtgnyeFshjSImC2Zs1Tws4js/fQPMPJ9KKTlFrPeVPIbDRuHnvOA3YByuS2UCNwrloYqOMRQ1ooDY0qwaoKRJxGSZRKP+QCwBn/0YRyzPYcYq77irUATVbGcIytGkN4E7mOyiLayx/MT888AthMx9DGDTLj/zIfPz44emUGqC/Zoio1UdFzohzFp0TNNA7xQhFxDWJiNGNG98L54yLVYUsv3+kZx9G8/uyEoQFk8NELrDeIIggf5Cb3b3/I3nnFNdZe0QOrCHl4+4ZsgVyH16gMb4XHq4IrwA0gkV7kAwyZH7Fs7f0S/O7IbnZX7jelzy+v13f8LsAFD0kVfrQyTklZyCUPL+F2Ef66WHug7i9f/bWyfnOIsrNTZQ/WCXxCcAnY/QmwMeggLwIyeCKD+FB3k6tsj/K6nR4G01fiZCcnTlIGBkw/d2bUzvgSG2kqMvhOkU+ZNirvGS1XgyWKy/xS2TDa3uE/kNuoJX0UC/kP8j/kmA=='))

Limitações e premissas

Assim, apenas 9 entradas são suportadas - vários dígitos não são manipulados corretamente. Como a especificação indica que as entradas são identificadas com um dígito , não um número , isso é permitido.


Entrada e saída

A entrada é recebida via entrada padrão e a saída é via saída padrão.


Teste

Entrada e saída de amostra:

1--.---.
    x.  \
2--.  x.-=---------.
     .    .~.       X.--.
3---. .      a.----.   /
       O.~.-.       \ /
      .              =
4-.  /              / .-:
   X.              .----:
5-.


(~(1))a(~((3)O((4)X(5))))
(((1)x(2))x(3))X((~(1))a(~((3)O((4)X(5)))))

Testado aqui: http://ideone.com/gP4CIq


O algoritmo

É basicamente um DFS bastante ingênuo das saídas. Para cada saída, ele começa do caractere à sua esquerda e rastreia o fio, ramificando (e adicionando à expressão) a cada porta. Quando atinge uma entrada, a adiciona à expressão e segue para o último ponto em que ramificou, pois podemos ter certeza de que a ramificação não é possível sem um portão. E, claro, todos os casos inválidos são descartados. Nada realmente especial para ele - e, portanto, provavelmente é mais do que poderia ter sido.


Notas

O tamanho provavelmente poderia ser reduzido um pouco com alguma reestruturação, mas eu gastei tempo suficiente nisso hoje. A versão de golfe manual foi a compactada.

A compactação gzip torna o golfe interessante, porque determinado cache (por exemplo d=(-1,0,1)) ocupa mais espaço do que permitir que o algoritmo de compactação cuide dele. No entanto, optei por jogar a versão manual o máximo possível, em vez de otimizar a compactação.


Golfed manualmente ( 909 895 840 803):

import sys
def T(c,p):
 h=c[0];i=c[1]
 if h<0 or i<0 or h>=len(m)or i>=len(m[h]):return''
 v=m[h][i];r='';j=p[0];k=p[1];a=h;b=i;d=(-1,0,1)
 if v==' ':return''
 if v in'=-'and j==h:b-=k-i;r+=T([a,b],c)
 if v in'=|'and k==i:a-=j-h;r+-T([a,b],c)
 if v in'=/\\':
  e=j==h or k==i;s=j-h>0;t=j-h<0;u=k-i>0;w=k-i<0;f=(s and u)or(t and w);g=(s and w)or(t and u)
  if not(e or v=='/'and f or v=='\\'and g):a-=j-h;b-=k-i;r+=T([a,b],c)
 if v=='.':
  for x in d:
   for y in d:
    w=[a+x,b+y]
    if not(x==y==0)and w!=p:r+=T(w,c)
 if j==h and k-i>0:
  if v in'aoAOxX':r='('+T([a-1,b-1],c)+')'+v+'('+T([a+1,b-1],c)+')'
  if v=='~':r='~('+T([a,b-1],c)+')'
 if v.isdigit():r=v
 return r
m=[]
for l in sys.stdin:
 m.append(list(l))
e=enumerate
for i,a in e(m):
 for j,b in e(a):
  if b==':':
   print T([i,j-1],[i,j])

Totalmente não-destruído (2488):

import sys

def findOuts(c):
    for i, iVal in enumerate(c):
        for j, jVal in enumerate(iVal):
            if jVal == ':':
                yield [i, j]

def trace(pos, prev):
    if pos[0] < 0 or pos[1] < 0 or pos[0] >= len(circuit) or pos[1] >= len(circuit[pos[0]]):
        return ''
    val = circuit[pos[0]][pos[1]]
    if val == ' ':
        return ''
    next = pos[:]
    ret = ''
    if val in '=-':
        if prev[0] == pos[0]:
            next[1] -= prev[1] - pos[1]
            ret += trace(next, pos)
    if val in '=|':
        if prev[1] == pos[1]:
            next[0] -= prev[0] - pos[0]
            ret += trace(next, pos)
    if val in '=/\\':
        # top-bottom, left-right
        tblr = prev[0] == pos[0] or prev[1] == pos[1]
        # top-left, bottom-right
        tlbr = (prev[0] - pos[0] == 1 and prev[1] - pos[1] == 1) or (prev[0] - pos[0] == -1 and prev[1] - pos[1] == -1)
        # top-right, bottom-left
        trbl = (prev[0] - pos[0] == 1 and prev[1] - pos[1] == -1) or (prev[0] - pos[0] == -1 and prev[1] - pos[1] == 1)
        if not ((val == '/' and (tlbr or tblr)) or (val == '\\' and (trbl or tblr)) or (val == '=' and tblr)):
            next[0] -= prev[0] - pos[0]
            next[1] -= prev[1] - pos[1]
            ret += trace(next, pos)

    if val == '.':
        for x in (-1,0,1):
            for y in (-1,0,1):
                if x == y == 0:
                    continue

                w = [next[0] + x, next[1] + y]
                if w == prev:
                    continue

                # only one of them should return anything
                ret += trace(w, pos)

    # assumption that a logic gate always has a . on its connections, as according to spec
    if val in 'aoAOxX':
        # only from the right/output
        if not (prev[0] == pos[0] and prev[1] == pos[1] + 1):
            return ret
        ret = '(' + trace([next[0] - 1, next[1] - 1], pos) + ')' + val + '(' + trace([next[0] + 1, next[1] - 1], pos) + ')'

    if val == '~':
        # only from the right/output
        if not (prev[0] == pos[0] and prev[1] == pos[1] + 1):
            return ret
        ret = '~(' + trace([next[0], next[1] - 1], pos) + ')'

    if val in '123456789':
        ret = val

    return ret

circuit = []
for line in sys.stdin.readlines():
    # padding added to prevent index out of bounds later
    circuit.append(list(line))

for out in findOuts(circuit):
    next = out[:]
    next[1] -= 1
    print trace(next, out)

O que é DFS? Além disso, trabalhar de trás para frente é exatamente o que eu estava pensando.
Justin

@ Quincunx Profundidade - primeira pesquisa. Basicamente, recursão (ou usando uma construção LIFO, uma pilha) e viajando o mais longe possível ao longo de um caminho até atingir um beco sem saída ou a meta; nesse ponto, ele retorna ao último ponto de divergência e tenta os outros caminhos.
Bob

Boa suposição sobre as entradas. Foi exatamente isso que eu quis dizer (e tentei sugerir isso). No entanto, seu programa funciona 0como um dígito? Como sobre a encomenda trocados de modo a que 2vem antes 1, etc.
Justin

@ Quincunx Estou usando Python .isdigit(), que é efetivamente equivalente ao regex [0-9], tanto quanto eu posso dizer. Isso está correto de acordo com suas especificações? O que você quer dizer com encomenda trocada? Da maneira como é implementado, ele seguirá primeiro no ramo ascendente de qualquer porta lógica, mas não há garantia de pedido de entradas.
10244 Bob

isdigit()é consistente. Ordenação trocada significa algo como 2a primeira entrada e 1a segunda entrada (classificada verticalmente).
1155 Justin justin

6

Java: 1523 1512 caracteres

import java.util.*;class W{int v=99;Map<Integer,String>t;boolean k;public static void main(String[]y){new W().d();}W(){try{java.io.InputStream i=new java.io.File("r").toURL().openStream();t=new HashMap<>();int a=0,x=0,y=0;while((a=i.read())>-1){if(a==10){y++;x=0;continue;}q(x,y,(a>47&a<58?"!":"")+(char)a);x++;}}catch(Exception e){}}void d(){while(!k){k=!k;for(Map.Entry<Integer,String>g:t.entrySet())e(g.getKey(),g.getValue());}for(String b:t.values())if(b.startsWith("$"))System.out.println(b.substring(1));}void e(int a,String s){if(s==null||!s.startsWith("!"))return;int x=a/v,y=a%v;s=s.substring(1);b(s,x,y,x-1,y+1);b(s,x,y,x,y+1);b(s,x,y,x+1,y+1);b(s,x,y,x-1,y);b(s,x,y,x+1,y);b(s,x,y,x-1,y-1);b(s,x,y,x,y-1);b(s,x,y,x+1,y-1);}void b(String p,int m,int n,int x,int y){String s=t.get(x*v+y);if(s==null)return;boolean g=y==n+1;boolean h=y==n-1;boolean i=x==m+1;boolean j=x==m-1;if(z(s,"-=")&n==y){if(i)b(p,x,y,x+1,y);if(j)b(p,x,y,x-1,y);}if(z(s,"|=")&m==x){if(g)b(p,x,y,x,y+1);if(h)b(p,x,y,x,y-1);}if(z(s,"/=")){if(j&g)b(p,x,y,x-1,y+1);if(i&h)b(p,x,y,x+1,y-1);}if(z(s,"\\=")){if(i&g)b(p,x,y,x+1,y+1);if(j&h)b(p,x,y,x-1,y-1);}if(z(s,".")){q(x,y,"!"+p);u();}if(z(s,"~")){q(x,y,"!~("+p+")");u();}if((s.charAt(0)=='%'&n==y-1)|(s.charAt(0)=='&'&n==y+1)){q(x,y,"!("+p+")"+s.charAt(1)+"("+s.substring(2)+")");u();}if(z(s,"OoAaXx")){q(x,y,(n==y+1?"%":"&")+s+p);u();}if(z(s,":")){q(x,y,"$"+p);u();}}void q(int x,int y,String z){t.put(x*v+y,z);}void u(){k=false;}boolean z(String s,String c){return c.indexOf(s)>-1;}}

Ele fornece esta saída para a entrada de amostra:

(~(((5)X(4))O(3)))a(~(1))
((~(((5)X(4))O(3)))a(~(1)))X(((2)x(1))x(3))

Para diminuir seu tamanho:

  • Ele não realiza nenhuma verificação de erro, tratamento de erros ou validação de entrada, assumindo que a entrada seja sempre válida.
  • É limitado a 99 linhas de entrada.
  • Seu arquivo de entrada deve ser chamado apenas r, sem nenhuma extensão de arquivo no nome.
  • Não faz nenhum esforço para detectar se os parênteses são ou não necessários. Ele pressupõe que eles sempre são necessários e, como essa suposição é falsa, há muito mais parênteses do que o necessário, mas como isso não faz com que as especificações falhem, não há problema.
  • A ordem dos parâmetros para cada operador binário é geralmente imprevisível, pois depende da velocidade em que os valores são propagados e da ordem de varredura da célula. Mas como todos os operadores binários são comutativos, isso não deve ser problema.

Estou certo de que deve ser possível reduzi-lo mais, mas apenas um pouco.

O intérprete é implementado na forma de algum tipo de autômato celular. Ele verifica todos os valores de configuração do campo, repetindo-o quantas vezes forem necessárias até que nenhuma alteração seja detectada.

Aqui está uma versão não destruída:

import java.util.*;

class Wiring {

    int maxLines = 99;
    Map<Integer, String> circuitState;
    boolean finished;

    public static void main(String[] args) {
        new Wiring().interpret();
    }

    Wiring() {

        try {
            // Always read the input from the "r" file, and do not check if it even
            // exists. BTW, the toURL() method is deprecated, but we don't care about
            // this in code-golfing.
            java.io.InputStream stream = new java.io.File("r").toURL().openStream();

            circuitState = new HashMap<>();
            int byteRead = 0, cellX = 0, cellY = 0;

            while ((byteRead = stream.read()) > -1) {

                // Check for line break;
                if (byteRead == 10) {
                    cellY++;
                    cellX = 0;
                    continue;
                }

                // Populate the circuit cell. Precede numbers with an exclamation mark.
                setCircuitCell(cellX, cellY, (byteRead >= '0' & byteRead <= '9' ? "!" : "") + (char) byteRead);
                cellX++;
        } catch (Exception e) {
        }
    }

    void interpret() {
        while (!finished) {
            finished = !finished; // i.e. finished = false;
            for (Map.Entry<Integer, String> entry : circuitState.entrySet()) {
                analyzeCell(entry.getKey(), entry.getValue());
            }
        }

        // Now print the output. To do that scan for cells marked with "$".
        for (String cell : circuitState.values()) {
            if (cell.startsWith("$")) System.out.println(cell.substring(1));
        }
    }

    void analyzeCell(int cellIndex, String cellValue) {
        // Only the cells with a value marked with "!" are worth to analyze.
        if (cellValue == null || !cellValue.startsWith("!")) return;

        // Convert the cellIndex to a bidimensional coordinate.
        int x = cellIndex / maxLines, y = cellIndex % maxLines;

        // Remove the "!".
        cellValue = cellValue.substring(1);

        // Propagate the cell value to neighbouring cells.
        propagateCellData(cellValue, x, y, x - 1, y + 1);
        propagateCellData(cellValue, x, y, x, y + 1);
        propagateCellData(cellValue, x, y, x + 1, y + 1);
        propagateCellData(cellValue, x, y, x - 1, y);
        propagateCellData(cellValue, x, y, x + 1, y);
        propagateCellData(cellValue, x, y, x - 1, y - 1);
        propagateCellData(cellValue, x, y, x, y - 1);
        propagateCellData(cellValue, x, y, x + 1, y - 1);
    }

    void propagateCellData(String cellValue, int sourceX, int sourceY, int targetX, int targetY) {
        String targetContent = circuitState.get(targetX * maxLines + targetY);

        // If the target cell does not exist, just ignore.
        if (targetContent == null) return;

        boolean targetBelowSource = targetY == sourceY + 1;
        boolean targetAboveSource = targetY == sourceY - 1;
        boolean targetRightToSource = targetX == sourceX + 1;
        boolean targetLeftToSource = targetX == sourceX - 1;

        // Propagate horizontally through wires.
        if (isStringContained(targetContent, "-=") & sourceY == targetY) {
            if (targetRightToSource) propagateCellData(cellValue, targetX, targetY, targetX + 1, targetY);
            if (targetLeftToSource) propagateCellData(cellValue, targetX, targetY, targetX - 1, targetY);
        }

        // Propagate vertically.
        if (isStringContained(targetContent, "|=") & sourceX == targetX) {
            if (targetBelowSource) propagateCellData(cellValue, targetX, targetY, targetX, targetY + 1);
            if (targetAboveSource) propagateCellData(cellValue, targetX, targetY, targetX, targetY - 1);
        }

        // Propagate in the diagonal x=-y.
        if (isStringContained(targetContent, "/=")) {
            if (targetLeftToSource & targetBelowSource) {
                propagateCellData(cellValue, targetX, targetY, targetX - 1, targetY + 1);
            }
            if (targetRightToSource & targetAboveSource) {
                propagateCellData(cellValue, targetX, targetY, targetX + 1, targetY - 1);
            }
        }

        // Propagate in the diagonal x=y.
        if (isStringContained(targetContent, "\\=")) {
            if (targetRightToSource & targetBelowSource) {
                propagateCellData(cellValue, targetX, targetY, targetX + 1, targetY + 1);
            }
            if (targetLeftToSource & targetAboveSource) {
                propagateCellData(cellValue, targetX, targetY, targetX - 1, targetY - 1);
            }
        }

        // If we got a dot, store the value there.
        // Do not forget to mark it with "!", so we can rescan it later.
        if (isStringContained(targetContent, ".")) {
            setCircuitCell(targetX, targetY, "!" + cellValue);
            markThatStateChanged();
        }

        // If we got a "~", store the inverted value there.
        // Do not forget to mark it with "!", so we can rescan it later.
        if (isStringContained(targetContent, "~")) {
            setCircuitCell(targetX, targetY, "!~(" + cellValue + ")");
            markThatStateChanged();
        }

        // If we found a binary logical port with one of the values set and
        // we can set the another value, do it. Use "%" and "&" to know which
        // one was already defined.
        // BTW, do not forget to mark it with "!", so we can rescan it later.
        if ((targetContent.charAt(0) == '%' & sourceY == targetY - 1)
                | (targetContent.charAt(0) == '&' & sourceY == targetY + 1))
        {
            setCircuitCell(targetX, targetY,
                    "!(" + cellValue + ")"
                    + targetContent.charAt(1)
                    + "(" + targetContent.substring(2) + ")");
            markThatStateChanged();
        }

        // Found a binary logical port without any value setted, so set it.
        // Use "%" and "&" to mark which one was setted.
        if (isStringContained(targetContent, "OoAaXx")) {
            setCircuitCell(targetX, targetY, (sourceY == targetY + 1 ? "%" : "&") + targetContent + cellValue);
            markThatStateChanged();
        }

        // If we found an output, store the value there.
        // Mark it with "$", so we will print it in the future.
        if (isStringContained(targetContent, ":")) {
            setCircuitCell(targetX, targetY, "$" + cellValue);
            markThatStateChanged();
        }
    }

    void setCircuitCell(int cellX, int cellY, String cellContents) {
        circuitState.put(cellX * maxLines + cellY, cellContents);
    }

    void markThatStateChanged() {
        finished = false;
    }

    boolean isStringContained(String searchingString, String searchTarget) {
        return searchTarget.indexOf(searchingString) > -1;
    }
}

Um pouco mais barato de usar try{}catch(Exception e){}do que dois throws Exception. Provavelmente há outras coisas, mas não tenho idéia de como jogar golfe em Java.
Bob

@ Bob Obrigado, sua sugestão me fez reduzi-lo em 7 caracteres. Além disso, eu poderia reduzir mais quatro.
Victor Stafusa
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.