Interpretare uno schema circuitale


12

La tua sfida è interpretare uno schema circuitale, completo di porte logiche.

Porte logiche (in realtà non è necessario sapere cosa fanno / sono per completare questa sfida):

  • e cancello: a
  • o cancello: o
  • nand gate: A
  • né cancello: O
  • xor gate: x
  • xnor gate: X
  • non cancello: ~

Ogni gate, tranne l'ultimo, accetta due input. Gli input provengono da a .negli angoli in alto a sinistra e in basso a sinistra del quadrato 3 per 3 centrato sul cancello. Per no, l'ingresso è direttamente alla sua sinistra. L'output è .direttamente a destra.

I fili sono rappresentati da -|\/.=

  • - contatta due fili, uno a destra e uno a sinistra: c-c
  • | contatta due fili, uno sopra e uno sotto:

    c
    |
    c
    
  • /e \lavorare come segue:

    c        c
     \      /
      c    c
    
  • . contatta tutti i fili circostanti:

    ccc
    c.c
    ccc
    
  • =è speciale; collega i cavi adiacenti attraverso di esso:

    -=-
    

    collega i due fili. Nel seguente

    \|/
    -=-
    /|\
    

    ogni filo opposto è collegato tra loro, ma non gli altri (questo è dove differisce .).

  • Affinché la corrente scorra, entrambi i fili devono essere collegati all'altro, quindi la |-corrente non scorre.

Esempio di cablaggio:

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

l'ingresso è diviso in due fili e quindi diviso in tre. In questa divisione, il filo inferiore si sposta al centro e la divisione verso il basso del filo superiore appare sul fondo. Successivamente, la parte superiore dei tre fili viene spostata al centro.

Cablaggio di esempio con porte:

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

Formato di input:

  • ogni filo di ingresso sarà etichettato con una cifra. Alla fine (subito prima di newline), ogni uscita verrà etichettata con :(e il filo andrà sempre dritto in essa, cioè -:o .:o =:)
  • l'input sarà sempre valido; non ci saranno anelli o fili che si uniscono senza un cancello. Si noti che possono esserci fili con estremità libere.
  • = sarà usato solo quando necessario.

Formato di output:

  • ogni ingresso è referenziato con il numero corrispondente.
  • viene emessa un'espressione. Ad esempio, se i fili calcolano l'ingresso 1 e l'ingresso 2, l'uscita è 1a2.
  • le funzioni emesse devono corrispondere alle porte logiche all'inizio.
  • per non mostrare, metti ~prima il posto giusto.
  • per più funzioni, utilizzare le parentesi per mostrare l'ordine di esecuzione. Le parentesi possono anche essere utilizzate quando esiste una sola funzione. Per esempio,

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

    ha un possibile output di ~((2x3)A(~1))

  • più uscite devono essere separate da una nuova riga (o equivalente)

Input di esempio:

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

Un possibile output corrispondente:

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

Oooohhhh, interessante! Ci proverò.
cjfaure,

5
Prevedo che ne emergerà un interessante exolang, se lo estendiamo in modo da renderlo Turing completo.
Victor Stafusa,

In caso di un "errore del compilatore" (ovvero cablaggio di ingresso non corretto) cosa dovrebbe fare l'interprete?
Victor Stafusa,

E, se collego direttamente due ingressi? O collegare direttamente due uscite? Oppure inserisci linee aperte nell'output?
Victor Stafusa,

1
@Victor Questo è già simile. Ma sono andato avanti e ne ho creato un altro
Justin il

Risposte:


4

Python 2488 1567 806 706 697 657 653

Yay per 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=='))

Limitazioni e ipotesi

Allo stato attuale, sono supportati solo fino a 9 input: più cifre non vengono gestite correttamente. Poiché le specifiche indicano che gli ingressi sono etichettati con una cifra , non un numero , questo è consentito.


Ingresso e uscita

L'ingresso viene preso tramite l'ingresso standard e l'uscita tramite l'uscita standard.


analisi

Input e output di esempio:

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)))))

Testato qui: http://ideone.com/gP4CIq


L'algoritmo

È fondamentalmente un DFS piuttosto ingenuo dalle uscite. Per ogni output, inizia dal carattere uno alla sua sinistra e traccia il filo, ramificandosi (e aggiungendo all'espressione) ad ogni gate. Quando raggiunge un input, lo aggiunge all'espressione e ritorna all'ultimo punto ramificato, poiché possiamo essere sicuri che la ramificazione non sia possibile senza un gate. E, naturalmente, tutti i casi non validi vengono eliminati. Niente di veramente speciale - e quindi probabilmente è più lungo di quanto avrebbe potuto essere.


Appunti

Probabilmente le dimensioni potrebbero essere ridotte un po 'con qualche ristrutturazione, ma oggi ho trascorso abbastanza tempo su questo. La versione golfizzata manualmente era quella compressa.

La compressione gzip rende il golf interessante, perché una certa memorizzazione nella cache (ad es. d=(-1,0,1)) Occupa effettivamente più spazio di quanto non consenta all'algoritmo di compressione di occuparsene. Tuttavia, ho optato per golfare la versione manuale per quanto possibile piuttosto che ottimizzare per la compressione.


Giocato a golf 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])

Full ungolfed (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)

Che cos'è DFS? Inoltre, lavorare all'indietro dall'output è esattamente quello a cui stavo pensando.
Giustino,

@Quincunx Depth-first-search. Fondamentalmente, ricorsione (o altrimenti usando un costrutto LIFO, una pila) e viaggiando il più lontano possibile lungo un percorso fino a quando non raggiunge un vicolo cieco o l'obiettivo, a quel punto ritorna all'ultimo punto di divergenza e prova gli altri percorsi.
Bob

Buona ipotesi sugli input. Questo è esattamente ciò che intendevo (e ho cercato di esprimerlo per suggerirlo). Tuttavia, il tuo programma funziona 0come una cifra? Che ne dici di scambiare gli ordini in modo che 2venga prima 1, ecc.
Giustino il

@Quincunx Sto usando Python .isdigit(), che è effettivamente equivalente al regex [0-9]per quanto posso dire. È corretto secondo le tue specifiche? Cosa intendi per ordine scambiato? Il modo in cui è implementato, andrà prima sul ramo verso l'alto di qualsiasi gate logico, ma non c'è garanzia di ordinare gli input.
Bob,

isdigit()è consistente. L'ordinamento scambiato significa qualcosa come 2il primo input e 1come il secondo input (ordinati verticalmente).
Giustino,

6

Java: 1523 1512 caratteri

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;}}

Fornisce questo output per l'input di esempio:

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

Per comprimere le sue dimensioni:

  • Non esegue alcun controllo degli errori, gestione degli errori o convalida dell'input, presupponendo che l'input sia sempre valido.
  • È limitato a 99 righe di input.
  • Il suo file di input deve essere chiamato giusto r, senza alcuna estensione nel nome.
  • Non fa alcuno sforzo per rilevare se la parentesi è o non è necessaria. Presuppone che siano sempre necessari, e poiché questa ipotesi è falsa, c'è molta più parentesi del necessario, ma poiché ciò non fa comunque fallire le specifiche, non è un problema.
  • L'ordine dei parametri per ciascun operatore binario è generalmente imprevedibile, in quanto dipende dalla velocità di propagazione dei valori e dall'ordine di scansione delle celle. Ma poiché tutti gli operatori binari sono commutativi, questo non dovrebbe essere un problema.

Sono sicuro che dovrebbe essere possibile ridurlo di più, ma solo un po '.

L'interprete è implementato sotto forma di una sorta di automi cellulari. Esegue la scansione dell'intero valore di impostazione del campo, ripetendolo tutte le volte che è necessario fino a quando non vengono rilevate modifiche.

Ecco una versione non golfata:

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;
    }
}

Molto più economico da usare try{}catch(Exception e){}anziché due throws Exception. Probabilmente ci sono altre cose, ma non ho idea di come giocare a Java.
Bob

@Bob Grazie, il tuo suggerimento mi ha fatto ridurre di 7 caratteri. Inoltre, potrei ridurne altri 4 in più.
Victor Stafusa,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.