Compruebe si tres letras pueden formar un "cubo de Godel-Escher-Bach"

29

Esta pregunta está inspirada en la portada del libro "Godel, Escher, Bach":

El desafío aquí es escribir una función que indique si tres letras dadas pueden producir una escultura en 3D que se pueda leer desde tres lados.

Para este ejercicio, las únicas letras que puede usar son 26 mapas de bits de 5px * 5px:

O en binario (de la A a la Z):

01110  11110  01111  11110  11111  11111  11111  10001  11111  11111  10001  10000  10001  10001  01110  11110  01110  11110  01111  11111  10001  10001  10001  10001  10001  11111
10001  10001  10000  10001  10000  10000  10000  10001  00100  00100  10010  10000  11011  11001  10001  10001  10001  10001  10000  00100  10001  10001  10001  01010  01010  00010
10001  11110  10000  10001  11100  11110  10011  11111  00100  00100  11100  10000  10101  10101  10001  10001  10001  11111  01110  00100  10001  01010  10001  00100  00100  00100
11111  10001  10000  10001  10000  10000  10001  10001  00100  10100  10010  10000  10001  10011  10001  11110  10011  10010  00001  00100  10001  01010  10101  01010  00100  01000
10001  11110  01111  11110  11111  10000  11111  10001  11111  11100  10001  11111  10001  10001  01110  10000  01111  10001  11110  00100  01110  00100  01010  10001  00100  11111

La escultura está formada por tres letras en el siguiente orden:

  • letra uno en la parte superior,
  • letra dos a la izquierda
  • letra tres a la derecha
  • la parte inferior de la letra uno está unida a la parte superior de la letra dos.

Ejemplo:

Su función puede aceptar como entrada tres letras mayúsculas (tres caracteres o tres cadenas de una letra) y generar un booleano (verdadero / falso o 0/1) que indica si la escultura correspondiente puede existir.

Ejemplo:

f("B","E","G") // true  (because if you "sculpt out" B on top + E on the left + G on the right, and watch the three sides of the sculpture, you'll see exactly B, E and G as they are defined)
f("B","G","E") // false (because if you "sculpt out" B on top + G on the left + E on the right, and watch the three sides of the sculpture, you won't see a complete G and a complete E. Their shapes bother each other)

NB: puede volver verdadero incluso si la escultura contiene "píxeles voladores" (cubos o grupo de cubos que están unidos a nada).

Se aplican lagunas estándar.

Más precisamente, no puede usar la entrada externa además de las tres letras, y no puede codificar las 17576 respuestas posibles en su código fuente

¡La respuesta más corta en caracteres en cualquier idioma gana!

Que te diviertas :)

xem
fuente
Sí, es el rompecabezas MU lo que me hizo descubrir el libro, y es la portada del libro lo que me hizo pensar en este desafío. ¿Hay algún problema? ¿Era esto parte de tus 18 hoyos?
xem
2
Hubiera sido una buena opción reemplazar el hoyo 1.;) ... No importa, en todo caso es mi culpa por no levantar algo antes. ¡Es un desafío realmente decente, +1!
Martin Ender
¿Podemos recuperar los datos que definen las formas de las letras de un archivo externo, o eso también debe incluirse en la fuente?
CesiumLifeJacket
Su binario B tiene 0 en la esquina superior izquierda, no 1.
Calvin's Hobbies

Respuestas:

13

Mathematica 423

Agregué una sección llamada "Cómo funciona el bloqueo".

Sin golf

(* Los datos binarios del alfabeto se almacenan como una sola cadena s. Los varsimporta y los convierte en una matriz).

vars=IntegerDigits[#,10,5]&/@Transpose[ImportString[s,"Table"]];
get[char_]:=(ToCharacterCode[char]-64)[[1]];
cube=Flatten[Table[{i,j,k},{i,5},{j,5},{k,5}],2];

(* character slice along axis *)
slice[char_,layer_,axis_,bit_]:=Insert[(Reverse@#),layer,axis]&/@Position[Reverse@vars[[get[char]]],bit]

(* cuboid assembly  *)
charBlocks[{char_,axis_,bit_}]:=Flatten[Table[slice[char,k,axis,bit],{k,5}],1]

(* letters are those whose HOLES should be sculped out of the full cube *)
sculpturePoints[letters_(*{char_,axis_,bit_}*)]:=Complement[cube,Union[Join@@(charBlocks/@letters(*{char,axis,bit}*))]];

collapse[letters_(*{char_,axis_,bit_}*),axis_]:=Union[Reverse/@(Delete[#,axis]&/@sculpturePoints[letters(*{char,axis,bit}*)])](*/.{x_,y_}\[RuleDelayed] {6-x,y}*)

vQ[l_]:=collapse[l,3]==collapse[{l[[1]]},3]\[And]collapse[l,2]==collapse[{l[[2]]},2]\[And]collapse[l,1]==collapse[{l[[3]]},1]

validQ@l_:= vQ[{{l[[1]],3,0},{l[[2]],2,0},{l[[3]],1,0}}]


perspective[letts_,view_:1]:=
Graphics3D[{AbsolutePointSize[10],Cuboid/@sculpturePoints[letts]},
ImageSize-> 120,
ViewPoint-> Switch[view,1,{0,0,\[Infinity]},2,{0,-\[Infinity],0},3,{\[Infinity],0,0},4,Top,5,Front,6,Right,True,{0,0,\[Infinity]}],
PlotLabel-> Switch[view,1,"top orthogonal view",2,"front orthogonal view",3,"right orthogonal view",4,"top close-up view",5,"front close-up view",6,"right close-up view"],
ImagePadding->10]

Ejemplo

¿Es {"B", "G", "E"}válido el cubo ? (es decir, ¿las tres letras se proyectarán correctamente en las paredes?)

validQ[{"B", "G", "E"}]

Falso

Ilustraciones

Las siguientes figuras muestran cómo se representa BGE. La fila superior de figuras toma perspectivas ortogonales, como si el espectador estuviera colocado a distancias infinitas del cubo. La fila inferior muestra cómo se verían los bloques de cerca. Las figuras en 3D se pueden girar manualmente para inspeccionar con precisión dónde se colocan los cubos de la unidad individual.

Se produce un problema con la letra "G". No hay nada que conecte el serif con el resto de la carta.

pts = {{"B", 3, 0}, {"G", 2, 0}, {"E", 1, 0}}
GraphicsGrid@Partition[Table[perspective[pts, view], {view, 1, 6}], 3]

bge


BEG, sin embargo, debería funcionar bien.

 validQ[{"B", "E", "G"}]

Cierto

pts2 = {{"B", 3, 0}, {"E", 2, 0}, {"G", 1, 0}}
GraphicsGrid@Partition[Table[perspective[pts2, view], {view, 1, 6}], 3]

mendigar


¿Cómo funciona el bloqueo?

Disculpe si esto parece obvio, pero tal vez algunas personas quieran visualizar cómo las letras interfieren entre sí, cancelando sus píxeles 3D.

Sigamos lo que sucede con la letra G, en la representación del cubo BGE.

Prestaremos especial atención al voxel (píxel 3D o cubo de la unidad) a continuación. Ese es el píxel que desaparece en el cubo BGE. Es el píxel correspondiente a la Fila 4, Columna 5 en la matriz de bits y en la gráfica de matriz correspondiente.

bloqueo 1


En el plano xy, el píxel corresponde al disco gris en el punto (5,2). Pero debido a que vamos a trabajar en 3D, debemos considerar las 5 posiciones en el eje desde (5,1,2) hasta (5,5,2). Si alguno de esos píxeles sobrevive esculpido por las letras B y E, podremos ver el píxel de interés en la proyección 3D en la pared.

bloqueo 2


Las letras interfieren cuando se eliminan los píxeles del bloque sólido. A la izquierda, la flecha negra representa el tallado de píxeles, correspondiente al bit en la parte inferior derecha; tiene el valor 0 para la letra B. La división elimina el píxel en (5,1,2), junto con los que están directamente arriba y debajo. Quedan cuatro píxeles por contabilizar.

bloqueo 3

Pero como muestra el panel derecho, la letra E esculpe los píxeles restantes de interés, (5,2,2) (5,3,2), (5,4,2) y (5,5,2). (Esto se debe al hecho de que la letra E tiene bits iguales a 0 en la cuarta fila, desde la columna 2 hasta la columna 5). Como resultado, no queda un solo píxel entre los que se necesitaban para garantizar la sombra en el punto (5 , 2) en la pared del fondo (para la letra G). En cambio, habrá un punto brillante correspondiente a un agujero en la letra G! El cubo BGE no es bueno porque representa incorrectamente G.

Golfó 423 caracteres

La función hdesempeñaba el mismo papel que validQen el código no protegido. La función de representación, perspectiveno está incluida porque no contribuye y no es requerida por el desafío.

x=Reverse;q=Flatten;
g@c_:=(ToCharacterCode[c]-64)[[1]];
r[{c_,a_,b_}]:=q[Table[Insert[(x@#),k,a]&/@Position[x@(IntegerDigits[#,10,5]&/@
Transpose[ImportString[s,"Table"]])[[g[c]]],b],{k,5}],1]
p@l_:=Complement[q[Table[{i,j,k},{i,5},{j,5},{k,5}],2],Union[Join@@(r/@l)]];
w[l_,a_]:=Union[x/@(Delete[#,a]&/@p[l])]
v@l_:=w[l,3]==w[{l[[1]]},3]\[And]w[l,2]==w[{l[[2]]},2]\[And]w[l,1]==w[{l[[3]]},1]

h@l_:= v[{{l[[1]],3,0},{l[[2]],2,0},{l[[3]],1,0}}]
DavidC
fuente
Woah, esas vistas en 3D son muy ordenadas! ¿Estás seguro de que el último bloque de código es "UnGolfed"? Me parece golfista. :)
xem
Estás en lo correcto. El bloque final es de golf. Corregí el encabezado. Una cosa genial de las vistas 3D es que son interactivas: la rotación y el zoom se pueden hacer con el mouse.
DavidC
Por cierto, según mi cuenta, hay 564 cubos válidos entre las 15600 permutaciones posibles.
DavidC
Esa es una buena información. ¿Cuánto tiempo te llevó calcular eso? también, 26 * 26 * 26 = 17576, no 15600. ¿O me estoy perdiendo algo?
xem
Usé permutaciones, no tuplas; es decir, no hay letras repetidas. 26 * 25 * 24 = 15600. Le tomó 21 segundos encontrar los 564 casos.
DavidC
12

Prolog, 440 , 414

:- encoding(utf8).
i(I) :- between(0,4,I).
h(T,L,R,X,Y,Z) :- i(X),i(Y),i(Z),I is 4-X,c(T,Z,I),c(L,Z,Y),c(R,X,Y).
f(T,L,R) :- forall((i(U),i(V),I is 4-V),((\+c(T,U,V);h(T,L,R,I,Y,U)),(\+c(L,U,V);h(T,L,R,X,V,U)),(\+c(R,U,V);h(T,L,R,U,V,Z)))).
c(C,X,Y) :- char_code(C,N),i(X),i(Y),Z is X+5*Y+25*(N-65),I is floor(Z/15),O is (Z mod 15),string_code(I,"䙎㹟䘑߯硁䙏縑ԁࠟя摟䠑䠑ᐑ粤Ⴟ䔅┉ё籁垑䙑曓䗱㩑䙏㡏晑䘞䕟㡞縐Ⴄ䙄㩑⩑䒪噑⩊䕤ᅱ粤ࢨ?",V),1 is (V-32)>>O/\1.

El programa se llama así:

?- f('B','E','G').
true.
?- f('B','G','E').
false.

PrologParecía ser una buena opción, ya que es fácil representar el problema en la lógica de primer orden. También Prologproporciona una potente funcionalidad para resolver este tipo de problema.

Sin embargo, dado que el código es golf, creo que debería agregar alguna explicación.

Versión ligeramente golfizada

:- encoding(utf8).
i(I) :- between(0,4,I).
t(C,X,Z) :- I is 4-X,c(C,Z,I).
l(C,Y,Z) :- c(C,Z,Y).
r(C,X,Y) :- c(C,X,Y).
h(T,L,R,X,Y,Z) :- i(X),i(Y),i(Z),t(T,X,Z),l(L,Y,Z),r(R,X,Y).
u(T,L,R) :- forall((i(U),i(V),I is 4-V,c(T,U,V)),h(T,L,R,I,Y,U)).
v(T,L,R) :- forall((i(U),i(V),c(L,U,V)),h(T,L,R,X,V,U)).
w(T,L,R) :- forall((i(U),i(V),c(R,U,V)),h(T,L,R,U,V,Z)).
f(T,L,R) :- u(T,L,R),v(T,L,R),w(T,L,R).
c(C,X,Y) :- char_code(C,N),i(X),i(Y),Z is X+5*Y+25*(N-65),I is floor(Z/15),O is (Z mod 15),string_code(I,"䙎㹟䘑߯硁䙏縑ԁࠟя摟䠑䠑ᐑ粤Ⴟ䔅┉ё籁垑䙑曓䗱㩑䙏㡏晑䘞䕟㡞縐Ⴄ䙄㩑⩑䒪噑⩊䕤ᅱ粤ࢨ?",V),1 is (V-32)>>O/\1.

Las coordenadas correspondientes a los píxeles a cada lado del dado se pueden convertir fácilmente en un sistema de coordenadas 3D. Yo uso T, Ly Rpara el lado superior (1), izquierdo (2) y derecho (3). uy vse usan para las coordenadas en las imágenes:

  • T :(u,v) -> (4-v, ?, u)
  • L :(u,v) -> (?, v, u)
  • R :(u,v) -> (u, v, ?)

Los resultados para cada píxel activo (es decir, negro) se combinan en un conjunto de "píxeles 3D" que se pueden activar sin cambiar la apariencia del objeto desde este lado. La intersección de los conjuntos para cada lado son todos píxeles en 3D, que se pueden activar sin agregar píxeles, lo que obstruiría la vista (es decir, al mirar desde al menos un lado habría un píxel que no debería estar allí).

Todo lo que queda es verificar por cada lado, si hay un píxel en la intersección que bloquea la vista, donde es necesario.

Esto lleva a los predicados en el programa:

  • f : hace la verificación final; toma las letras en la parte superior, el lado izquierdo y el lado derecho
  • u , v y w : realice las comprobaciones, si por cada píxel activo en el lado hay un píxel 3D en la intersección, eso bloquea la vista
  • h : verifica la existencia de un píxel en la intersección
  • t , l , r : comprueba si un píxel 3D puede bloquearse desde el lado superior, izquierdo y derecho.
  • c : comprueba el píxel en la imagen de una letra. La cadena allí puede parecer un poco extraña, pero es solo una forma compacta de almacenar los datos de la imagen. Es simplemente una secuencia de caracteres con los siguientes valores (notación hexadecimal):

    [464e,3e5f,4611,7ef,7841,464f,7e11,501,81f,44f,645f,4811,4811,1411,7ca4,10bf,4505,2509,451,7c41,5791,4651,66d3,45f1,3a51,464f,384f,6651,461e,455f,385e,7e10,10a4,4644,3a51,2a51,44aa,5651,2a4a,4564,1171,7ca4,8a8,3f]
    

    Cada uno de estos caracteres almacena los datos de las filas de 3 píxeles en imágenes de letras (= 15 píxeles). Los píxeles también se reordenan para que los datos se almacenen en una ubicación y no se dividan en varias filas, como los datos del OP.

Formulación matemática

fórmula

Datos de entrada

fórmula

Conversión de píxel en un carácter a conjunto de píxeles 3D que obstruyen la vista de este píxel

fórmula

fórmula

fórmula

Píxeles que se pueden agregar de forma segura (sin obstruir la vista en el lugar equivocado)

fórmula

Comprueba para cada lado, que los píxeles que necesitan ser obstruidos pueden ser obstruidos de manera segura

fórmula

fórmula

fórmula

Combinación de cheques para cada lado

fórmula

fabianista
fuente
1
Yo ... ¿Qué? Esto me parece incomprensible. (+1)
vea el
Santo ... me voy a la cama ...
BrunoJ
¡Impresionante! Gracias por esta respuesta
xem
1
Agradable. Por cierto, creo que el proceso comienza con un bloque cúbico sólido. (Parece pensar que agrega píxeles donde antes no había ninguno). Cada letra elimina algunos píxeles 3D de ese bloque. Entonces, la interferencia surge cuando una letra vecina elimina píxeles que una letra "quería conservar". La interferencia proviene de "píxeles faltantes" en lugar de píxeles adicionales.
DavidC
9

J - 223 197191 char

Una función que toma una lista de tres caracteres como argumento.

(_5#:\".'1b',"#:'fiiifalllvhhhheehhhvhhllvgkkkvnlhhvv444vhhvhhggvhjha44v1111vv848vv248vehhheciiivfjhhedmkkvilll9ggvggu111uo616ou121uha4ahg878ghpljh')((-:0<+/"1,+/"2,:+/)*`(*"1/)/)@:{~_65+3&u:

Este golf se basa en gran medida en una característica poderosa de J llamada rango , que nos da las operaciones de "esculpir" y "vigilar" casi de forma gratuita. Para simplificarlo un poco, el rango se refiere a la dimensionalidad de un sustantivo o de los argumentos naturales de un verbo.

J tiene matrices multidimensionales, y es obvio que, por ejemplo, una matriz 3D se puede interpretar como una sola matriz 3D, o como una lista de matrices, o una matriz 2D de vectores, o una matriz 3D de escalares. Por lo tanto, cada operación en J puede tener su aplicación controlada y cómo distribuirse sobre el argumento. El rango 0 significa aplicar en los escalares, el rango 1 significa aplicar en los vectores, y así sucesivamente.

   1 + 2 + 3 + 4  NB. add these things together
10
   +/ 1 2 3 4     NB. sum the list by adding its items together
10
   i. 3 4         NB. 2D array, with shape 3-by-4
0 1  2  3
4 5  6  7
8 9 10 11
   +/"2 i. 3 4    NB. add the items of the matrix together
12 15 18 21
   0 1 2 3 + 4 5 6 7 + 8 9 10 11    NB. equivalent
12 15 18 21
   +/"1 i. 3 4    NB. now sum each vector!
6 22 38
   +/"0 i. 3 4    NB. now sum each scalar!
0 1  2  3
4 5  6  7
8 9 10 11

Esto se vuelve muy poderoso cuando introduce funciones diádicas (dos argumentos), porque si las formas de los dos argumentos (después de considerar el rango) son agradables, J hará un bucle implícito:

   10 + 1             NB. scalar addition
11
   10 20 30 + 4 5 6   NB. vector addition, pointwise
14 25 36
   10 + 4 5 6         NB. looping! 
14 15 16
   10 20 + 4 5 6      NB. shapes do not agree...
|length error
|   10 20    +4 5 6

Cuando todas sus formas son agradables y puede especificar el rango usted mismo, hay muchas formas de combinar argumentos. Aquí mostramos algunas de las formas en que puede multiplicar una matriz 2D y una matriz 3D.

   n =: i. 5 5
   n
 0  1  2  3  4
 5  6  7  8  9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
   <"2 n *"2 (5 5 5 $ 1)  NB. multiply by 2-cells
+--------------+--------------+--------------+--------------+--------------+
| 0  1  2  3  4| 0  1  2  3  4| 0  1  2  3  4| 0  1  2  3  4| 0  1  2  3  4|
| 5  6  7  8  9| 5  6  7  8  9| 5  6  7  8  9| 5  6  7  8  9| 5  6  7  8  9|
|10 11 12 13 14|10 11 12 13 14|10 11 12 13 14|10 11 12 13 14|10 11 12 13 14|
|15 16 17 18 19|15 16 17 18 19|15 16 17 18 19|15 16 17 18 19|15 16 17 18 19|
|20 21 22 23 24|20 21 22 23 24|20 21 22 23 24|20 21 22 23 24|20 21 22 23 24|
+--------------+--------------+--------------+--------------+--------------+
   <"2 n *"1 (5 5 5 $ 1)  NB. multiply by vectors
+---------+---------+--------------+--------------+--------------+
|0 1 2 3 4|5 6 7 8 9|10 11 12 13 14|15 16 17 18 19|20 21 22 23 24|
|0 1 2 3 4|5 6 7 8 9|10 11 12 13 14|15 16 17 18 19|20 21 22 23 24|
|0 1 2 3 4|5 6 7 8 9|10 11 12 13 14|15 16 17 18 19|20 21 22 23 24|
|0 1 2 3 4|5 6 7 8 9|10 11 12 13 14|15 16 17 18 19|20 21 22 23 24|
|0 1 2 3 4|5 6 7 8 9|10 11 12 13 14|15 16 17 18 19|20 21 22 23 24|
+---------+---------+--------------+--------------+--------------+
   <"2 n *"0 (5 5 5 $ 1)  NB. multiply by scalars
+---------+---------+--------------+--------------+--------------+
|0 0 0 0 0|5 5 5 5 5|10 10 10 10 10|15 15 15 15 15|20 20 20 20 20|
|1 1 1 1 1|6 6 6 6 6|11 11 11 11 11|16 16 16 16 16|21 21 21 21 21|
|2 2 2 2 2|7 7 7 7 7|12 12 12 12 12|17 17 17 17 17|22 22 22 22 22|
|3 3 3 3 3|8 8 8 8 8|13 13 13 13 13|18 18 18 18 18|23 23 23 23 23|
|4 4 4 4 4|9 9 9 9 9|14 14 14 14 14|19 19 19 19 19|24 24 24 24 24|
+---------+---------+--------------+--------------+--------------+

Notarás que esto realmente no graba las letras en la orientación que hace la pregunta, solo las escribe, pero es conveniente para la lógica de rango. A menos que invirtamos o giremos las letras antes de aplicarlas, no funcionará correctamente. Pero corregir cosas así tomaría caracteres preciosos, por lo que codificaremos las letras de tal manera que, cuando J las grabe de forma natural, algunas caras triples estarán en las orientaciones y posiciones relativas correctas. Resulta que la solución más corta es rotar todas las letras un cuarto de vuelta en sentido antihorario. Teniendo en cuenta que la tercera dimensión de J representa el eje de adelante hacia atrás, el diagrama en bruto a continuación muestra por qué funciona este esquema.

visualización del cubo Figura A: Los tres lados del cubo en el que J talla. Figura B: Los tres lados que tienen las letras orientadas como se hace la pregunta.

Esta elección en la codificación ahorra 12 caracteres sobre el método anterior y hace que todo sea más ordenado. El golf real crea el cubo fuera del"1 y "2talla con cierta lógica funky, debido a una optimización no relacionada.

Luego tenemos que revisar las caras. Desde el bloque codificamos como 1s y 0s, sólo podemos resumir lo largo de cada eje en la forma en que queremos (estos son los +/"1, +/"2y+/ bits), ajuste a booleanos ( 0<), y luego comparar a todos ellos directamente a la original de 90 ° - letras convertidas

El esquema de compresión codifica cada fila de 5 píxeles de cada letra como la representación base 32 de un número binario. Al explotar una cantidad de azúcares sintácticos y sobrecargas de operadores, ".'1b',"#:es la forma más corta de convertir la lista de caracteres en números base 36. Bueno, técnicamente, base 32, pero J piensa que es unario, entonces, ¿quién está contando?

El uso está abajo. Tenga en cuenta que las cadenas son matrices de caracteres en J, por lo que 'A','B','C'se puede escribir una lista de tres elementos 'ABC'para abreviar. Además, los booleanos son 1/0.

   NB. can be used inline...
   (_5#:\".'1b',"#:'fiiifalllvhhhheehhhvhhllvgkkkvnlhhvv444vhhvhhggvhjha44v1111vv848vv248vehhheciiivfjhhedmkkvilll9ggvggu111uo616ou121uha4ahg878ghpljh')((-:0<+/"1,+/"2,:+/)*`(*"1/)/)@:{~_65+3&u:'BEG'
1
   NB. or assigned to a name
   geb=:(_5#:\".'1b',"#:'fiiifalllvhhhheehhhvhhllvgkkkvnlhhvv444vhhvhhggvhjha44v1111vv848vv248vehhheciiivfjhhedmkkvilll9ggvggu111uo616ou121uha4ahg878ghpljh')((-:0<+/"1,+/"2,:+/)*`(*"1/)/)@:{~_65+3&u:
   geb 'BGE'
0
Algoritmo de tiburón
fuente
4

Python, 687 682 671

import itertools as t,bz2
s=range(5)
c=dict([(i,1)for i in t.product(*3*[s])])
z=dict([(chr(i+65),[map(int,bz2.decompress('QlpoOTFBWSZTWXndUmsAATjYAGAQQABgADABGkAlPJU0GACEkjwP0TQlK9lxsG7aomrsbpyyosGdpR6HFVZM8bntihQctsSiOLrWKHHuO7ueAyiR6zRgxbMOLU2IQyhAEAdIJYB0ITlZwUqUlAzEylBsw41g9JyLx6RdFFDQEVJMBTQUcoH0DEPQ8hBhXBIYkXDmCF6E/F3JFOFCQed1Saw='.decode('base64')).split('\n')[j].split()[i])for j in s])for i in range(26)])
def m(a,g):
 for e in c:c[e]&=g[e[a]][e[a-2]]
def f(a):
 g=map(list,[[0]*5]*5)
 for e in c:g[e[a]][e[a-2]]|=c[e]
 return g
r=lambda g:map(list,zip(*g)[::-1])
def v(T,L,R):T,L,R=r(r(z[T])),r(z[L]),z[R];m(1,T);m(2,L);m(0,R);return(T,L,R)==(f(1),f(2),f(0))

Llamar con v:

v('B','E','G') => True
v('B','G','E') => False

Todo lo que sigue es de mi versión anterior sin golf que incluye útiles funciones de dibujo. Siéntase libre de usarlo como punto de partida.

import string as s
import itertools as t

az = """01110  11110  01111  11110  11111  11111  11111  10001  11111  11111  10001  10000  10001  10001  01110  11110  01110  11110  01111  11111  10001  10001  10001  10001  10001  11111
10001  10001  10000  10001  10000  10000  10000  10001  00100  00100  10010  10000  11011  11001  10001  10001  10001  10001  10000  00100  10001  10001  10001  01010  01010  00010
10001  11110  10000  10001  11100  11110  10011  11111  00100  00100  11100  10000  10101  10101  10001  10001  10001  11111  01110  00100  10001  01010  10001  00100  00100  00100
11111  10001  10000  10001  10000  10000  10001  10001  00100  10100  10010  10000  10001  10011  10001  11110  10011  10010  00001  00100  10001  01010  10101  01010  00100  01000
10001  11110  01111  11110  11111  10000  11111  10001  11111  11100  10001  11111  10001  10001  01110  10000  01111  10001  11110  00100  01110  00100  01010  10001  00100  11111""".split('\n')

dim = range(len(az))
az = dict([(c, [map(int, az[j].split()[i]) for j in dim]) for i, c in enumerate(s.uppercase)])
cube = dict([(i, 1) for i in t.product(*3*[dim])])

def mask(axis, grid):
    for c in cube:
        if not grid[c[axis]][c[axis - 2]]:
            cube[c] = 0

def face(axis):
    grid = [[0 for j in dim] for i in dim]
    for c in cube:
        if cube[c]:
            grid[c[axis]][c[axis - 2]] = 1
    return grid

def rot(grid):
    return map(list, zip(*grid)[::-1])

def draw(grid, filled='X', empty=' '):
    s = ''
    for y in dim:
        for x in dim:
            s += filled if grid[y][x] else empty
        s += '\n'
    print s

def drawAll():
    print 'TOP:\n'
    draw(rot(rot(face(1))))
    print 'LEFT:\n'
    draw(rot(rot(rot(face(2)))))
    print 'RIGHT:\n'
    draw(face(0))

def valid(top, left, right):
    top, left, right = rot(rot(az[top])), rot(az[left]), az[right]
    mask(1, top)
    mask(2, left)
    mask(0, right)
    return top == face(1)and left == face(2) and right == face(0)

letters = 'BEG'

if valid(*letters):
    print letters, 'is valid.\n'
else:
    print letters, 'is not valid!\n'

drawAll()

Llame validpara ejecutarlo:

valid('B', 'E', 'G') #returns True
valid('B', 'G', 'E') #returns False

En este momento, el código está configurado para probar la validez B E Ge imprimir las caras resultantes:

BEG is valid.

TOP:

XXXX 
X   X
XXXX 
X   X
XXXX 

LEFT:

XXXXX
X    
XXX  
X    
XXXXX

RIGHT:

XXXXX
X    
X  XX
X   X
XXXXX

Ejecutándolo B G Epodemos ver que la G es incorrecta:

BGE is not valid!

TOP:

XXXX 
X   X
XXXX 
X   X
XXXX 

LEFT:

XXXXX
X    
X  XX
X    
XXXXX

RIGHT:

XXXXX
X    
XXX  
X    
XXXXX
Pasatiempos de Calvin
fuente
wow, buen trabajo! +1 para drawAll y la integridad de la respuesta. +1 por usar un algoritmo tan corto. <3 it
xem
@xem ¡Gracias! Finalmente lo jugué al golf. Aunque no pude encontrar la manera de hacer que bz2 descomprima caracteres unicode.
Aficiones de Calvin
+1. Buena respuesta. Espero que más personas voten a favor de los campos de golf más pequeños, como este, porque realmente requiere esfuerzo.
Vectorizado
1
g=[[0 for j in s]for i in s]se puede acortar a g=map(list,[[0]*5]*5). También se puede evitar la sangría de bloques si son una sola instrucción: if c[e]:g[e[a]][e[a-2]]=1.
Bakuriu
@Bakuriu y Bitpwner, gracias por las sugerencias y ediciones :)
Calvin's Hobbies
1

Python 3 + numpy, 327C

from numpy import*
B=hstack([ord(x)>>i&1for x in'옮弟ჹ羂옱쏷)ជ࿂︹缘龌ℿ쓥剴ℌᾄ起츱ꎚㆋឺ௣옮忬⧼ﯠႄ挒⺌ꕆ豈ꪱ袨冊䈑∾Ϣ'for i in range(16)])[:-6].reshape(26,5,5)
T=transpose
def f(*X):
 A=ones((5,5,5));F=list(zip([A,T(A,(1,0,2)),T(fliplr(A),(2,0,1))],[B[ord(x)-65]for x in X]))
 for r,f in F:r[array([f]*5)==0]=0
 return all([all(r.sum(0)>=f)for r,f in F])

Esta solución de golf necesita una biblioteca externa, numpy, que es bastante popular, así que creo que está bien usarla.

La cadena Unicode está en 41 caracteres, mientras que lo mismo en la respuesta del prólogo de @ fabian es 44.

Lo más interesante aquí es que la indexación de la matriz numpy. En a[ix], ixpuede ser una matriz booleana con la misma forma que a. Es lo mismo que decir a[i, j, k] where ix[i, j, k] == True.

Versión sin golf

import numpy as np
table = '옮弟ჹ羂옱쏷)ជ࿂︹缘龌ℿ쓥剴ℌᾄ起츱ꎚㆋឺ௣옮忬⧼ﯠႄ挒⺌ꕆ豈ꪱ袨冊䈑∾Ϣ'

def expand_bits(x):
    return [ord(x) >> i & 1 for i in range(16)]

# B.shape = (26, 5, 5), B[i] is the letter image matrix of the i(th) char
B = np.hstack([expand_bits(x) for x in table])[:-6].reshape(26, 5, 5)

def f(*chars):
    """
    cube:    ----------   axis:           
            /         /|      --------->2  
           /   1     / |     /|            
          /         /  |    / |            
         /         /   |   /  |            
        |---------|  3 |  v   |           
        |         |    /  1   |           
        |    2    |   /       v          
        |         |  /        0         
        |         | /                  
        -----------
    """
    cube = np.ones((5, 5, 5))
    cube_views = [
        cube,
        cube.transpose((1, 0, 2)),  # rotate to make face 2 as face 1
        np.fliplr(cube).transpose(2, 0, 1),  # rotate to make face 3 as face 1
    ]
    faces = [B[ord(char) - ord('A')] for char in chars]
    # mark all white pixels as 0 in cube
    for cube_view, face in zip(cube_views, faces):
        # extrude face to create extractor
        extractor = np.array([face] * 5)
        cube_view[extractor == 0] = 0

    return np.all([
        # cube_view.sum(0): sum along the first axis
        np.all(cube_view.sum(0) >= face)
        for cube_view, face in zip(cube_views, faces)
    ])

Script para comprimir tabla

import numpy as np

def make_chars():
    s = """
01110  11110  01111  11110  11111  11111  11111  10001  11111  11111  10001  10000  10001  10001  01110  11110  01110  11110  01111  11111  10001  10001  10001  10001  10001  11111
10001  10001  10000  10001  10000  10000  10000  10001  00100  00100  10010  10000  11011  11001  10001  10001  10001  10001  10000  00100  10001  10001  10001  01010  01010  00010
10001  11110  10000  10001  11100  11110  10011  11111  00100  00100  11100  10000  10101  10101  10001  10001  10001  11111  01110  00100  10001  01010  10001  00100  00100  00100
11111  10001  10000  10001  10000  10000  10001  10001  00100  10100  10010  10000  10001  10011  10001  11110  10011  10010  00001  00100  10001  01010  10101  01010  00100  01000
10001  11110  01111  11110  11111  10000  11111  10001  11111  11100  10001  11111  10001  10001  01110  10000  01111  10001  11110  00100  01110  00100  01010  10001  00100  11111
""".strip().split('\n')
    bits = np.zeros((26, 5, 5), dtype=np.bool)
    for c_id in range(26):
        for i in range(5):
            for j in range(5):
                bits[c_id, i, j] = s[i][j + c_id * 7] == '1'
    bits = np.hstack([bits.flat, [0] * 7])
    bytes_ = bytearray()
    for i in range(0, len(bits) - 8, 8):
        x = 0
        for j in range(8):
            x |= bits[i + j] << j
        bytes_.append(x)
    chars = bytes_.decode('utf16')
    return chars
Rayo
fuente