Interpréter un schéma de circuit

12

Votre défi est d'interpréter un schéma de circuit, avec des portes logiques.

Portes logiques (vous n'avez pas vraiment besoin de savoir ce qu'elles font / sont pour relever ce défi):

  • et porte: a
  • ou portail: o
  • porte nand: A
  • ni porte: O
  • xor porte: x
  • Porte xnor: X
  • pas de porte: ~

Chaque porte mais la dernière prend deux entrées. Les entrées proviennent d'un .dans les coins supérieur gauche et inférieur gauche du carré 3 x 3 centré sur la porte. Sinon, l'entrée est directement à sa gauche. La sortie se fait .directement vers la droite.

Les fils sont représentés par -|\/.=

  • - contacte deux fils, un à droite et un à gauche: c-c
  • | contacte deux fils, l'un au-dessus et l'autre en dessous:

    c
    |
    c
    
  • /et \travailler comme suit:

    c        c
     \      /
      c    c
    
  • . contacte chaque fil environnant:

    ccc
    c.c
    ccc
    
  • =est spécial; il relie les fils adjacents à travers lui:

    -=-
    

    relie les deux fils. Dans ce qui suit

    \|/
    -=-
    /|\
    

    chaque fil opposé est connecté les uns aux autres, mais pas aux autres (c'est là qu'il diffère .).

  • Pour que le courant circule, deux fils doivent tous deux être connectés à l'autre, de sorte que le |-courant ne circule pas .

Exemple de câblage:

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

l'entrée est divisée en deux fils, puis divisée en trois. Dans cette division, le fil inférieur se déplace vers le milieu et la division vers le bas du fil supérieur apparaît en bas. Ensuite, le haut des trois fils est déplacé vers le milieu.

Exemple de câblage avec portes:

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

Format d'entrée:

  • chaque fil d'entrée sera étiqueté avec un chiffre. À la fin (juste avant la nouvelle ligne), chaque sortie sera étiquetée avec :(et le fil y ira toujours à droite , c'est -:-à- dire ou .:ou =:)
  • l'entrée sera toujours valide; il n'y aura pas de boucles ou de fils se réunissant sans porte. Notez qu'il peut y avoir des fils avec des extrémités libres.
  • = ne sera utilisé qu'en cas de besoin.

Format de sortie:

  • chaque entrée est référencée avec le numéro correspondant.
  • une expression est sortie. Par exemple, si les fils calculent l'entrée 1 et l'entrée 2, la sortie est 1a2.
  • quelles que soient les fonctions produites, elles doivent correspondre aux portes logiques au début.
  • pour ne pas montrer, placez l' ~avant au bon endroit.
  • pour plusieurs fonctions, utilisez des parenthèses pour montrer l'ordre d'exécution. Les parenthèses peuvent également être utilisées lorsqu'il n'y a qu'une seule fonction. Par exemple,

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

    a une sortie possible de ~((2x3)A(~1))

  • les sorties multiples doivent être séparées par un retour à la ligne (ou équivalent)

Exemple d'entrée:

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

Une sortie correspondante possible:

(~1)a(~(3O(4X5))
((1x2)x3)X((~1)a(~(3O(4X5))))
Justin
la source
Oooohhhh, intéressant! Je vais essayer.
cjfaure
5
Je prévois un exolang intéressant qui en sortira, si nous l'étendons de manière à le rendre Turing-complet.
Victor Stafusa
En cas d '"erreur de compilation" (c'est-à-dire un câblage d'entrée mal formé), que doit faire l'interprète?
Victor Stafusa
Et, si je connecte directement deux entrées? Ou connecter directement deux sorties? Ou entrer des lignes ouvertes dans la sortie?
Victor Stafusa
1
@Victor C'est déjà similaire. Mais je suis allé de l'avant et j'en ai créé un autre
Justin

Réponses:

4

Python 2488 1567 806 706 697 657 653

Ouais pour 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=='))

Limites et hypothèses

En l'état, seules 9 entrées au maximum sont prises en charge - plusieurs chiffres ne sont pas traités correctement. Comme la spécification indique que les entrées sont étiquetées avec un chiffre et non un chiffre , cela est autorisé.


Entrée et sortie

L'entrée est prise via l'entrée standard et la sortie via la sortie standard.


Essai

Exemple d'entrée et de sortie:

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

Testé ici: http://ideone.com/gP4CIq


L'algorithme

C'est fondamentalement un DFS plutôt naïf des sorties. Pour chaque sortie, il part du caractère un à sa gauche et trace le fil, se ramifiant (et s'ajoutant à l'expression) à chaque porte. Quand il atteint une entrée, il l'ajoute à l'expression et revient sur le dernier point qu'il a ramifié, car nous pouvons être sûrs que la ramification n'est pas possible sans porte. Et bien sûr, tous les cas invalides sont rejetés. Rien de vraiment spécial - et c'est donc probablement plus long qu'il aurait pu l'être.


Remarques

La taille pourrait probablement être assez réduite avec quelques restructurations, mais j'ai passé assez de temps là-dessus pour aujourd'hui. La version jouée manuellement était celle compressée.

La compression gzip rend le golf intéressant, car une certaine mise en cache (par exemple d=(-1,0,1)) prend en fait plus d'espace que de laisser l'algorithme de compression s'en occuper. Cependant, j'ai choisi de jouer au golf la version manuelle autant que possible plutôt que d'optimiser pour la compression.


Joué manuellement au golf ( 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])

Plein non golfé (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
la source
Qu'est-ce que DFS? De plus, travailler à l'envers à partir de la sortie est exactement ce à quoi je pensais.
Justin
@Quincunx Profondeur-première-recherche. Fondamentalement, la récursion (ou autrement en utilisant une construction LIFO, une pile) et en voyageant autant que possible le long d'un chemin jusqu'à ce qu'elle atteigne une impasse ou le but, auquel cas elle revient au dernier point de divergence et essaie les autres chemins.
Bob
Bonne hypothèse sur les entrées. C'est exactement ce que je voulais dire (et j'ai essayé de le formuler pour le suggérer). Cependant, votre programme fonctionne-t-il avec 0un chiffre? Que diriez-vous d'échanger des commandes pour que cela 2arrive avant 1, etc.
Justin
@Quincunx J'utilise Python .isdigit(), qui est effectivement équivalent à l'expression régulière [0-9]pour autant que je sache. Est-ce correct selon vos spécifications? Que voulez-vous dire par échange de commande? De la façon dont il est implémenté, il se dirigera d'abord sur la branche ascendante de toute porte logique, mais il n'y a aucune garantie de classement des entrées.
Bob
isdigit()est consistent. L'ordre échangé signifie quelque chose comme 2comme la première entrée et 1comme la deuxième entrée (trié verticalement).
Justin
6

Java: 1523 1512 caractères

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

Il donne cette sortie pour l'entrée échantillon:

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

Afin de serrer sa taille:

  • Il n'effectue aucune vérification d'erreur, traitement d'erreur ou validation d'entrée, en supposant que l'entrée est toujours valide.
  • Est limité à 99 lignes d'entrée.
  • Son fichier d'entrée doit être appelé juste r, sans aucune extension de fichier dans le nom.
  • Il ne fait aucun effort pour détecter si les parenthèses sont ou ne sont pas nécessaires. Cela suppose qu'ils sont toujours nécessaires, et puisque cette hypothèse est fausse, il y a beaucoup plus de parenthèses que nécessaire, mais comme cela ne fait pas échouer la spécification de toute façon, ce n'est pas un problème.
  • L'ordre des paramètres pour chaque opérateur binaire est en général imprévisible, car il dépend de la vitesse de propagation des valeurs et de l'ordre de balayage des cellules. Mais comme tous les opérateurs binaires sont commutatifs, cela ne devrait poser aucun problème.

Je suis sûr qu'il devrait être possible de le réduire davantage, mais juste un peu.

L'interpréteur est implémenté sous la forme d'une sorte d'automate cellulaire. Il scanne l'intégralité des valeurs de réglage sur place, en les répétant autant de fois que nécessaire jusqu'à ce qu'aucun changement ne soit détecté.

Voici une version non golfée:

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
la source
Un tout petit peu moins cher à utiliser try{}catch(Exception e){}que deux throws Exception. Il y a probablement d'autres choses, mais je ne sais pas comment jouer au golf à Java.
Bob
@Bob Merci, votre suggestion m'a fait réduire 7 caractères. De plus, je pourrais réduire encore 4 de plus.
Victor Stafusa