Solución original: JavaScript - 261 255 228 227 179 153 caracteres
/(\d)(\1(\d|.{6}|.{9})|(\d|.{6}|.{9})\1|.{7}\1(.|.{9})|(.|.{9})\1.{7}|(.{7,9}|.{17})\1.{8}|.{8}\1(.{7,9}|.{17}))\1/.test(s.replace(/\n/g,'A'))?'yes':'no'
Suponiendo que la cadena a probar está en la variable s
(para que sea una función, f
luego agregue f=s=>
al comienzo del código o, de lo contrario, tome la entrada de un indicador y luego reemplace s
con prompt()
).
Las salidas son a la consola.
3 RD Solución: JavaScript (ECMAScript 6) - 178 caracteres
p=x=>parseInt(x,36);for(t="2313ab1b8a2a78188h9haj9j8iaiir9r",i=v=0;s[i];i++)for(j=0;t[j];v|=s[i]==s[i+a]&s[i]==s[i+b]&i%9<8&(b>3|(i+b-a)%9<8))a=p(t[j++]),b=p(t[j++]);v?'yes':'no'
Tomé la segunda solución, a continuación, (que usa expresiones regulares para verificar los caracteres en ciertas configuraciones) y la modifiqué para verificar la cadena de caracteres idénticos en las mismas configuraciones sin usar expresiones regulares.
La cadena Base-36 "2313ab1b8a2a78188h9haj9j8iaiir9r"
proporciona pares de desplazamientos para verificar, es decir, el par 23
da como resultado la comprobación de si el carácter i th es idéntico al carácter (i + 2) th y al carácter (i + 3) th (el equivalente de la expresión regular (.).\1\1
- con algunas comprobaciones adicionales para garantizar que el carácter no idéntico no sea una nueva línea).
2 nd Solución: JavaScript (ECMAScript 6) - 204 caracteres
p=x=>parseInt(x,18);g=a=>a?a>1?"(.|\\n){"+a+"}":".":"";f=(x,a,b)=>RegExp("(.)"+g(a)+"\\1"+g(b)+"\\1").test(x);for(t="10907160789879h8",i=v=0;t[i];v|=f(s,x,y)||f(s,y,x))x=p(t[i++]),y=p(t[i++]);v?'yes':'no'
Crea múltiples expresiones regulares (ver más abajo para más detalles) usando pares de valores tomados de la cadena Base-18 10907160789879h8
y toma OR
todas las pruebas. Para reducirlo aún más, puede observar que las expresiones regulares vienen en pares donde una es la "inversa" de la otra (ignorando las expresiones regulares para 3 en una fila horizontal y verticalmente ya que el OP indica que nunca estarán presentes) si desea agregar esas pruebas nuevamente en el anexo 0088
a la cadena Base-18).
Explicación
Comience con 16 expresiones regulares que cubren todas las configuraciones posibles de movimientos válidos:
REs=[
/(\d)\1\1/, // 3-in-a-row horizontally
/(\d).\1\1/, // 3-in-a-row horizontally after left-most shifts right
/(\d)\1.\1/, // 3-in-a-row horizontally after right-most shifts left
/(\d)(?:.|\n){9}\1\1/, // 3-in-a-row horizontally after left-most shifts down
/(\d)(?:.|\n){7}\1.\1/, // 3-in-a-row horizontally after middle shifts down
/(\d)(?:.|\n){6}\1\1/, // 3-in-a-row horizontally after right-most shifts down
/(\d)\1(?:.|\n){6}\1/, // 3-in-a-row horizontally after left-most shifts up
/(\d).\1(?:.|\n){7}\1/, // 3-in-a-row horizontally after middle shifts up
/(\d)\1(?:.|\n){9}\1/, // 3-in-a-row horizontally after right-most shifts up
/(\d)(?:.|\n){7,9}\1(?:.|\n){8}\1/, // 3-in-a-row vertically (with optional top shifting left or right)
/(\d)(?:.|\n){7}\1(?:.|\n){9}\1/, // 3-in-a-row vertically after middle shifts right
/(\d)(?:.|\n){9}\1(?:.|\n){7}\1/, // 3-in-a-row vertically after middle shifts left
/(\d)(?:.|\n){8}\1(?:.|\n){7}\1/, // 3-in-a-row vertically after bottom shifts right
/(\d)(?:.|\n){8}\1(?:.|\n){9}\1/, // 3-in-a-row vertically after bottom shifts left
/(\d)(?:.|\n){17}\1(?:.|\n){8}\1/, // 3-in-a-row vertically after top shifts down
/(\d)(?:.|\n){8}\1(?:.|\n){17}\1/, // 3-in-a-row vertically after bottom shifts up
];
( Nota: las expresiones regulares para 3 en una fila horizontalmente ( 0º ) y verticalmente (parte del noveno ) son irrelevantes ya que el OP establece que las entradas que coincidan con estas nunca estarán presentes ) .
Probar cada uno de ellos contra la entrada determinará si se puede encontrar un movimiento válido de esa configuración.
Sin embargo, las expresiones regulares se pueden combinar para dar estos 6:
/(\d)(?:.|(?:.|\n){9}|(?:.|\n){6})?\1\1/ // Tests 0,1,3,5
/(\d)\1(?:.|(?:.|\n){9}|(?:.|\n){6})?\1/ // Tests 0,2,6,8
/(\d)(?:.|\n){7}\1(?:.|(?:.|\n){9})\1/ // Tests 4,10
/(\d)(?:.|(?:.|\n){9})\1(?:.|\n){7}\1/ // Tests 7,11
/(\d)(?:(?:.|\n){7,9}|(?:.|\n){17})\1(?:.|\n){8}\1/ // Tests 9,14
/(\d)(?:.|\n){8}\1(?:(?:.|\n){7,9}|(?:.|\n){17})\1/ // Tests 9a,12,13,15
Estos se pueden combinar en una sola expresión regular:
/(\d)(?:.|(?:.|\n){9}|(?:.|\n){6})?\1\1|(\d)\2(?:.|(?:.|\n){9}|(?:.|\n){6})?\2|(\d)(?:.|\n){7}\3(?:.|(?:.|\n){9})\3|(\d)(?:.|(?:.|\n){9})\4(?:.|\n){7}\4|(\d)(?:(?:.|\n){7,9}|(?:.|\n){17})\5(?:.|\n){8}\5|(\d)(?:.|\n){8}\6(?:(?:.|\n){7,9}|(?:.|\n){17})\6/
Que solo necesita ser probado contra la entrada.
Casos de prueba
Algunos casos de prueba que otras personas pueden encontrar útiles (no cumple con el formato de entrada de usar solo los dígitos 1-7, pero eso se corrige fácilmente y es solo una cuadrícula de 8x4, ya que es el mínimo requerido para una prueba de todas las entradas válidas )
En el formato de un mapa desde la cadena de entrada a cuál de las 16 expresiones regulares anteriores coincide.
Tests={
"12345678\n34567812\n56781234\n78123456": -1, // No Match
"12345678\n34969912\n56781234\n78123456": 1, // 3-in-a-row horizontally after left-most shifts right
"12345678\n34567812\n59989234\n78123456": 2, // 3-in-a-row horizontally after right-most shifts left
"12345978\n34567899\n56781234\n78123456": 3, // 3-in-a-row horizontally after left-most shifts down
"12345978\n34569892\n56781234\n78123456": 4, // 3-in-a-row horizontally after middle shifts down
"12345678\n34967812\n99781234\n78123456": 5, // 3-in-a-row horizontally after right-most shifts down
"12399678\n34967812\n56781234\n78123456": 6, // 3-in-a-row horizontally after left-most shifts up
"12345678\n34597912\n56789234\n78123456": 7, // 3-in-a-row horizontally after middle shifts up
"12345998\n34567819\n56781234\n78123456": 8, // 3-in-a-row horizontally after right-most shifts up
"12945678\n34597812\n56791234\n78123456": 9, // 3-in-a-row vertically after top shifts right
"12349678\n34597812\n56791234\n78123456": 9, // 3-in-a-row vertically after top shifts left
"12345978\n34569812\n56781934\n78123456": 10, // 3-in-a-row vertically after middle shifts right
"92345678\n39567812\n96781234\n78123456": 11, // 3-in-a-row vertically after middle shifts left
"12945678\n34967812\n59781234\n78123456": 12, // 3-in-a-row vertically after bottom shifts right
"12349678\n34569812\n56781934\n78123456": 13, // 3-in-a-row vertically after bottom shifts left
"12395678\n34567812\n56791234\n78193456": 14, // 3-in-a-row vertically after top shifts down
"12345698\n34567892\n56781234\n78123496": 15, // 3-in-a-row vertically after bottom shifts up
"12345678\n34567899\n96781234\n78123456": -1, // No match - Matches (.)\1.\1 but not 3 in a row
"12345679\n99567812\n56781234\n78123456": -1, // No match - Matches (.).\1\1 but not 3 in a row
};
Editar 1
Reemplazar \d
s con .
: guarda 6 caracteres.
Editar 2
Reemplace (?:.|\n)
con [\s\S]
y elimine grupos no capturadores adicionales y referencias de respaldo actualizadas (como lo sugiere m-buettner ) y agregue en la salida sí / no.
Editar 3
- Se agregó la solución ECMAScript 6 para construir las expresiones regulares individuales a partir de una cadena Base-18.
- Se eliminaron las pruebas para 3 en fila horizontalmente (como lo sugiere m-buettner ).
Editar 4
Se agregó otra solución (más corta) y dos casos de pruebas no coincidentes más.
Editar 5
- Solución original acortada mediante la sustitución de nuevas líneas con un carácter no numérico (como lo sugiere VadimR ).
Editar 6
- Solución original acortada mediante la combinación de bits de la expresión regular (como lo sugiere VadimR ).