Interpretieren Sie einen Schaltplan

12

Ihre Herausforderung besteht darin, einen Schaltplan mit Logikgattern zu interpretieren.

Logikgatter (Sie müssen eigentlich nicht wissen, was diese tun / sind, um diese Herausforderung abzuschließen):

  • und Tor: a
  • oder Tor: o
  • und Tor: A
  • noch Tor: O
  • xor gate: x
  • xnor gate: X
  • nicht Tor: ~

Jedes Tor außer dem letzten hat zwei Eingänge. Die Eingänge befinden sich .in der oberen linken und unteren linken Ecke des 3 x 3-Quadrats, zentriert auf dem Tor. Für nicht, ist der Eingang direkt auf der linken Seite. Die Ausgabe erfolgt .direkt nach rechts.

Drähte werden durch dargestellt -|\/.=

  • - kontaktiert zwei Drähte, einen rechts und einen links: c-c
  • | kontaktiert zwei Drähte, einen oben und einen unten:

    c
    |
    c
    
  • /und \arbeite wie folgt:

    c        c
     \      /
      c    c
    
  • . kontaktiert jeden umgebenden Draht:

    ccc
    c.c
    ccc
    
  • =ist speziell; es verbindet benachbarte Drähte darüber:

    -=-
    

    verbindet die beiden Drähte. Im Folgenden

    \|/
    -=-
    /|\
    

    Jeder gegenüberliegende Draht ist miteinander verbunden, aber nicht die anderen (hier unterscheidet er sich von den anderen .).

  • Damit der Strom fließt, müssen zwei Drähte mit dem anderen verbunden sein, damit kein|- Strom fließt.

Verdrahtungsbeispiel:

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

Der Eingang ist in zwei Drähte und dann in drei aufgeteilt. Bei dieser Aufteilung bewegt sich der untere Draht in die Mitte, und die Aufteilung des oberen Drahtes nach unten wird unten angezeigt. Als nächstes wird die Oberseite der drei Drähte in die Mitte verschoben.

Beispielverdrahtung mit Toren:

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

Eingabeformat:

  • Jedes Eingangskabel ist mit einer Ziffer gekennzeichnet. Am Ende (direkt vor dem Zeilenumbruch) wird jeder Ausgang mit :(und der Draht geht immer direkt hinein, dh -:oder .:oder =:) gekennzeichnet.
  • Die Eingabe ist immer gültig. Es gibt keine Schleifen oder Drähte, die sich ohne Tor verbinden. Beachten Sie, dass es Drähte mit losen Enden geben kann.
  • = wird nur bei Bedarf verwendet.

Ausgabeformat:

  • Jeder Eingang wird mit der entsprechenden Nummer referenziert.
  • ein Ausdruck wird ausgegeben. Wenn die Drähte beispielsweise Eingang 1 und Eingang 2 berechnen, lautet der Ausgang 1a2.
  • Alle ausgegebenen Funktionen müssen den Logikgattern am Anfang entsprechen.
  • um das nicht zu zeigen, stelle die ~vor die richtige stelle .
  • Verwenden Sie für mehrere Funktionen Klammern, um die Ausführungsreihenfolge anzuzeigen. Klammern können auch verwendet werden, wenn nur eine Funktion vorhanden ist. Beispielsweise,

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

    hat eine mögliche Ausgabe von ~((2x3)A(~1))

  • mehrere Ausgaben müssen durch einen Zeilenumbruch (oder ein gleichwertiges Zeichen) getrennt werden

Beispieleingabe:

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

Ein möglicher entsprechender Ausgang:

(~1)a(~(3O(4X5))
((1x2)x3)X((~1)a(~(3O(4X5))))
Justin
quelle
Oooohhhh, interessant! Ich versuche es mal.
cjfaure
5
Ich sehe ein interessantes Exolang voraus, wenn wir es so erweitern, dass es Turing-complete wird.
Victor Stafusa
Was kann der Interpreter im Falle eines "Compiler-Fehlers" (dh einer fehlerhaften Eingangsverdrahtung) tun?
Victor Stafusa
Und wenn ich direkt zwei Eingänge verbinde? Oder direkt zwei Ausgänge verbinden? Oder offene Zeilen in den Ausgang eingeben?
Victor Stafusa
1
@ Victor Das ist schon ähnlich. Aber ich ging voran und schuf einen anderen
Justin

Antworten:

4

Python 2488 1567 806 706 697 657 653

Yay für 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=='))

Einschränkungen und Annahmen

Momentan werden nur bis zu 9 Eingaben unterstützt - Mehrfachziffern werden nicht korrekt verarbeitet. Da die Spezifikation angibt, dass Eingaben mit einer Ziffer und nicht mit einer Zahl gekennzeichnet sind , ist dies zulässig.


Ein- und Ausgabe

Die Eingabe erfolgt über den Standardeingang und die Ausgabe über den Standardausgang.


Testen

Probenein- und ausgabe:

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

Hier getestet: http://ideone.com/gP4CIq


Der Algorithmus

Es ist im Grunde eine ziemlich naive DFS von den Ausgängen. Für jede Ausgabe beginnt sie mit dem Zeichen eins links von ihr und folgt der Leitung, wobei an jedem Gatter eine Verzweigung erfolgt (und der Ausdruck hinzugefügt wird). Wenn es eine Eingabe erreicht, fügt es sie dem Ausdruck hinzu und geht zum letzten Verzweigungspunkt zurück, da wir sicher sein können, dass eine Verzweigung ohne ein Gatter nicht möglich ist. Und natürlich werden ungültige Fälle verworfen. Nichts wirklich Besonderes - und deshalb ist es wahrscheinlich länger, als es hätte sein können.


Anmerkungen

Die Größe könnte mit einigen Umstrukturierungen wahrscheinlich ein wenig reduziert werden, aber ich habe heute genug Zeit dafür aufgewendet. Die manuell golfene Version war die komprimierte.

Die gzip-Komprimierung macht das Golfen interessant, weil bestimmte Caching-Vorgänge (z. B. d=(-1,0,1)) tatsächlich mehr Platz beanspruchen, als der Komprimierungsalgorithmus dafür sorgen würde. Ich entschied mich jedoch dafür, die manuelle Version so weit wie möglich zu spielen, anstatt die Komprimierung zu optimieren.


Manuelles Golfen ( 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])

Voll 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)
Bob
quelle
Was ist DFS? Von der Ausgabe aus rückwärts zu arbeiten ist genau das, woran ich gedacht habe.
Justin
@Quincunx Tiefensuche. Grundsätzlich Rekursion (oder anderweitige Verwendung eines LIFO-Konstrukts, eines Stapels) und möglichst weites Fahren entlang eines Pfades, bis er eine Sackgasse oder das Ziel erreicht. An diesem Punkt kehrt er zum letzten Divergenzpunkt zurück und probiert die anderen Pfade aus.
Bob
Gute Annahme bei den Eingängen. Genau das habe ich gemeint (und ich habe versucht, es auszudrücken, um das nahezulegen). Arbeitet Ihr Programm jedoch mit 0einer Ziffer? Wie wäre es mit einer getauschten Bestellung, die früher 2kommt 1, usw.
Justin
@Quincunx Ich verwende Python's .isdigit(), was meiner Meinung nach praktisch dem regulären Ausdruck entspricht [0-9]. Stimmt das mit Ihrer Spezifikation überein? Was meinen Sie mit getauschten Bestellungen? So wie es implementiert ist, wird es zuerst in den Aufwärtszweig eines logischen Gatters geleitet, aber es gibt keine Garantie für die Reihenfolge der Eingänge.
Bob
isdigit()ist konsistent. Vertauschte Reihenfolge bedeutet so etwas wie 2die erste Eingabe und 1die zweite Eingabe (vertikal sortiert).
Justin
6

Java: 1523 1512 Zeichen

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

Es gibt diese Ausgabe für die Beispieleingabe:

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

Um seine Größe zusammenzudrücken:

  • Es werden keine Fehlerüberprüfungen, Fehlerbehandlungen oder Eingabevalidierungen durchgeführt, vorausgesetzt, die Eingabe ist immer gültig.
  • Ist auf 99 Eingabezeilen begrenzt.
  • Die Eingabedatei muss einfach rohne Dateierweiterung im Namen aufgerufen werden .
  • Es wird kein Aufwand betrieben, um festzustellen, ob die Klammern erforderlich sind oder nicht. Es wird davon ausgegangen, dass sie immer notwendig sind, und da diese Annahme falsch ist, gibt es viel mehr Klammern als erforderlich, aber da dies die Spezifikation nicht zum Scheitern bringt, ist dies kein Problem.
  • Die Reihenfolge der Parameter für jeden binären Operator ist im Allgemeinen nicht vorhersehbar, da sie von der Geschwindigkeit abhängt, mit der die Werte übertragen werden, und von der Reihenfolge der Zellabtastung. Da jedoch alle binären Operatoren kommutativ sind, sollte dies kein Problem sein.

Ich bin mir sicher, dass es möglich sein sollte, es weiter zu reduzieren, aber nur ein bisschen.

Der Interpreter ist in Form einer Art zellularer Automaten implementiert. Es scannt die gesamten Feldeinstellungswerte und wiederholt sie so oft, bis keine Änderungen mehr festgestellt werden.

Hier ist eine ungolfed Version:

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;
    }
}
Victor Stafusa
quelle
Winzig billiger try{}catch(Exception e){}als zwei throws Exception. Es gibt wahrscheinlich andere Dinge, aber ich habe keine Ahnung, wie man Java spielt.
Bob
@ Bob Danke, dein Vorschlag hat mich dazu gebracht, ihn um 7 Zeichen zu reduzieren. Außerdem könnte ich noch 4 weitere reduzieren.
Victor Stafusa