Diseño del lenguaje: coincidencia de patrones 2D


49

Este es el desafío quincenal # 6 . Tema: Diseño del lenguaje

Hay una sala de chat para este desafío. ¡Ven y únete a nosotros si quieres discutir ideas!

Y ahora para algo completamente diferente...

Esta quincena, queremos experimentar con un nuevo tipo de desafío. En este desafío, ¡estarás diseñando un lenguaje! La coincidencia de patrones es un problema muy común en la programación y, a menudo, muy útil para el golf de código. Las expresiones regulares, por ejemplo, se pueden usar para detectar un patrón en una línea de texto. Sin embargo, no existen métodos establecidos para describir y detectar patrones bidimensionales.

El reto

Debe diseñar un lenguaje de coincidencia de patrones, que permita la descripción de patrones bidimensionales en bloques de texto. El modo de operación de su lenguaje será similar a las expresiones regulares (aunque su lenguaje no tiene que tener nada en común con regex de lo contrario):

  • Como entrada, recibirá un bloque rectangular de texto. Puede suponer que el texto consta solo de caracteres ASCII imprimibles (0x20 a 0x7E), así como de nuevas líneas (0x0A) para separar las filas de la cuadrícula.
  • Si se puede encontrar una coincidencia, de acuerdo con la descripción del patrón, como cualquier subconjunto de este bloque de texto, esta coincidencia debe devolverse o imprimirse. Si la coincidencia puede ser no rectangular, debe rellenarse a un área rectangular con algún carácter reservado. Si hay varias coincidencias válidas, puede decidir cómo se elige la coincidencia devuelta (la más grande, la más pequeña, la primera, etc.).

Para algunas aplicaciones, puede ser útil si su implementación puede devolver la posición de una coincidencia en lugar de la coincidencia misma, pero esto no es un requisito.

Como mínimo, su idioma debería poder hacer coincidir un patrón como una subregión contigua y rectangular de su entrada.

Su respuesta debe contener:

  • Una descripción del idioma.
  • Una implementación de trabajo . Puede ser un programa o un conjunto de funciones / clases en el idioma que elija.
  • Debe demostrar su idioma mostrando cómo se puede usar para resolver los ejemplos que se proporcionan a continuación . Su idioma no tiene que ser capaz de hacer coincidir todos ellos, pero debe poder hacer coincidir al menos 8 de estos. Si su idioma puede hacer algo elegante que no pensamos, siéntase libre de incluirlo también.

Si su respuesta se basa en ideas existentes, está bien, pero por favor déle crédito a su debido tiempo.

Extensiones

Lo anterior describe el mínimo que debe cumplir una presentación válida. Sin embargo, varias generalizaciones podrían hacer que un lenguaje de coincidencia de patrones sea aún más útil, incluidos, entre otros:

  • Ser capaz de anclar el patrón a uno o más bordes, de modo que se pueda verificar si toda la región de entrada tiene un cierto patrón.
  • Produciendo todos los partidos en lugar de solo uno. Puedes elegir la semántica de las coincidencias superpuestas.
  • Tomando texto no rectangular como entrada.
  • Permitir que los patrones especifiquen coincidencias no rectangulares. En tal caso, la salida debe rellenarse a un rectángulo con algún carácter reservado.
  • Permitir patrones para especificar coincidencias con agujeros.
  • Permitir coincidencias no contiguas, como dos caracteres que aparecen con un cierto desplazamiento.
  • Fácil especificación de rotaciones y reflexiones.
  • Opcionalmente, trate la entrada cíclicamente como un cilindro o un toro, de modo que los bordes opuestos se consideren adyacentes.

Puntuación

El objetivo principal de este desafío es producir un lenguaje eficaz de coincidencia de patrones 2D que podría utilizarse en el futuro. Como tal, un sistema de puntaje como "la longitud combinada más corta para resolver los ejemplos" llevaría a la codificación de ciertas funciones a expensas de la usabilidad general. Por lo tanto, hemos decidido que este desafío se ejecuta mejor como un concurso de popularidad. La presentación con más votos netos gana. Si bien no podemos forzar cómo vota la gente, aquí hay algunas pautas sobre lo que los votantes deberían buscar idealmente:

  • Expresividad. ¿Puede el lenguaje resolver una variedad de problemas, incluso más allá de los ejemplos presentados en esta pregunta? ¿Es compatible con alguna de las extensiones sugeridas?
  • Legibilidad. ¿Qué tan intuitiva es la notación (al menos para las personas que conocen la sintaxis básica)?
  • Golfitude Esto sigue siendo CodeGolf.SE. Para los propósitos de este sitio, por supuesto, sería bueno tener un lenguaje coincidente que necesite muy poco código para describir un patrón.

Problemas de ejemplo

El siguiente fragmento de pila muestra 16 problemas de ejemplo con los que un lenguaje de coincidencia de patrones 2-D podría solucionar. Cada ejemplo contiene una breve descripción del problema y luego suele ir seguido de un ejemplo de entrada donde se puede encontrar una coincidencia y un ejemplo donde no se puede encontrar una coincidencia (si corresponde).

Como se indicó anteriormente, su idioma solo necesita poder resolver 8 de estos problemas. Todo lo demás es opcional, pero, por supuesto, debería aumentar la cantidad de votos que obtenga.

(No, no es necesario que leas ese código HTML. Presiona el botón "Ejecutar fragmento de código" para que tu navegador lo muestre correctamente, que también puedes ver a pantalla completa).


¿Existe un límite de tiempo general para estos problemas? Estoy muy interesado en resolver esto, pero estoy muy ocupado, podría llevarme 2 semanas fácilmente.
Devon Parsons

77
@DevonParsons No hay fecha límite de entrada.
PhiNotPi

Parece interesante, especialmente desde que creó una nueva etiqueta para esto. Creo que debería haber puntos de bonificación por crear un lenguaje 2D para ello.
mbomb007

1
@ mbomb007 Hay puntos de bonificación por crear un lenguaje 2D. Estoy bastante seguro de que obtendría una cantidad decente de votos a favor. ;)
Martin Ender

@ MartinBüttner Ni siquiera sé cómo crear un lenguaje, de verdad. ¿Podría ser algo tan (simple) como crear un programa Python que tome un archivo del código de su nuevo idioma (e interpretarlo / ejecutarlo según su sintaxis definida) y producir resultados?
mbomb007

Respuestas:


32

SnakeEx

¡Resuelve 15/16 problemas hasta ahora!

Intérprete en línea ! - Especificación completa del idioma - Fuente Javascript

captura de pantalla del intérprete

La idea detrás de este lenguaje es definir 'serpientes' que se mueven alrededor de los caracteres de verificación de texto usando una sintaxis tipo regex.

Un programa en SnakeEx consiste en una lista de definiciones para serpientes que utilizan diferentes secuencias de comandos. Las serpientes pueden engendrar otras serpientes usando estas definiciones, que es donde SnakeEx obtiene la mayor parte de su poder: podemos hacer coincidir las estructuras de ramificación e incluso hacer recursividad (ver ejemplo de coincidencia de Paren).

Esencialmente, cada programa se verá como un conjunto de expresiones regulares, pero con la adición de comandos de dirección de la forma <dir>que cambian la dirección de la serpiente, y los comandos de llamada de la forma {label<dir>params}que generan más serpientes.

Para un punto de entrada, el intérprete genera una serpiente usando la primera definición, moviéndose hacia la derecha.

No es terriblemente conciso, pero es muy poderoso y (creo) bastante legible.

Actualizaciones

  • Cambiado para no lógico y ~ para no marcar coincidencias
  • Agregado <!>para resolver colinear
  • Cruces a juego resueltas
  • Tableros de ajedrez resueltos de una manera menos terrible
  • Sintaxis de cierre acotada añadida %{min,max}
  • Ejemplo de recursión agregado

Soluciones

15 resueltos, 1 en progreso

¡Puede probar estos programas fácilmente con el Intérprete en línea vinculado anteriormente!

Problema 1 - Encontrar tableros de ajedrez

m:{v<R>2}{h<>1}
v:{c<L>A1}+
h:{c<R>A2}+
c:_?(#_)+#?

Para una introducción detallada, comience en el problema 3.

Problema 2: verificación de tableros de ajedrez

m:{v<R>2}{h<>1}
v:${c<L>A1}+$
h:${c<R>A2}+$
c:$_?(#_)+#?$

Terminar el libro de las serpientes apropiadas con el símbolo de fuera de los límites $es una forma de hacer que un programa coincida solo con la entrada completa.

Problema 3: detectar un rectángulo de dígitos

m:{c<R>A1}%{2,}
c:[0-9]%{2,}

La mserpiente se mueve hacia la derecha, generando al menos 2 serpientes ( %{2,}es un cierre que significa "2 al infinito") usando la definición c ( c) y moviéndose hacia la derecha ( <R>), o más bien hacia abajo en este caso, porque todas las direcciones son relativas a la serpiente actual. El Aparámetro es el azúcar que solo especifica que la serpiente de desove debe moverse después de la llamada. El 1parámetro es cómo restringimos las coincidencias a los rectángulos: los parámetros numéricos colocan a las serpientes en "grupos". Una partida no cuenta a menos que todas las serpientes del mismo grupo viajen exactamente a la misma distancia.

Problema 4 - Encontrar una palabra en una búsqueda de palabras

m:<*>GOLF

El comando de dirección <*>especifica que la serpiente debe girar en cualquier dirección diagonal u ortogonal. Luego, busca la expresión regular simple.

Problema 5 - Detectar entradas cuadradas

m:{v<R>1}{h<>1}
v:${c<L>A1}+$
h:${c<R>A1}+$
c:$.+$

La clave aquí es el carácter especial $, que solo coincide si la serpiente está fuera de los límites. Engendramos una serpiente horizontal y una vertical; cada uno de ellos genera más serpientes a medida que corre a lo largo del borde, y todos están en el mismo grupo y deben tener la misma longitud.

Problema 6 - Encuentra planeadores en un juego de la vida

m:<+>[({l1<R>A}{l2<R>A}{l3<R>})({l1<L>A}{l2<L>A}{l3<L>})]
l1:##\.
l2:[(#\.)(\.#)]#
l3:#\.\.

mcomienza en cualquiera de las cuatro direcciones ortogonales ( <+>), logrando la rotación. Luego, se ve a la izquierda o derecha para las tres filas en orden, logrando la reflexión.

(Tenga en cuenta que he reemplazado los espacios con puntos solo porque las áreas de texto HTML utilizadas en mi intérprete parecen hacer cosas estúpidas si tienen múltiples espacios seguidos)

Problema 7 - Match Portales abisales

m:{e<R>A1}{d<R>A1}%{2,22}{e<R>1}
e:~.X%{3,22}~.
d:X\.+X

La mserpiente se mueve hacia la derecha, generando una serpiente para verificar el borde izquierdo, 2-22 serpientes para verificar las columnas del medio, y finalmente una serpiente para verificar el borde derecho. El ~operador indica que lo que sigue se debe verificar pero no se debe marcar como parte de la solución.

¡Los cierres limitados ahora nos permiten resolver este problema de forma completa y adecuada!

Problema 8 - Colocación del cofre de Minecraft

m:~{s<>}~!{d<+>}\.
s:<+>.<BR>([$\.]<R>)%{3}
d:.<+>CC

Aquí usamos un no lógico ( !), que coincide si y solo si el siguiente token no coincide con nada. La declaración ddetecta un cofre doble en una dirección particular, por !{d<+>}lo que se asegura de que no haya cofres dobles en ninguna dirección ortogonal. sse mueve en un pequeño diamante alrededor del cuadrado actual, verificando que al menos 3 de esos espacios estén vacíos o fuera del tablero. El final \.finalmente coincide con el cuadrado, suponiendo que todas las condiciones anteriores tuvieron éxito.

Problema 9 - Alineación horizontal y vertical

m:<R>?#~.*#

La serpiente mopcionalmente gira a la derecha ( <R>?) antes de hacer coincidir la secuencia. .es un comodín, como en regex.

Problema 10 - Puntos colineales

m:<!>#~.*#~.*#

¡Con la adición de la <!>dirección, podemos resolver esto ahora! <!>es como, <+>pero en lugar de ramificarse en cuatro direcciones, se ramifica en todas las direcciones posibles.

Problema 12 - Evita la letra Q

m:{h<R>A}%{4}
h:[^Qq]%{4}

Solo genera 4 serpientes que buscan cuatro caracteres que no son la letra Q.

Problema 13 - Minería de diamantes

m:{tl<RB>1}{tr<RF>1}
tl:X/*{bl<L>1}X
tr:X\\*{br<R>1}X
bl:X\\*X
br:X/*X

Este es muy bueno. mgenera dos serpientes, una yendo hacia atrás a la derecha y otra hacia adelante a la derecha. Cada uno de ellos sigue las barras a una X, luego genera otra serpiente en ángulo recto a su dirección actual, que sigue las barras a la X inferior.

Problema 14 - Cruces coincidentes

m:{a<R>A}+{b<R>A}+{a<R>A}+
a:{e<>P1}{c<>P2}{e<>P3}
b:{c<>P1}{c<>P2}{c<>P3}
e:\.+
c:#+

Esta es la primera vez que uso el Pparámetro iggyback. Normalmente las serpientes son independientes, pero si realiza una llamada con este parámetro, la serpiente que llama se moverá con la persona que llama. Entonces e2puede verificar una secuencia de '.', Luego una secuencia de '#', luego otra secuencia de '.', Y hacer que todas sean llamadas separadas para que podamos agruparlas con '1,' 2 'y' 3 ' , obligando a sus longitudes a coincidir.

Problema 15: unir una palabra en un tablero de Boggle

m{I}:<*>p<*>a<*>n<*>a<*>m<*>a

Simple, si es prolijo. IEl parámetro especifica mayúsculas y minúsculas (podemos especificar parámetros en las definiciones y en las llamadas). La serpiente gira en cualquier dirección, coincide con el personaje, gira de nuevo, y así sucesivamente.

m{EI}:<*>p<*>a<*>n<*>a<*>m<*>a

Esta es la versión sin caracteres de reutilización. El Eparámetro xclusive prohíbe que la serpiente coincida con cualquier personaje que ya haya sido marcado, al igual que los rastros de baba de feersum.

Problema 16 - Envuelva los bordes

m{W}:{c<R>WA}%{3}
c:###

El Wparámetro permite que la serpiente se ajuste cuando se ejecuta fuera de los límites. También tenemos Hy Vpermitir solo envoltura horizontal o vertical.

Extra - Solucionador de laberintos

m{E}:$(<P>\.)+$

Resuelve un laberinto ASCII donde el piso transitable es períodos. La <P>dirección significa hacia adelante, izquierda o derecha (azúcar para [<F><L><R>]).

Extra - Emparejamiento Paren

m:\(~{r<>P}\)
r:[^\(\)]*(\({r<>P}\))?[^\(\)]*

Todavía no he descubierto cómo hacer Prelude, ¡pero aquí hay un primer paso! Aquí uso la rserpiente de forma recursiva para hacer coincidir los paréntesis correspondientes comprobando que no haya paréntesis sin igual entre ellos.

Extra - Topología ASCII: Bucles de conteo


¿Consideraría agregar una sintaxis para que este lenguaje pueda reemplazar en lugar de solo coincidir? Quiero usar alguna solución a este desafío para escribir una entrada para codegolf.stackexchange.com/questions/51231/… pero ninguna solución aquí encuentra encontrar y reemplazar. (Sé que mi respuesta no sería válida debido a las reglas de tiempo de las especificaciones de idioma)
Sparr

@Sparr. No es una mala idea, sin duda sería más útil para el golf de código. No estoy seguro de cuándo tendré tiempo para hacerlo yo mismo, pero lo tendré en cuenta.
BMac

3
por separado: la sintaxis para los cambios de dirección es confusa. Debido a que la serpiente progresa después de emparejar un personaje, parece que tengo que hacer que mi dirección cambie un personaje antes de que tenga sentido para mí. ejemplo: cadena "ABC \ nDEF" y quiero hacer coincidir la pieza de tetris en forma de L definida por ABCF, quiero escribir mi serpiente como "m: ABC <R> F" pero tengo que escribir "m: AB <R> CF "en su lugar. Entiendo que esto coincide con la especificación, pero me parece muy contradictorio.
Sparr

Tengo una solución parcial para la sintaxis del preludio. El único problema es que no puedo hacer que no coincida si no coincide con toda la entrada: m:{a<>} a:[({n<R>A})({n<R>A}*{l<R>A}{a<>P}{r<R>A})]*{n<R>A}* l:$[^\(\)]*\([^\(\)]*$ r:$[^\(\)]*\)[^\(\)]*$ n:$[^\(\)]+$
TheNumberOne

21

Slip, Python 3.4 ( wiki de Github , intérprete en línea )

Al igual que el envío de feersum, esto también se basa en atravesar la cuadrícula, pero al igual que el envío de CarpetPython, esto se basa en expresiones regulares. De alguna manera parece que logré tomar el término medio.

Hay muchas características no implementadas que quiero agregar, así que, cuando sea relevante, he notado lo que pretendo hacer cuando tenga tiempo.


Actualizaciones

(Ver la página de Github para noticias detalladas)

  • 5 de abril de 2015 : ¡Slip ahora tiene un intérprete en línea ! Todavía está en sus primeras etapas, por lo que puede haber algunos errores.

  • 5 de abril de 2015 : ¡Actualización de eficiencia! Ahora, los portales inferiores hacen una gran entrada mucho más rápido (2s). También ha habido una serie de cambios de sintaxis, así que asegúrese de consultar el wiki. Numeración grupal también fija.


El deslizamiento tiene un puntero de coincidencia que comienza en un cuadrado particular y que inicialmente está orientado hacia la derecha. Cuando se hace coincidir un carácter, el puntero se mueve hacia adelante en la dirección actual (aunque la implementación no hace las cosas exactamente en ese orden).

La dirección del puntero del partido se puede establecer en una dirección particular con ^<digit>, en donde ^0, ^1, ^2, ..., ^7establecer el puntero a N, NE, E, ..., NW, respectivamente (las agujas del reloj en marcha).

Los siguientes accesos directos también están disponibles:

  • ^* comprueba las 8 direcciones ortogonales o diagonales,
  • ^+ comprueba las 4 direcciones ortogonales.

(Plan futuro: Permitir la configuración de direcciones arbitrarias, por ejemplo, (1, 2)para el movimiento del caballero)

Por ejemplo ( Problema 4 - Encontrar una palabra en una búsqueda de palabras ),

^*GOLF

intenta las 8 direcciones ortogonales o diagonales, buscando la palabra "GOLF". Por defecto, Slip intenta todos los cuadrados iniciales posibles y devuelve un resultado de cada uno, filtrando duplicados, por lo que una cuadrícula como

GOLF
O
L
FLOG

devuelve solo la fila superior y la fila inferior como coincidencias (ya que la fila superior y la columna izquierda "GOLF" comienzan desde el mismo cuadrado). Para obtener todas las coincidencias, ose puede utilizar el modo de coincidencia superpuesta.

Del mismo modo ( Problema 15 - Une una palabra en un tablero de Boggle ),

p^*a^*n^*a^*m^*a

coincide panamaintentando una dirección diferente cada vez. Use la ibandera para la insensibilidad a mayúsculas y minúsculas. Slip reutiliza los caracteres de forma predeterminada, pero esto se puede alternar con el rindicador de no repetición.

(Plan futuro: un modificador de modo de búsqueda que verifica automáticamente conjuntos de direcciones después de cada movimiento para que no ^*sea ​​necesario repetir )

La dirección del puntero del partido también se puede girar 90 grados hacia la izquierda o hacia la derecha con <o >respectivamente. Por ejemplo,

 `#`#< `#<  <`#<`#

busca el patrón

  #
## 
 ##

mirando en el siguiente orden:

765
894
123

Esto nos permite resolver el problema 6: encontrar planeadores con

^+(`#`# >`# > `#>`#> |`#`# <`# < `#<`#< | `#`#> `#>  >`#>`#| `#`#< `#<  <`#<`#)

donde las partes 1 y 2 codifican las formas del planeador, y las partes 3 y 4 codifican sus contrapartes reflejadas.

Tenga en cuenta que Slip usa la tecla `de retroceso para escapar. Esto se debe a que Slip tiene otra forma de movimiento que le da nombre al idioma: el comando slip. /desliza el puntero de coincidencia ortogonalmente hacia la izquierda, mientras \desliza el puntero de coincidencia ortogonalmente hacia la derecha.

Por ejemplo,

abc   ghi
   def

puede ser igualada por abc\def/ghi.

Si bien no es particularmente útil por sí solo, el deslizamiento se vuelve más importante cuando se combina con el (?| <regex> )grupo estacionario, que actúa como una búsqueda anticipada coincidente. La expresión regular en el interior coincide, luego, al final, la posición y la dirección del puntero de coincidencia se restablecen al estado anterior al grupo estacionario.

Por ejemplo,

abc
def
ghi

se pueden combinar con (?|abc)\(?|def)\(?|ghi).

Del mismo modo, el problema 12: evitar la letra Q se puede resolver con

%(\(?|[^qQ]{4})){4}

donde %hay un comando antideslizante para detener la \activación del primero .

El deslizamiento también tiene una aserción de longitud (?_(<group num>) <regex> ), que solo coincide con la expresión regular dentro si su longitud de coincidencia es la misma que la del número de grupo dado.

Por ejemplo, Problema 13: la minería de diamantes se puede resolver fácilmente con

^1X(`/*)X>(?_(1)`\*)X>(?_(1)`/*)X>(?_(1)`\*)

que intenta hacer coincidir el lado superior izquierdo del diamante primero, luego afirma que los otros tres lados tienen la misma longitud.

(Ejecutar con vbandera para salida detallada)

Match found in rectangle: (8, 0), (12, 4)
  X
 / \
X   X
 \ /
  X

Match found in rectangle: (0, 0), (6, 6)
   X
  / \
 /   \
X     X
 \   /
  \ /
   X

Match found in rectangle: (2, 2), (4, 4)
 X
X X
 X

Match found in rectangle: (10, 2), (14, 6)
  X
 / \
X   X
 \ /
  X

Match found in rectangle: (5, 3), (9, 7)
  X
 / \
X   X
 \ /
  X

Match found in rectangle: (0, 6), (2, 8)
 X
X X
 X

Una alternativa más golfista es

^1X(`/*)(X>(?_(1)`\*)X>(?_(1)`/*)){2}

que coincide con el primer lado dos veces.

Muchos de los otros problemas se pueden resolver mediante deslizamiento, grupos estacionarios y afirmaciones de longitud:

Problema 14 - Cruces coincidentes:

(?|(`.+)(`#+)(`.+))(\(?|(?_(2)`.+)(?_(3)`#+)(?_(4)`.+)))*(\(?|(?_(2)`#+)(?_(3)`#+)(?_(4)`#+)))+(\(?|(?_(2)`.+)(?_(3)`#+)(?_(4)`.+)))+

Una vez que capture las anchuras de las .s y #s en la primera fila, es simplemente deslizarse hasta el fondo.

Salida:

Match found in rectangle: (0, 1), (5, 5)
.###..
######
######
.###..
.###..

Match found in rectangle: (4, 6), (6, 8)
.#.
###
.#.

Este probablemente se pueda jugar con un poco de recurrencia, una vez que solucione algunos errores.

Problema 3 - Detecta un rectángulo de dígitos:

(?|`d`d+)(\(?|(?_(1)`d+)))+

Haga coincidir una fila superior de dos o más dígitos, luego asegúrese de que cada línea de abajo tenga la misma longitud. `des una clase de caracteres predefinida equivalente a [0-9].

Tenga en cuenta que esto encuentra todas las coincidencias .

Problema 7: coincide con los portales inferiores:

(?|.X{2,22}.)\((?|(?_(1)X`.+X))\){3,22}(?_(1).X+.)

que sale, para el ejemplo superior en el hilo original :

Match found in rectangle: (2, 1), (5, 5)
XXXX
X..X
X..X
X..X
XXXX

Match found in rectangle: (9, 1), (14, 5)
.XXXX.
X....X
X....X
X....X
.XXXX.

Match found in rectangle: (13, 4), (17, 8)
.XXXX
X...X
X...X
X...X
.XXX.

Finalmente, algunas otras características de Slip incluyen:

  • $0, $1, $2, ..., $7ancla el borde norte, la esquina noreste, el borde este, etc. $+ancla cualquier borde y $*ancla cualquier esquina.
  • $seguido de un carácter en minúscula establece un ancla en la posición actual, que luego puede coincidir $con el carácter en mayúscula correspondiente, por ejemplo, $ay $A.
  • # alterna la bandera de no mover, lo que impide que el puntero de la partida avance después de la próxima partida.
  • ,coincide con un carácter similar ., pero no lo agrega a la salida, lo que permite coincidencias no contiguas mientras puede ser reconocido por (?_()).

... y más. Realmente hay demasiados para enumerar en esta página.

Otros problemas

Problema 1 - Encontrar tableros de ajedrez:

(?|`#?(`_`#)+`_?)(?_(1)(?|...+))(\(?_(1)(?|`#?(`_`#)+`_?$a)))+<(?|`#?(`_`#)+`_?)(?_(9)(?|...+))(\(?_(9)(?|`#?(`_`#)+`_?)))+$A

Los dos problemas del tablero de ajedrez ciertamente no son el fuerte de Slip. Hacemos coincidir la fila superior y luego nos aseguramos de que tenga al menos la longitud 3 y alterna entre #y _, luego deslice y combine las filas posteriores. Al final, el $aancla debe estar en la parte inferior derecha del tablero de ajedrez.

Luego giramos a la izquierda y repetimos para las columnas, asegurándonos de que coincidamos $Aal final.

Problema 2 - Verificación de tableros de ajedrez:

$7%(\(?|`_?(`#`_)*`#?$2))+$5<%(\(?|`_?(`#`_)*`#?$0))+$3

Al igual que el problema anterior, verificamos que cada fila sea correcta, luego giramos a la izquierda y hacemos lo mismo para las columnas. Los anclajes se utilizan para asegurarse de que solo coincida toda la tabla.

Problema 9 - Alineación horizontal y vertical:

>?`#,*`#

Aplicamos el opcional? cuantificador al >comando rotar para que coincidamos hacia la derecha o hacia abajo. Encontramos los 5 en el ejemplo con omodo superpuesto, pero solo 4 sin él desde entonces #.,##y #.,#comenzamos desde la misma posición.

Problema 10 - Puntos colineales

^+`#(?|(,*)<(,*))(((?_(2),*)<(?_(3),*),>)+#`#){2}

Haga coincidir #unos caracteres horizontales y algunos caracteres verticales, luego repita hasta el segundo #y repita hasta el tercero #.

Problema 5 - Detección de entradas cuadradas:

$7.(.*)>(?_(1).*)$3>((?|.*)\)*

Ancle la esquina superior izquierda y verifique que el borde superior tenga la misma longitud que el borde derecho, antes de anclar la esquina inferior derecha. Si la entrada pasa esto, entonces vamos hacia atrás para que coincida con toda la entrada.

Problema 8 - Colocación del cofre de Minecraft:

`.^+(($^|(?|`.))>){3}($^|`.|C<(($^|(?|`.))>){3})

Corre con la pbandera para obtener las posiciones de cada partido. $^es un ancla que coincide si el próximo movimiento pondría el puntero fuera de límites.

Primero hacemos coincidir a ., luego verificamos que estamos rodeados por tres .s / límites, luego nos aseguramos de que el cuarto cuadrado circundante también sea un ./ límite o sea un solo cofre (verificando sus cuadrados circundantes).

Problema 11: verificar la sintaxis del preludio :

$7>%(/(?|[^()]+$4)(?1)?|/(?|[^()]*`([^()]*$4)(?1)?/(?|[^()]*`)[^()]*$4)(?1)?)$1

Hice algunos intentos, pero creo que este es correcto. Aquí usamos recursividad, que tiene la misma sintaxis que PCRE.

Este enfoque requiere que la entrada sea rectangular, pero una vez que haga una coincidencia no rectangular, esa suposición no será necesaria.

Aquí está el mismo enfoque, golf con más recursividad:

$7>%((/(?|([^()]*)$4)|/(?|(?4)`((?3))(?1)?/(?|(?4)`)(?3)))*)$1

Problema 16 - Envuelva alrededor de los bordes:

%(\(?|`#{3})){3}

(Nota: el ajuste aún no se ha enviado al intérprete en línea)

Esto requiere la bandera de ajuste w. Técnicamente, la inicial %para no deslizarse no es necesaria, pero luego el partido se contará como comenzar desde un cuadrado más alto.

Problema EX 1 - Solucionador de laberintos:

S(^+`.)*^+E

Problema del comentario de BMac en el chat . Use la rbandera para el modo de no repetición para que el puntero de coincidencia no se atasque yendo y viniendo.

Problema EX 2 - Reconocimiento facial :

(?|O(,*),(?_(2),*)O)(?_(2)(\(?|,))*)\(?|,(?_(2),*)O)(?_(2)(\(?|,))*)\`\(?_(2)`_*)`_(?_(2)`_*)`/

Tenga en cuenta que solo estoy haciendo coincidir caras, no haciendo ningún borrado. Tenga en cuenta que la pregunta contiene símbolos de euro, que deberán ser reemplazados por algunos ASCII imprimibles para funcionar.


Ese patrón de hash es un planeador de Conway
Heimdall

17

PMA / Caracoles (C ++)

Imagino el lenguaje como caracoles moviéndose alrededor de una cuadrícula y ejecutando comandos. Los caracoles dejan un rastro de limo en cada cuadro al que se mueven, lo que por defecto hace que el cuadrado sea inigualable. Una coincidencia tiene éxito si se alcanza el final de la lista de comandos.

Hay suficientes operadores ahora que vamos a necesitar una lista de precedencia para realizar un seguimiento de ellos. Las operaciones se resuelven en el siguiente orden:

  1. Dentro de grupos ( ) [ ]
  2. División a lo largo del carácter de alternancia |
  3. Evalúa todo a la izquierda de a `como grupo
  4. Cuantificadores [m],[n]yn
  5. Aserciones = !
  6. Concatenación

El intérprete está escrito en C ++. El código fuente abismal se puede encontrar aquí .

Problema 4: Búsqueda de palabras

El programa

z\G\O\L\F

es suficiente para obtener un valor verdadero o falso para determinar si se encuentra la palabra. z(uno de los 15 comandos direccionales absolutos o relativos) coincide en cualquier dirección octilineal. Múltiples comandos de dirección consecutivos son OR juntos. Por ejemplo ruldy, sería casi equivalente a z, ya que esos son los comandos para derecha, arriba, izquierda, abajo y cualquier dirección diagonal. Sin embargo, las instrucciones se probarían en un orden diferente. El primer personaje que coincide siempre es el que comienza el caracol, independientemente de la dirección. \<carácter> coincide con un único carácter literal.

La estrategia predeterminada es probar el patrón en cada cuadrado en el cuadro delimitador de la entrada justificada a la izquierda y generar el número de coincidencias. Si se requiere un valor booleano 1o 0, se puede utilizar el siguiente programa:

?
z\G\O\L\F

Si hay al menos una nueva línea en el archivo fuente, la primera línea se trata como opciones para la conversión inicial de la entrada en una forma rectangular. La ?opción imprime 0 o 1 dependiendo de si hay una coincidencia en alguna parte.

Problema 15: Boggle

Después de implementar la alternancia, ahora es posible resolver Boggle. Sería mejor utilizar coincidencias que no distingan entre mayúsculas y minúsculas para este, pero implementar esa no es mi máxima prioridad.

\p|\P)z(\a|\A)z{\n|\N)z{\a|\A}z(\m|\M)z(\a|\A

|funciona exactamente igual que una expresión regular unidimensional. Hay dos pares de delimitadores para agrupar, a saber, ()y {}. Un corchete de cierre cerrará cualquier grupo abierto del tipo opuesto que se interponga entre él y el más cercano del mismo tipo. Por ejemplo, a continuación {({{), solo el grupo de la izquierda permanece abierto. Como puede ver, los símbolos de agrupación no coincidentes en los bordes están implícitamente cerrados. Hay otro comando de agrupación en el `que no entraré ahora.

Problema 8: Cofres de Minecraft

El programa imprime el número de ubicaciones de cofres válidas. Este fue el primero que tuve que jugar al golf, y reduje mi número de bytes (17) varias veces.

\.!(o\C)2o(!\Cw)3
  • \. coincide con un punto literalmente, en el punto de partida del partido.
  • !(o\C)2es equivalente a !((o\C)2)que la cuantificación tiene mayor precedencia que la afirmación. <atom> <number>significa repetir tiempos <atom>exactos <number>. ogira el caracol en cualquier dirección ortogonal. !Es una afirmación negativa. Por lo tanto, esta parte verifica la ausencia de un cofre doble adyacente.
  • o gira en alguna dirección ortogonal.
    • (!\Cw)3afirma que no hay Cfrente al caracol y luego gira 3 veces en sentido antihorario.

Problema 2: Verificación de tableros de ajedrez

&
\#!(o^_)|\_!(o^#

La &opción establece la salida del programa como 1si la coincidencia tuviera éxito en todas las posiciones, y de lo 0contrario. ^ccoincide con un carácter que no es c, equivalente a [^c]en regex. En conjunto, el programa significa: Imprimir 1 si en cada posición en el rectángulo delimitador de la entrada, hay una #que no es ortogonalmente adyacente a un carácter que no lo es _o una _que no es ortogonalmente adyacente a un carácter que es no #; de lo contrario 0.


La idea del rastro de baba es buena para lidiar con boggle, estaba teniendo algunos problemas con eso
BMac

Esa es una buena solución para el problema de Boggle. No puedo resolver eso con mi enfoque.
Logic Knight

14

La clase Re2d, Python 2

Actualización: se agregó el problema "9. Alineación".

Mi enfoque es usar el módulo Python re para hacer la búsqueda y la coincidencia. La clase Re2d prepara el texto para el procesamiento, ejecuta las funciones re y formatea los resultados para la salida.

Tenga en cuenta que este no es un lenguaje completamente nuevo: es el lenguaje de expresión regular estándar proyectado en 2 dimensiones con banderas agregadas para modos 2D adicionales.

La clase tiene el siguiente uso:

re2dobject = Re2d(<horizontal pattern>, [<vertical pattern>], [<flags>])

Ambos patrones son patrones RE estándar de texto lineal. Si no se proporciona un patrón vertical, la clase también usará el patrón horizontal para hacer coincidir verticalmente. Las banderas son las banderas RE estándar con algunas extensiones 2D.

Pruebas

1. Finding chessboards
Chessboard pattern at (2, 1, 4, 3)

print '\n1. Finding chessboards'
reob1 = Re2d('#(_#)+_?|_(#_)+#?')
found = reob1.search('~______~\n~##_#_#~\n~#_#_##~\n~##_#_#~\n~______~')
print 'Chessboard pattern at', found
assert not reob1.search('#_##\n_#_#\n__#_\n#_#_\n#_#_')

El método de búsqueda encontró un patrón de tablero de ajedrez y devuelve una posición de 4 tuplas. La tupla tiene la x,yposición del primer personaje del partido y la width, heightdel área coincidente. Solo se proporciona un patrón, por lo que se utilizará para la coincidencia horizontal y vertical.

2. Verifying chessboards
Is chess? True

print '\n2. Verifying chessboards'
reob2 = Re2d('^#(_#)*_?|_(#_)*#?$')
print 'Is chess?', reob2.match('_#_#_#_#\n#_#_#_#_\n_#_#_#_#')
assert not reob2.match('_#_#_#__\n__#_#_#_\n_#_#_#__')

El tablero de ajedrez se verificó con el método de coincidencia que devuelve un valor booleano. Tenga en cuenta que el ^e $inician y se requieren caracteres finales para que coincida con el conjunto del texto.

3. Rectangle of digits
Found: [(0, 1, 5, 3), (1, 1, 4, 3), (2, 1, 3, 3), (3, 1, 2, 3), (0, 2, 5, 2), (1, 2, 4, 2), (2, 2, 3, 2), (3, 2, 2, 2), (6, 3, 2, 2)]
Not found: None

print '\n3. Rectangle of digits'
reob3 = Re2d(r'\d\d+', flags=MULTIFIND)
print 'Found:', reob3.search('hbrewvgr\n18774gwe\n84502vgv\n19844f22\ncrfegc77')
print 'Not found:', reob3.search('uv88wn000\nvgr88vg0w\nv888wrvg7\nvvg88wv77')

Ahora usamos la MULTIFINDbandera para devolver todas las coincidencias posibles para el bloque de más de 2 dígitos. El método encuentra 9 posibles coincidencias. Tenga en cuenta que pueden superponerse.

4. Word search (orthogonal only)
Words: [(0, 0, 4, 1), (0, 3, 4, 1), (3, 3, -4, -1), (3, 2, -4, -1), (3, 0, -4, -1)] [(0, 0, 1, 4), (3, 0, 1, 4), (3, 3, -1, -4), (0, 3, -1, -4)]
Words: ['SNUG', 'WOLF', 'FLOW', 'LORE', 'GUNS'] ['S\nT\nE\nW', 'G\nO\nL\nF', 'F\nL\nO\nG', 'W\nE\nT\nS']
No words: [] []

print '\n4. Word search (orthogonal only)'
words = 'GOLF|GUNS|WOLF|FLOW|LORE|WETS|STEW|FLOG|SNUG'
flags = HORFLIP | VERFLIP | MULTIFIND
reob4a, reob4b = Re2d(words, '.', flags), Re2d('.', words, flags)
matching = 'SNUG\nTEQO\nEROL\nWOLF'
nomatch = 'ABCD\nEFGH\nIJKL\nMNOP'
print 'Words:', reob4a.search(matching), reob4b.search(matching)
print 'Words:', reob4a.findall(matching), reob4b.findall(matching)
print 'No words:', reob4a.findall(nomatch), reob4b.findall(nomatch)

Esta prueba muestra el uso de volteo vertical y horizontal. Esto permite palabras coincidentes que se invierten. Las palabras diagonales no son compatibles. La MULTIFINDbandera permite múltiples coincidencias superpuestas en las 4 direcciones. El método findall utiliza la búsqueda para encontrar los cuadros coincidentes y luego extrae los bloques de texto coincidentes. Observe cómo la búsqueda usa ancho y / o alto negativo para coincidencias en la dirección inversa. Las palabras en la dirección vertical tienen nuevos caracteres de línea, esto es consistente con el concepto de bloques de caracteres 2D.

7. Calvins portals
Portals found: [(3, 1, 5, 6)]
Portal not found None

print '\n7. Calvins portals'
reob7 = Re2d(r'X\.{2,22}X|.X{2,22}.', r'X\.{3,22}X|.X{3,22}.', MULTIFIND)
yes = '....X......\n.XXXXXX.XX.\n...X...X...\n.X.X...XXX.\n...X...X.X.\n.XXX...X.X.\nX..XXXXX.X.'
no = 'XX..XXXX\nXX..X..X\nXX..X..X\n..X.X..X\n.X..X.XX'
print 'Portals found:', reob7.search(yes)
print 'Portal not found', reob7.search(no)

Esta búsqueda necesitaba patrones separados para cada dimensión, ya que el tamaño mínimo es diferente para cada una.

9. Alignment
Found: ['#.,##', '##'] ['#\n.\n,\n.\n#', '#\n,\n.\n#']
Found: [(3, 4, 5, 1), (6, 4, 2, 1)] [(7, 0, 1, 5), (3, 1, 1, 4)]
Not found: None None

print '\n9. Alignment'
reob9a = Re2d(r'#.*#', r'.', MULTIFIND)
reob9b = Re2d(r'.', r'#.*#', MULTIFIND)
matching = '.,.,.,.#.,\n,.,#,.,.,.\n.,.,.,.,.,\n,.,.,.,.,.\n.,.#.,##.,\n,.,.,.,.,.'
nomatch = '.,.#.,.,\n,.,.,.#.\n.,#,.,.,\n,.,.,.,#\n.#.,.,.,\n,.,.#.,.\n#,.,.,.,\n,.,.,#,.'
print 'Found:', reob9a.findall(matching), reob9b.findall(matching)
print 'Found:', reob9a.search(matching), reob9b.search(matching)
print 'Not found:', reob9a.search(nomatch), reob9b.search(nomatch)

Este conjunto de 2 búsquedas encuentra 2 coincidencias verticales y 2 horizontales, pero no puede encontrar la #.,#cadena incrustada .

10. Collinear Points (orthogonal only)
Found: [(0, 1, 7, 1)] [(3, 1, 1, 4)]
Not found: None None

print '\n10. Collinear Points (orthogonal only)'
matching = '........\n#..#..#.\n...#....\n#.......\n...#....'
nomatch = '.#..#\n#..#.\n#....\n..#.#'
reob10h = Re2d(r'#.*#.*#', '.')
reob10v = Re2d('.', r'#.*#.*#')
flags = MULTIFIND
print 'Found:', reob10h.search(matching, flags), reob10v.search(matching, flags)
print 'Not found:', reob10h.search(nomatch, flags), reob10v.search(nomatch, flags)

Aquí usamos 2 búsquedas para encontrar coincidencias en ambas direcciones. Es capaz de encontrar múltiples coincidencias ortogonales, pero este enfoque no admite coincidencias diagonales.

12. Avoid qQ
Found: (2, 2, 4, 4)
Not found: None

print '\n12. Avoid qQ'
reob12 = Re2d('[^qQ]{4,4}')
print 'Found:', reob12.search('bhtklkwt\nqlwQklqw\nvtvlwktv\nkQtwkvkl\nvtwlkvQk\nvnvevwvx')
print 'Not found:', reob12.search('zxvcmn\nxcvncn\nmnQxcv\nxcvmnx\nazvmne')

Esta búsqueda encuentra la primera coincidencia.

13. Diamond Mining
.X.
X.X
.X.

.X.
X.X
.X.

..X..
./.\.
X...X
.\./.
\.X..

..X..
./.\.
X...X
.\./.
..X..

.XX.\
//.\.
X...X
.\./.
..X..

...X...
../.\..
./.X.\.
X.X.X.X
.\.X.//
..\./X.
.X.X..\

Diamonds: [(2, 2, 3, 3), (0, 6, 3, 3)] [(8, 0, 5, 5), (10, 2, 5, 5), (5, 3, 5, 5)] [(0, 0, 7, 7)]
Not found: None None None

print '\n13. Diamond Mining'
reob13a = Re2d(r'.X.|X.X', flags=MULTIFIND)
reob13b = Re2d(r'..X..|./.\\.|X...X|.\\./.', flags=MULTIFIND)
reob13c = Re2d(r'...X...|../.\\..|./...\\.|X.....X|.\\.../.|..\\./..', flags=MULTIFIND)
match = '''
...X......X....
../.\..../.\...
./.X.\..X...X..
X.X.X.XX.\./.\.
.\.X.//.\.X...X
..\./X...X.\./.
.X.X..\./...X..
X.X....X.......
.X.............
'''.strip().replace(' ', '')
nomatch = '''
.X......./....
.\....X.......
...X.\.\...X..
..X.\...\.X.\.
...X.X...X.\.X
../X\...\...X.
.X...\.\..X...
..\./.X....X..
...X..../.....
'''.strip().replace(' ', '')
for diamond in reob13a.findall(match)+reob13b.findall(match)+reob13c.findall(match):
    print diamond+'\n'
print 'Diamonds:', reob13a.search(match), reob13b.search(match), reob13c.search(match)
print 'Not found:', reob13a.search(nomatch), reob13b.search(nomatch), reob13c.search(nomatch)

El problema del diamante es más difícil. Se necesitan tres objetos de búsqueda para los tres tamaños. Puede encontrar los seis diamantes en el conjunto de prueba, pero no escala a diamantes de tamaño variable. Esta es solo una solución parcial al problema del diamante.

Código de Python 2

import sys
import re

DEBUG = re.DEBUG
IGNORECASE = re.IGNORECASE
LOCALE = re.LOCALE
UNICODE = re.UNICODE
VERBOSE = re.VERBOSE
MULTIFIND = 1<<11
ROTATED = 1<<12     # not implemented
HORFLIP = 1<<13
VERFLIP = 1<<14
WRAPAROUND = 1<<15  # not implemented

class Re2d(object):
    def __init__(self, horpattern, verpattern=None, flags=0):
        self.horpattern = horpattern
        self.verpattern = verpattern if verpattern != None else horpattern
        self.flags = flags

    def checkblock(self, block, flags):
        'Return a position if block matches H and V patterns'
        length = []
        for y in range(len(block)):
            match = re.match(self.horpattern, block[y], flags)
            if match:
                length.append(len(match.group(0)))
            else:
                break
        if not length:
            return None
        width = min(length)
        height = len(length)
        length = []
        for x in range(width):
            column = ''.join(row[x] for row in block[:height])
            match = re.match(self.verpattern, column, flags)
            if match:
                matchlen = len(match.group(0))
                length.append(matchlen)
            else:
                break
        if not length:
            return None
        height = min(length)
        width = len(length)
        # if smaller, verify with RECURSIVE checkblock call:
        if height != len(block) or width != len(block[0]):
            newblock = [row[:width] for row in block[:height]]
            newsize = self.checkblock(newblock, flags)
            return newsize
        return width, height

    def mkviews(self, text, flags):
        'Return views of text block from flip/rotate flags, inc inverse f()'
        # TODO add ROTATED to generate more views
        width = len(text[0])
        height = len(text)
        views = [(text, lambda x,y,w,h: (x,y,w,h))]
        if flags & HORFLIP and flags & VERFLIP:
            flip2text = [row[::-1] for row in text[::-1]]
            flip2func = lambda x,y,w,h: (width-1-x, height-1-y, -w, -h)
            views.append( (flip2text, flip2func) )
        elif flags & HORFLIP:
            hortext = [row[::-1] for row in text]
            horfunc = lambda x,y,w,h: (width-1-x, y, -w, h)
            views.append( (hortext, horfunc) )
        elif flags & VERFLIP:
            vertext = text[::-1]
            verfunc = lambda x,y,w,h: (x, height-1-y, w, -h)
            views.append( (vertext, verfunc) )
        return views

    def searchview(self, textview, flags=0):
        'Return matching textview positions or None'
        result = []
        for y in range(len(textview)):
            testtext = textview[y:]
            for x in range(len(testtext[0])):
                size = self.checkblock([row[x:] for row in testtext], flags)
                if size:
                    found = (x, y, size[0], size[1])
                    if flags & MULTIFIND:
                        result.append(found)
                    else:
                        return found
        return result if result else None

    def search(self, text, flags=0):
        'Return matching text positions or None'
        flags = self.flags | flags
        text = text.split('\n') if type(text) == str else text
        result = []
        for textview, invview in self.mkviews(text, flags):
            found = self.searchview(textview, flags)
            if found:
                if flags & MULTIFIND:
                    result.extend(invview(*f) for f in found)
                else:
                    return invview(*found)
        return result if result else None

    def findall(self, text, flags=0):
        'Return matching text blocks or None'
        flags = self.flags | flags
        strmode = (type(text) == str)
        text = text.split('\n') if type(text) == str else text
        result = []
        positions = self.search(text, flags)
        if not positions:
            return [] if flags & MULTIFIND else None
        if not flags & MULTIFIND:
            positions = [positions]
        for x0,y0,w,h in positions:
            if y0+h >= 0:
                lines = text[y0 : y0+h : cmp(h,0)]
            else:
                lines = text[y0 : : cmp(h,0)]
            if x0+w >= 0:
                block = [row[x0 : x0+w : cmp(w,0)] for row in lines]
            else:
                block = [row[x0 : : cmp(w,0)] for row in lines]
            result.append(block)
        if strmode:
            result = ['\n'.join(rows) for rows in result]
        if flags & MULTIFIND:
            return result
        else:
            return result[0]

    def match(self, text, flags=0):
        'Return True if whole text matches the patterns'
        flags = self.flags | flags
        text = text.split('\n') if type(text) == str else text
        for textview, invview in self.mkviews(text, flags):
            size = self.checkblock(textview, flags)
            if size:
                return True
        return False

Si el problema del diamante no estaba claro, los diamantes pueden ser de cualquier tamaño, no solo 0, 1 o 2. Editar: he editado la especificación para aclarar esto.
PhiNotPi

Entendido. Tomaré una nota en la respuesta que el Re2d solo tiene una solución parcial a este problema. No puede escalar a tamaños variables. ¿Eso esta bien?
Logic Knight

Si está bien.
PhiNotPi

14

Mugre , Haskell

Introducción

Grime se basa en gramáticas booleanas . La idea básica es construir patrones rectangulares a partir de componentes más pequeños y verificar si se encuentran en la matriz de entrada. Hasta ahora, Grime solo admite coincidencias rectangulares y resuelve al menos 11 problemas de manera más o menos elegante.

EDITAR: se corrigieron las cruces (gracias a DLosc por detectar el error) y se agregó minería de diamantes.

EDIT2: Se agregaron clases de personajes, inspiradas en las de Slip. También cambió la sintaxis de los indicadores de opción, mejoró el analizador de expresiones y agregó el problema de no Q.

EDITAR3: Se implementaron restricciones de tamaño y se agregó el problema de los portales de Nether.

Uso

Un programa Grime se llama gramática , y la extensión de archivo correcta para una gramática es .gr, aunque esto no se aplica. La gramática se evalúa como

runhaskell grime.hs [options] grammarfile matrixfile

donde matrixfilees un archivo que contiene la matriz de caracteres. Por ejemplo, la gramática de los dígitos se evaluaría como

runhaskell grime.hs digits.gr digit-matrix

Para mayor velocidad, recomiendo compilar el archivo con optimizaciones:

ghc -O2 grime.hs
./grime digits.gr digit-matrix

De forma predeterminada, el intérprete imprime la primera coincidencia que encuentra, pero esto se puede controlar mediante los indicadores de opción:

  • -e: coincide solo con toda la matriz, imprime 1por coincidencia y 0sin coincidencia.
  • -n: imprime el número de coincidencias, o la matriz completa si -etambién se da.
  • -a: imprime todos los partidos.
  • -p: imprime también las posiciones de los partidos, en el formato (x,y,w,h).
  • -s: no imprima las coincidencias ellos mismos.
  • -d: imprime información de depuración.

Las opciones también se pueden especificar dentro de la gramática, insertándolas antes de cualquier línea y agregando una coma ,(ver ejemplos a continuación).

Sintaxis y Semántica

Una gramática de Grime consta de una o más definiciones , cada una en una línea separada. Cada uno de ellos define el valor de un no terminal , y uno de ellos debe definir el nivel anónimo no terminal . La sintaxis de una definición es N=Eo E, donde Nes una letra mayúscula y Ees una expresión .

Las expresiones se construyen de la siguiente manera.

  • Cualquier carácter que se haya escapado \coincide con cualquier 1x1rectángulo que contenga ese carácter.
  • . coincide con cualquier personaje individual.
  • $coincide con un 1x1rectángulo fuera de la matriz de caracteres.
  • _ coincide con cualquier rectángulo de ancho o alto cero.
  • Los grupos de caracteres predefinidos son digit, uppercase, lowercase, alphabetic, alpha numeric y symbol.
  • Las nuevas clases de caracteres se pueden definir por la sintaxis [a-prt-w,d-gu]. Se incluyen las letras de la izquierda y se excluyen las de la derecha, por lo que coincide exactamente con las letras abchijklmnoprtvw. Si el lado izquierdo está vacío, se considera que contiene todos los caracteres. La coma se puede omitir si el lado derecho está vacío. Los personajes [],-\deben escapar con \.
  • Una letra mayúscula sin escape no es un terminal y coincide con la expresión que se le asigna.
  • Si Py Qson expresiones, entonces PQes solo su concatenación horizontal, y P/Qes su concatenación vertical, con Parriba.
  • P+es uno o más Ps alineados horizontalmente, y P/+es el mismo alineado verticalmente.
  • Las operaciones booleanas se denotan P|Q, P&Qy P!.
  • P?es la abreviatura de P|_, P*para P+|_y P/*para P/+|_.
  • P#coincide con cualquier rectángulo que contenga una coincidencia de P.
  • P{a-b,c-d}, donde abcdhay enteros no negativos, es una restricción de tamaño en P. Si Pes una clase de caracteres, entonces la expresión coincide con cualquier mxnrectángulo que contenga solo esos caracteres, siempre que mesté entre ae binclusive, y nesté entre ce dinclusive. En otros casos, la expresión coincide con cualquier rectángulo que tenga el tamaño correcto y que Ptambién coincida. Si ao cse omiten, que se considera que son 0, y si bo dse omiten, que son infinitos. Si se omite el guión entre ay b, entonces usamos el mismo número para ambos extremos del intervalo. Si todoc-dse omite parte, ambos ejes están restringidos. Para aclarar, {-b}es equivalente a {0-b,0-b}, y {a-,c}es equivalente a {a-infinity,c-c}.

Notas

Grime permite definiciones paradójicas como A=A!con el comportamiento indefinido. Sin embargo, no causarán bloqueos o bucles infinitos.

Grime admite entradas no rectangulares; las filas se alinean simplemente a la izquierda y los espacios se pueden combinar usando $.

En el futuro, me gustaría implementar lo siguiente:

  • Emparejamiento más rápido. Actualmente, el intérprete no tiene en cuenta el hecho de que, por ejemplo, .solo puede coincidir con 1x1rectángulos. Hasta que encuentre una coincidencia, intenta todos los rectángulos de todos los tamaños en orden, fallando instantáneamente para cada uno de ellos.
  • Operaciones de rotación y reflexión, para la búsqueda de palabras y desafíos de planeadores.
  • Partidos no rectangulares que utilizan contextos , lo que sería útil en el desafío del tablero Boggle. Por ejemplo, Pv(Q>R)significa Pcon contexto inferior ( Qcon contexto correcto R). Coincidiría con los patrones en forma de L

    PPP
    PPP
    QQQRRRR
    QQQRRRR
    QQQRRRR
    

Las tareas

Dado aproximadamente en orden de complejidad.

Rectángulo de dígitos

d{2-}

Esto es simple: un rectángulo de dígitos de tamaño al menos 2x2.

No q o Q

[,qQ]{4}

Esto es casi tan simple como el primero; ahora tenemos un tamaño más restringido y una clase de caracteres personalizada.

Alineación horizontal y vertical

\#.*\#|\#/./*/\#

Ahora tenemos algunos personajes escapados. Básicamente, esto coincide con uno #, luego cualquier número de caracteres, luego #, ya sea horizontal o verticalmente.

Detectar entradas cuadradas

S=.|S./+/.+
e,S

Esta gramática es muy simple, básicamente define que un cuadrado es un 1x1rectángulo o un cuadrado más pequeño con una columna pegada en su borde derecho y una fila pegada en la parte inferior. Tenga en cuenta también la eopción antes del nivel superior no terminal, que alterna la verificación de entrada completa.

Encontrar una palabra en una búsqueda de palabras

G=\G
O=\O
L=\L
F=\F
GOLF|FLOG|G/O/L/F|F/L/O/G|G.../.O../..L./...F|...G/..O./.L../F...|F.../.L../..O./...G|...F/..L./.O../G...

Este es horrible, ya que Grime no tiene operaciones de rotación o reflexión. También es extremadamente lento, ya que Grime no sabe que los partidos solo pueden ser de tamaño 4x1, 1x4o 4x4.

El problema del planeador podría resolverse de manera similar, pero soy demasiado vago para escribirlo.

Portales abisales

.\X+./\X/+\.{2-22,3-22}\X/+/.\X+.

Con el operador de restricción de tamaño, este no es tan difícil. La parte central \.{2-22,3-22}coincide con cualquier rectángulo de .los tamaños correctos, y luego solo agregamos columnas de Xs en ambos lados, y agregamos filas de Xs con extremos ignorados en la parte superior e inferior de eso.

Cruces a juego

E=\.+/+
F=\#+/+
EFE/F/EFE&(E/F/E)F(E/F/E)

Lo que tenemos aquí es una conjunción (lógica Y) de dos expresiones. El no terminal Ecoincide con un rectángulo no vacío de .s y Fun rectángulo no vacío de #s. El lado izquierdo de la conjunción coincide con rectángulos del tipo

...####..
...####..
...####..
#########
#########
.....##..
.....##..

donde tenemos EFEen la parte superior, luego F, y luego EFEotra vez. El lado derecho coincide con las transposiciones de estos, por lo que obtenemos exactamente las cruces.

Minería de diamantes

C=./+
T=\X|CTC/\/.+\\
B=\X|\\.+\//CBC
CTC/\X.+\X/CBC

El no terminal Ces una abreviatura de cualquier 1xncolumna. La mitad superior de un diamante se corresponde con T: es un simple Xu otro Trodeado por una columna en ambos lados, y una fila /[something]\debajo de eso. Bcoincide con la parte inferior de un diamante de la misma manera, y el nivel superior no terminal es solo una fila de la forma X[something]Xentre la mitad superior y la mitad inferior.

Encontrar tableros de ajedrez

(\#\#|\#/\#|\_\_|\_/\_)#!&[#_]{3-}

El lado derecho [#_]{3-}coincide con uno 3x3o más rectángulos de #sys _, mientras que el lado izquierdo garantiza que no contiene dos #s o _s adyacentes .

Verificando tableros de ajedrez

e,(\#\#|\#/\#|\_\_|\_/\_)#!&[#_]+/+

Esto es básicamente lo mismo que el anterior, excepto que podemos hacer coincidir cualquier rectángulo no vacío, pero necesitamos usar el eindicador para la verificación de entrada completa.

Verifique la sintaxis del preludio

A=[,()]/*
P=A*|P(A/\(/A)P(A/\)/A)P
e,P

Esta es probablemente la gramática más interesante hasta ahora. El no terminal Acoincide con cualquier columna que no contenga (o ), y Pcoincide con algún número de As, o dos paréntesis coincidentes, entre y fuera de los cuales hay más Ps.


@DLosc Corregido, ¡gracias por encontrar el error!
Zgarb

Funcionaria \#(.*|./*)\#?
seequ

@Sieg Para la alineación? Desafortunadamente no, porque eso se analizaría como "uno #a la izquierda, luego una fila o columna de cualquier cosa, luego uno #a la derecha". Necesito especificar que los #s se concatenan verticalmente a la columna usando las barras inclinadas /.
Zgarb

10

TMARL

Lenguaje de reconocimiento y coincidencia de plantillas

Descripción

Mi intérprete ocupa 24K caracteres (¿los fragmentos de código toman caracteres? ), Por lo que la descripción completa se puede encontrar aquí .

La mejor parte: el intérprete está en Javascript, lo que significa que puedes probarlo aquí mismo.

Y para los problemas:

# 1 - Encontrar tableros de ajedrez

$#_#
a
$_#_
bvacvbS5&(avcS5)G0G2P

&añade búsquedas El G2 al final obtiene el tercer elemento en un elemento de coincidencia, la coincidencia real. Los primeros 2 elementos son coordenadas xey (1 basado, no 0).

# 3 - Detecta un rectángulo de dígitos

$DD
$DD
S1G2P

Creo que este es bastante sencillo.

# 4 - Encontrar una palabra en una búsqueda de palabras

$GO\LF
a
$G
$*O
$**\L
$***F
S6&(aS6)G0G2P

El Sargumento es uniforme para que busque todas las rotaciones. Es mayor que 4 porque se puede agregar a la siguiente búsqueda (no se pueden agregar coincidencias individuales).

# 5 - Detectar entradas cuadradas

IL-(IG0L)!P

No estoy seguro de si esto es completamente legal, ya que solo determina la cuadratura correctamente si la entrada es un rectángulo. Compara la longitud de la entrada ILcon la longitud de la primera fila IG0Ly la invierte.

# 6 - Encuentra planeadores en un juego de la vida

$## 
$# #
$# 
a
$ ##
$## 
$  #
bS6&(bMS6)&(aS6)&(aMS6)G0G2P

Finalmente, un uso para espejo!

# 12 - Evita la letra Q

$[^Qq]
~4*4S1G2P

S1 porque solo se necesita 1 coincidencia.

Haré algunos de los más difíciles más tarde.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.