Trouver des serpents dans une matrice

32

Défi

Étant donné une matrice binaire et une chaîne binaire, déterminez si cette chaîne binaire peut être trouvée en commençant à n'importe quel point de la matrice et en se déplaçant dans n'importe quelle direction à tout point suivant pour former la chaîne binaire. Autrement dit, la chaîne peut-elle être trouvée repliée à l'intérieur de la matrice?

La chaîne ne peut être pliée qu'à 90 degrés ou 180 degrés (connexions latérales; Manhattan Distance 1) et ne peut se chevaucher à aucun moment.

Exemple

Prenons l'exemple suivant:

Matrix:

010101
111011
011010
011011

Snake: 0111111100101

Ceci est un cas de test véridique. On peut voir le serpent replié dans la position suivante:

0-1 0 1 0 1
  |
1 1 1-0 1 1
  | | |   |
0 1 1 0-1-0
  | |
0 1-1 0 1 1

Règles

  • Les échappatoires standard s'appliquent
  • Vous pouvez prendre la longueur de la chaîne et la largeur et la hauteur de la matrice en entrée si vous le souhaitez
  • Vous pouvez prendre la matrice binaire et la chaîne binaire comme une chaîne multiligne / un tableau de chaînes / une chaîne jointe à une nouvelle ligne / toute autre chaîne jointe à une autre et une chaîne
  • Vous pouvez prendre les dimensions comme un tableau plat au lieu de plusieurs arguments
  • Votre programme doit se terminer pour n'importe quelle matrice 5 x 5 avec une chaîne d'une longueur maximale de 10 en moins d'une minute

Limites

  • La matrice n'est pas nécessairement carrée
  • La chaîne sera non vide
  • La chaîne peut avoir une longueur de 1
  • La chaîne ne contiendra pas plus de carrés que disponible (c'est-à-dire, len(string) <= width(matrix) * height(matrix)

Cas de test

Vérité

01010
10101
01010
10101
01010

0101010101010101010101010



01110
01100
10010
10110
01101

011111000110100



0

0



10
01

1010



100
010
001

100010001

Falsy

00000
00000
00000
00000
00000

1



10101
01010
10101
01010
10101

11



100
010
001

111



10001
01010
00100
01010
10001

1000100010001000101010100
HyperNeutrino
la source
Sandbox Post (Supprimé)
HyperNeutrino
4
Ou: Boggle binaire! Pouvez-vous également ajouter quelques cas de test supplémentaires?
Jonah
1
Que signifient plat, tranchant et rond dans ce contexte? Le carré ne signifie-t-il pas que la largeur et la hauteur peuvent ne pas être égales ou que le tableau peut être irrégulier?
Tahg
ce qui est sur terre est un tableau rond
Conor O'Brien
En relation.
Martin Ender

Réponses:

13

Python 2 , 275 271 264 249 octets

  • Sauvé quatre octets par le remplacement -1avec Het en supprimant une opération de tranchage ( [:]).
  • Enregistré sept octets grâce à Halvard Hummel ; supprimer encore une autre opération de découpage ( [:]), utiliser l'affectation de cibles multiples pour donner à une entrée visitée une valeur v not in "01"( S=S[1:];M[y][x]=H;-> S=M[y][x]=S[1:];) et passer d'un ternaire if / else à un simple logique ou ( any(...)if S else 1-> not S or any(...)).
  • Si vous étendez quelque peu votre définition de truey et falsey , vous pouvez autoriser cette solution longue de 257 octets . Il déclenche une exception ( ZeroDivisionError) lorsque le serpent est trouvé et retourne une liste vide ( []) lorsqu'il n'y a pas de serpent à trouver, qui sont deux comportements distincts.
  • Quatorze octets enregistrés grâce à user202729 ; jouer au golf deux copies profondes de tableau
  • Enregistré un octet; joué not S orau golf à S<[1]or~ S==[]or.
lambda M,S,w,h:any(H(eval(`M`),S,w,h,x,y)for y in range(h)for x in range(w)if S[0]==M[y][x])
def H(M,S,w,h,x,y):S=M[y][x]=S[1:];return S<[1]or any(H(eval(`M`),S,w,h,x+X,y+Y)for X,Y in[(~0,0),(1,0),(0,~0),(0,1)]if~0<x+X<w>0<=y+Y<h!=S[0]==M[y+Y][x+X])

Essayez-le en ligne!

Explication

Fonction lambda qui prend la matrice comme une liste bidimensionnelle de chaînes (soit "0"ou "1"), le serpent comme une liste unidimensionnelle et les dimensions de la matrice comme deux entiers.
La fonction lambda recherche dans la matrice des entrées qui correspondent au premier élément du serpent. Pour chaque correspondance trouvée, il appelle Havec une copie profonde de la matrice, pas de copie du serpent, les dimensions de la matrice et la position de la correspondance.

Quand Hest appelé, il supprime Sla première entrée et définit l'entrée de matrice de la position donnée sur autre chose que "0", "1". Si S'length est nul, il retourne True; comme il s'appelle récursivement, le serpent a été trouvé quelque part dans la matrice.
Si la S`` longueur n'est pas nulle, elle parcourt les quatre directions cardinales, teste si cette position est dans la matrice, compare l'élément de la matrice à cette position avec le premier élément de Set - s'il correspond - s'appelle récursivement.
HLes valeurs de retour de sont dirigées vers le haut des cadres de pile, vérifiant toujours si au moins une fonction a trouvé un serpent possible.

Sortie formatée

J'ai augmenté mon programme pour afficher également le chemin des glisseurs de serpent (s'il y en a un). Il utilise la même conception de sortie ASCII que la question. Lien TIO .

Jonathan Frech
la source
264 octets
Halvard Hummel
1
@HalvardHummel Merci; spécialement pour repérer l'opération de tranchage superflue.
Jonathan Frech
@ user202729 Vous pensez m[:]for~> m*1for? Pourrait fonctionner.
Jonathan Frech
@ user202729 Merci, l'astuce liée a fonctionné car je pense que cela nécessite une copie complète.
Jonathan Frech
9

JavaScript (ES6), 138 134

Pas si différent de @ Neil's, mais quoi d'autre pourrait-il être?

Entrée: matrice sous forme de chaîne de plusieurs lignes, chaîne binaire, largeur (sans compter la nouvelle ligne)

NB: la logique de la fonction récursive rest quelque peu inversée pour économiser quelques octets

(m,s,w)=>[...m].some((c,p,m,r=(p,o)=>s[m[p]=r,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))

Moins golfé

(m,s,w)=>(
  m=[...m],
  r= (p, o) => 
    (m[p] = -w, s[o])
    && (
         [~w, -~w, 1, -1].every( d =>
            m[d+=p] != s[o] || r(d, o+1)
         )
         && (m[p]=s[o-1])
    ),
  m.some((c,p) =>c == s[0] && !r(p,1))
)

Tester

var F=
(m,s,w)=>[...m].some((c,p,m,r=(p,o)=>s[m[p]=r,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))

// this slightly modified version tracks the path
var Mark=
(m,s,w)=>(m=[...m]).some((c,p,m,r=(p,o)=>s[m[p]=-o,o]&&([~w,-~w,1,-1].every(d=>m[d+=p]!=s[o]||r(d,o+1))&&(m[p]=s[o-1])))=>c==s[0]&&!r(p,1))
?m.map((c,p)=>c<-1?'.───│┘└.│┐┌.│'[((m[p-1]-c)**2<2)+((m[p+1]-c)**2<2)*2+((m[p+~w]-c)**2<2)*4+((m[p-~w]-c)**2<2)*8]:c<0?'*':c).join``:''

function go()
{
  O.textContent =F(M.value, S.value, M.value.search('\n'))+'\n\n'
  +Mark(M.value, S.value, M.value.search('\n'))
}

go()
#M {width:100px; height:100px }
<textarea id=M>010101
111011
011010
011011</textarea><br>
<input id=S value='0111111100101' oninput='go()'>
<button onclick='go()'>go</button>
<pre id=O></pre>

edc65
la source
6

JavaScript (ES6), 149 octets

(m,s,w)=>[...m].some((c,i)=>c==s[0]&&g(m,s,i),g=(m,s,i)=>!(s=s.slice(1))||[~w,-1,1,-~w].some(o=>m[o+=i]==s[0]&&g(m.slice(0,i)+' '+m.slice(i+1),s,o)))

Prend la matrice comme une chaîne délimitée par des sauts de ligne, le serpent comme une chaîne et la largeur (comme un entier). Librement basé sur la réponse de @ JonathanFrech.

Neil
la source
4

Mathematica, 180 156 141 141 153 138 136 104 octets

MemberQ[#|Table[""<>Part[Join@@#,p],{x,1##4},{y,1##4},{p,FindPath[GridGraph@{##4},x,y,#3,All]}],#2,All]&

Exemple d'entrée

[{{"1","1","1","1","1"},{"0","0","0","0","0"}},"10011001",8,5,2]

Explication

  1. GridGraph@{##4}est un Graphobjet pour une grille de sommets avec des sommets adjacents reliés par des arêtes, avec des dimensions {##4}- c'est-à-dire, {#4,#5}ou {width,height}.
  2. Nous parcourons tous les sommets de départ x(numérotés 1à 1##4 = width*height), tous les sommets de fin yet tous les chemins pde longueur au plus #3de xà y.
  3. Pour chacun de ces chemins, ""<>Part[Join@@#,p]extrait les caractères correspondants de la matrice et les place dans une chaîne.
  4. Nous incluons également la matrice elle-même, dont les caractères sont toutes les chaînes de longueur 1 que l'on peut y trouver.
  5. Nous voyons si l'une de ces chaînes correspond s, en recherchant à tous les niveaux, car il s'agit d'une liste très multidimensionnelle que nous avons créée.

Remarque: Le remplacement #3par {#3-1}in FindPath, afin que nous ne trouvions que des chemins exactement de la bonne longueur, est une énorme amélioration en termes de vitesse - mais coûte 4 octets de plus.


-24 octets: prendre les dimensions des choses en entrée

-15 octets: utiliser StringPartet StringJoincorrectement

+12 octets: correction du cas de longueur 1

-15 octets: ...

-2 octets: prendre la taille de la matrice en entrée sous forme de tableau

-32 octets: utiliser Tablepour itérer sur le chemin nous permet d'éviter d'utiliser Function, et l'utilisation MemberQ[...,s,All]nous permet simplement de coller la matrice sur la table lorsqu'il s'agit de serpents de longueur 1.

Misha Lavrov
la source
3

C # (.NET Core) , 346 341 336 302 297 octets

(m,h,w,s,l)=>{for(int y=0;y<h;y++)for(int x=0;x<w;x++)if(N(x,y,l-1))return 0<1;return 1<0;bool N(int x,int y,int p){if(p<0)return 0<1;if(y<0|x<0|y==h|x==w||m[y,x]>1||s[p]!=m[y,x])return 1<0;int g=m[y,x];m[y,x]=2;if(N(x,y-1,--p)||N(x-1,y,p)||N(x,y+1,p)||N(x+1,y,p))return 0<1;m[y,x]=g;return 1<0;}}

Essayez-le en ligne!

5 octets enregistrés en jouant à l' pincrément

5 octets économisés en prenant la longueur du serpent et en commençant par sa queue, et en supprimant un espace inutile

34 octets économisés en lisant correctement le défi et en voyant que je peux prendre en compte la hauteur et la largeur de la matrice

5 octets enregistrés, le cas de test d'un seul élément a échoué et le correctif a été bénéfique

Ungolfed

(m,h,w,s,l)=>{
    // Go through every potential starting point
    for(int y=0; y<h; y++)
        for(int x=0; x<w; x++)
            if(N(x,y,l-1)) // start the recursive steps
                return 0<1; // return true if N returns true, otherwise check the next element

    return 1<0; // return false as the snake doesn't fit into the matrix

    // C#7 local function in a Func
    bool N(int x, int y, int p)
    {
        // if there is no more snake to fit return true
        if(p<0)
            return 0<1;

        // if m element has part of the snake or 
        // snake part doesn't match matrix element then return false
        if(y<0 | x<0 | y==h | x==w || m[y,x]>1 || s[p] != m[y,x])
            return 1<0;

        // hold the current matrix element
        int g=m[y,x];
        // set the current matrix element to 2 to indicate it has a part of the snake
        m[y,x]=2;

        // check each of the four neighbours and recurse down that neighbour 
        // except if they are outside the matrix
        if(N(x,y-1,--p) ||
           N(x-1,y,p) ||
           N(x,y+1,p) ||
           N(x+1,y,p))
               return 0<1; // return true if remainder of the snake fits into the matrix

        // if snake doesn't fit then set the matrix element as not having part of the snake
        m[y,x]=g;
        // return false to indicate this neighbour direction doesn't fit the snake
        return 1<0; 
    }
}
Ayb4btu
la source
Un début de golf serait de supprimer tous les espaces inutiles ...
Jonathan Frech
if(...)return true;-> return ...;.
Jonathan Frech
@JonathanFrech D'accord, mais je l'ai laissé comme ça pour permettre aux autres de le lire un peu plus facilement jusqu'à ce que j'aie la chance d'y revenir (un jour demain).
Ayb4btu
@JonathanFrech Ne fonctionne pas, b[y,x]doit être réinitialisé à un moment donné. (Aussi désolé d'avoir mal orthographié votre nom dans ma réponse.)
Neil
Je voulais dire if(N(x,y,0)>0)return 0<1;; la première apparition de return.
Jonathan Frech
1

Kotlin , 413 octets

var x:(Array<Array<Char>>,String)->Boolean={b,s->fun f(s:String,x:Int,y:Int):Boolean{if(b[x][y]!=s[0])
return 0>1
if(s.length<2)
return 1>0
val v=b[x][y]
b[x][y]='Z'
try{return(-1..1).map{x+it}.flatMap{t->(-1..1).map{y+it}.map{t to it}}.filter{(X,Y)->(x-X)*(x-X)+(y-Y)*(y-Y)==1&&X in b.indices&&Y in b[0].indices&&f(s.substring(1),X,Y)}.any()}finally{b[x][y]=v}}
b.indices.any{x->(0..b[0].size-1).any{f(s,x,it)}}}

Embellie

var x: (Array<Array<Char>>, String) -> Boolean = { b, s ->
    fun f(s: String, x: Int, y: Int): Boolean {
        if (b[x][y] != s[0])
            return 0 > 1
        if (s.length < 2)
            return 1 > 0
        val v = b[x][y]
        b[x][y] = 'Z'
        try {
            return (-1..1).map{ x + it }
                    .flatMap { t -> (-1..1).map{y+it}.map { t to it } }
                    .filter { (X, Y) ->
                        (x - X)*(x - X) + (y - Y)*(y - Y) == 1 &&
                                X in b.indices && Y in b[0].indices &&
                                f(s.substring(1), X, Y) }
                    .any()
        } finally {
            b[x][y] = v
        }
    }
    b.indices.any { x -> (0..b[0].size - 1).any { f(s, x, it) } }
}

Tester

var x:(Array<Array<Char>>,String)->Boolean={b,s->fun f(s:String,x:Int,y:Int):Boolean{if(b[x][y]!=s[0])
return 0>1
if(s.length<2)
return 1>0
val v=b[x][y]
b[x][y]='Z'
try{return(-1..1).map{x+it}.flatMap{t->(-1..1).map{y+it}.map{t to it}}.filter{(X,Y)->(x-X)*(x-X)+(y-Y)*(y-Y)==1&&X in b.indices&&Y in b[0].indices&&f(s.substring(1),X,Y)}.any()}finally{b[x][y]=v}}
b.indices.any{x->(0..b[0].size-1).any{f(s,x,it)}}}

data class Test(val board: String, val snake: String, val output: Boolean)

val tests = listOf(
        Test("""01010
            |10101
            |01010
            |10101
            |01010""", "0101010101010101010101010", true),
        Test("""01110
            |01100
            |10010
            |10110
            |01101""", "011111000110100", true),
        Test("""0""", "0", true),
        Test("""10
            |01""", "1010", true),
        Test("""100
            |010
            |001""", "100010001", true),
        Test("""00000
            |00000
            |00000
            |00000
            |00000""", "1", false),
        Test("""10101
            |01010
            |10101
            |01010
            |10101""", "11", false),
        Test("""100
            |010
            |001""", "111", false),
        Test("""10001
            |01010
            |00100
            |01010
            |10001""", "1000100010001000101010100", false)
)

fun main(args: Array<String>) {
    tests.filter {(board, snake, expected) ->
        val boardR = board.trimMargin().lines().map { it.toCharArray().toTypedArray() }.toTypedArray()
        val result = x(boardR, snake)
        result != expected
    }.forEach { throw AssertionError(it) }
    println("Test Passed")
}
jrtapsell
la source