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.