Laser Mirror Portal Party

27

A bord 2D va contenir les objets suivants:

  • ^, >, v, Ou <: A laser émetteur vers le haut, à droite, vers le bas, ou vers la gauche respectivement. Il peut y avoir plus d'un. Les lasers se déplaceront en ligne droite dans un espace vide (l'espace vide est représenté par un point .). Les lasers ne traversent pas les émetteurs.
  • *: Une cible. Les lasers traversent les cibles. Il peut y avoir plus d'un.

Le tableau peut également contenir les objets suivants:

  • @: Un mur solide. Le laser ne passera pas ici.
  • \: Un réflecteur incliné vers la gauche . Modifie la direction des lasers selon le tableau suivant:

    Direction laser is travelling     Direction of laser after hitting reflector
    Up                                Left
    Right                             Down
    Down                              Right
    Left                              Up
    

    Il devrait être assez intuitif quant au fonctionnement des réflecteurs. Imaginez-les simplement comme un véritable miroir bilatéral et les directions devraient être claires.

  • /: Un réflecteur incliné à droite . Modifie la direction des lasers selon le tableau suivant:

    Direction laser is travelling     Direction of laser after hitting reflector
    Up                                Right
    Right                             Up
    Down                              Left
    Left                              Down
    
  • 1, 2, 3... 9: Un portail . Le nombre indique le canal du portail - il y aura exactement deux portails du même canal (par exemple, il n'y en aura pas trois 1). Le portail modifie la position des lasers à la position de l'autre portail du même canal. Par exemple:

    >     1     @     1     *
    

    Le laser frappera la cible car lorsqu'il touche le premier 1, il est téléporté vers le second 1de l'autre côté. Les lasers conservent la même direction qu'avant.

    Un portail ne téléportera pas le laser vers un portail d'un canal différent (c'est-à-dire qu'un 1ne téléportera pas le laser vers un 9.

Votre programme recevra une représentation 2D de la carte en entrée. La planche sera toujours de forme rectangulaire. La sortie devrait être Truesi toutes les cibles sont traversées par des lasers, ou Falseautrement.

Voici quelques cas de test:

  1. Contribution

    >....\
    ..*...
    >./../
    ..*...
    

    Sortie

    True
    
  2. Contribution

    >..........\
    1........../
    2..........1
    3..........2
    4..........3
    5..........4
    6..........5
    7..........6
    8..........7
    9..........8
    *..........9
    

    Sortie

    True
    
  3. Contribution

    >.@............*
    >..@...........*
    >...@..........*
    >....@.........*
    >.....@........*
    >...*..@........
    >.......@......*
    

    Sortie

    False
    
  4. Contribution

    ../\.
    >./**
    

    Sortie

    False
    
  5. Contribution

    /.......*.......\/3.....
    @..............//\.\....
    *.............2\.1\/\...
    \..............///.....<
    .........*...//\\/.....\
    >.............\.1.///.4.
    4.......*/...\2\/3/\/..^
    

    Sortie

    True
    
  6. Contribution

    vvvvvvvvvvvvvvvvv
    \\\\\\\\\\\\\\\\\
    /////////////////
    \\\\\\\\\\\\\\\\\
    /////////////////
    \\\\\\\\\\\\\\\\\
    /////////////////
    *****************
    

    Sortie (notez la cible à l'extrême droite)

    False
    
Absinthe
la source
Cela n'aurait-il pas plus de sens si un réflecteur incliné vers la droite (/) changeait la direction d'un faisceau laser de gauche (←) vers le bas (↓)?
squeamish ossifrage
@squeamish ossifrage Je suis désolé, je ne comprends pas votre question. Quelle règle la réflexion sur la table de réflecteur se penchant à gauche pensez - vous est incorrect?
absinthe
Je pense que vous vous êtes mélangé à gauche et à droite
squeamish ossifrage
1
Que se passe-t-il si le laser atteint la limite de la grille?
DavidG
2
@DavidG Rien, ou ça rebondit comme ça. (Ils sont équivalents dans ce cas). Il ne s'enroule pas comme on peut le voir dans l'exemple 6.
Dennis Jaheruddin

Réponses:

8

Python, 310 302 287 278 277 260

Pas radicalement différent de la publication Python existante, mais a un ou deux trucs remarquables, je pense. Il gère également les entrées "sans terminaison", telles que 1>1. EDIT : Oups! les émetteurs bloquent les lasers.

def t(b):
 w=len(b[0])+1;B=list('@'*w+'@'.join(b));i=l=len(B);C="<>^v@"
 while i:
    j=l-i;i-=1;d=C.find(B[j]);c='.'
    while c not in C:
     if'+'>c:B[j]='.'
     if'0'<c<C:j=(B*2).index(c,j+1)%l
     elif'.'<c:d^=2+(c<C)
     j-=[1,-1,w,-w,j][d];c=B[j%l]
 return'*'not in B

t prend une liste de chaînes (les lignes d'entrée) et retourne un résultat booléen.

Voici un joli gif du code en cours de golf:

entrez la description de l'image ici

EDIT : Awsome gif avec l'aimable autorisation de Will. Merci Will!

Aune
la source
La spécification précise que "les lasers ne passent pas par les émetteurs". ainsi 1>1prendra fin. Je n'ai pas pu trouver quelque chose qui ne se termine pas, même si je n'y ai pas mis beaucoup d'efforts et j'ai supposé que cela ne se produisait pas pour mon implémentation. Je vais bien sûr reconsidérer si quelqu'un peut en présenter un.
VisualMelon
4
@VisualMelon: Les règles sont symétriques dans le temps, sauf aux endroits où les lasers naissent ou meurent, ce qui signifie que tout doit se terminer (car vous pouvez toujours le retracer de manière unique jusqu'au point où il est né, et les émetteurs ne peuvent pas eux-mêmes faire partie d'une boucle).
Micah
@Micah hehe, merci pour une explication appropriée, comme je l'ai dit, je suis allé avec l'intuition et je ne me suis pas beaucoup inquiété, merci d'avoir mis un autre outil dans ma boîte.
VisualMelon
Ouais, je l'ai mal lu.
Ell
Chapeau à Ell! Très bien fait. Je pense que vous pouvez raser quelques octets de plus en utilisant le fait que .find(d)renvoie -1 s'il n'est pas trouvé. Si vous supprimez l' if-1<d:instruction et que vous le faites j+=[-1,1,w,-w,-i][d]à la place en haut de la boucle while, un -1 non trouvé se transformera en ajout du dernier élément de ce tableau à j, ce qui fera j0, que nous savons être @...?
Will
7

Perl, 647

C'est ma première tentative de code-golf, et je suis un peu gêné de ne pas avoir battu le score C #, mais j'ai pensé qu'il serait intéressant (ou amusant, ou simplement masochiste) de faire le tout en tant que série de substitutions d'expression régulière. (J'ai aussi pensé que ce serait amusant de rafraîchir mon Perl, mais à la fin, je regrettais profondément de ne pas l'avoir implémenté en Ruby ou Python.)

Je n'ai pas fait beaucoup de tests, mais je pense qu'il devrait gérer tous les cas.

La grille est entrée via STDIN. Il doit y avoir au moins une nouvelle ligne dans l'entrée (c'est-à-dire qu'une seule ligne sans nouvelle ligne ne fonctionnera pas).

%s=(d,'[|+#$vk%ZX]',u,'[|+#$^W%KX]',r,'[-G+#>k%KX]',l,'[-G+#<W%ZX]');%o=(d,'[-.*G/k\\\\Z',u,'[-.*G/W\\\\K',r,'[|.*$\\\\/kK',l,'[|.*$\\\\/ZW');for$d(d,u,r,l){$o{$d}.='123456789qwertyuio]'}%u=(d,'.|-+*$G#/Wk%\KZX',u,'.|-+*$G#/kW%\ZKX',r,'.-|+*G$#/Wk%\ZKX',l,'.-|+*G$#/kW%\KZX');@q=split//,"qwertyuio";local$/;$_=<STDIN>;for$i(1..9){$m{$i}=$q[$i-1];$m{$m{$i}}=$i;s/$i/$m{$i}/e}/.*?\n/;$l='.'x((length$&)-1);do{$c=0;for$d(d,u,r,l){%p=(d,"(?<=$s{d}$l)$o{d}",u,"$o{u}(?=$l$s{u})",r,"(?<=$s{r})$o{r}",l,"$o{l}(?=$s{l})");%h=split//,$u{$d};$c+=s!$p{$d}!$h{$&}||($v=$&,($o{$d}=~s/$v// && $s{$d}=~s/]/$m{$v}]/),$v)!es}}while($c);print/\*/?"False\n":"True\n"

Explication: le code met à jour de manière itérative la chaîne de grille lorsque les lasers la traversent. -représente un laser horizontal, |un laser vertical, des +lasers croisés,K un \miroir avec un laser rebondissant sur le dessus, kun /miroir avec un laser rebondissant sur le fond, Zun \miroir avec un laser rebondissant sur le bas et Wun /miroir avec un laser rebondissant sur le dessus haut. %est un /miroir avec des lasers des deux côtés, tandis Xqu'un \miroir avec des lasers des deux côtés. (Ils sont sensibles à la casse. J'ai essayé de choisir des lettres qui semblent quelque peu appropriées - par exemple, ketKsont des choix quelque peu évidents - mais malheureusement l'effet n'est vraiment pas très utile. Je devrais vraiment mettre ces informations dans un tableau, mais je suis épuisé en ce moment.)

La gestion des portails de la même manière (c.-à-d. En affectant à chaque chiffre un ensemble de caractères supplémentaires en fonction des positions laser d'entrée / sortie possibles) nécessiterait 144 caractères (y compris le 9 d'origine), donc à la place, lorsqu'un laser frappe un portail "d'entrée", J'ajoute le caractère de portail "de sortie" à l'ensemble de caractères qui émettent un laser dans la bonne direction. (Cela nécessite une distinction entre les portails d'entrée et de sortie; j'ai utilisé les lettres qwertyuiopour cela.)

Un peu sans golf, avec des déclarations imprimées pour que vous puissiez voir les substitutions se produire (chaque substitution représente un "round" de progression laser), et avec le g drapeau ajouté à la principale s///afin qu'il ne prenne pas autant d'itérations:

# Throughout, d,u,r,l represents lasers going down, up, left, or right
# `sources` are the character classes representing laser "sources" (i.e. any
# character that can, on the next round, cause a laser to enter the space
# immediately adjacent to it in the proper direction)
%sources=(d,'[|+#$vk%ZX]',u,'[|+#$^W%KX]',r,'[-G+#>k%KX]',l,'[-G+#<W%ZX]');
# `open` characters will not block a laser
%open=(d,'[-.*G/k\\\\Z',u,'[-.*G/W\\\\K',r,'[|.*$\\\\/kK',l,'[|.*$\\\\/ZW');
# One of each portal is changed into the corresponding letter in `qwertyuio`.
# At the start, each portal is 'open' and none of them is a source.
for$d(d,u,r,l){$open{$d}.='123456789qwertyuio]'}
# A mapping of 'open' characters to the characters they become when a laser
# goes through them. (This is used like a hash of hashes; see the assignment
# of `%h` below.)
%update=(d,'.|-+*$G#/Wk%\KZX',
    u,'.|-+*$G#/kW%\ZKX',
    r,'.-|+*G$#/Wk%\ZKX',
    l,'.-|+*G$#/kW%\KZX');
@q=split//,"qwertyuio";
local$/;$_=<STDIN>;
for$i(1..9){
    $m{$i}=$q[$i-1];
    $m{$m{$i}}=$i;
    s/$i/$m{$i}/e}
print "After substituting portals:\n";
print;
print "\n";
# Find the number of characters in each line and create a string of `.`'s,
# which will be used to correlate characters above/below one another in the
# grid with each other.
/.*?\n/;
$l='.'x((length$&)-1);
do{
    $changes=0;
    for$d(d,u,r,l){
        # `patterns` is a mapping from each direction to the regex representing
        # an update that must occur (i.e. a place where a laser must progress).
        # Each pattern is either a lookahead or lookbehind plus the necessary
        # "open" character class.
        %patterns=(d,"(?<=$sources{d}$l)$open{d}",
            u,"$open{u}(?=$l$sources{u})",
            r,"(?<=$sources{r})$open{r}",
            l,"$open{l}(?=$sources{l})");
        %h=split//,$update{$d};
        # Match against the pattern for each direction. Note whether any
        # matches were found.
        $changes+=s!$patterns{$d}!
            # If the "open" character for a map is in the `update` map, return
            # the corresponding value. Otherwise, the "open" character is a
            # portal.
            $h{$&} || ($v=$&,
                        # For portals, remove the input portal from the
                        # proper "open" list and add the output portal to
                        # the proper "source" list.
                       ($open{$d}=~s/$v// && $sources{$d}=~s/]/$m{$v}]/),
                       $v)
                    # This whole substitution should allow `.` to match
                    # newlines (see the definition of `$l` above), and the
                    # replacement must be an expression rather than a string
                    # to facilitate the portal logic. The `g` allows multiple
                    # updates per "frame"; it is left out of the golfed code.
                    !egs
    }
    # Print the next "frame".
    print;
    print "\n";
# Continue updating until no "open" spaces are found.
}while($changes);
# Print whether `*` is still present in the input.
print/\*/?"False\n":"True\n"
Kyle Strand
la source
J'ai expérimenté ce type d'approche (en utilisant des tableaux bool plutôt que regex) en Python, mais je ne pouvais pas me rapprocher de ce petit. Je pense que c'est une approche qui fait réfléchir! Mes tentatives ont été influencées à tort par catpad.net/michael/apl avec une belle vidéo youtube.com/watch?v=a9xAKttWgP4 et petercollingridge.co.uk/blog/python-game-of-life-in-one-line
que
1
@Will Merci! J'ai vraiment réalisé à quel point mes efforts étaient similaires à ceux du GoL au moment où j'ai déterminé à quel point il serait possible d'utiliser un caractère différent pour chaque combinaison possible de lasers entrant et sortant d'un portail. Je pense que je pourrais peut-être raser quelques personnages de plus, mais ... ce n'est clairement pas l'approche optimale!
Kyle Strand,
De plus, si quelqu'un connaît une meilleure façon de gérer les `` triples échappés dans les classes de personnages dans les premières lignes, ce serait bien ...
Kyle Strand
6

Python 338 351

def t(b):
 L=len;w=L(b[0])+3;b=list("@"*w+"@@".join(b)+"@"*w);w-=1;I=b.index
 for i in range(L(b)):
  c=b[i];d={"^":-w,"<":-1,">":1,"v":w}.get(c)
  if d:
   while c!='@':
    i+=d;c=b[i]
    if c=='*':b[i]='.'
    elif c in '/\\':d={-w:-1,w:1,1:w,-1:-w}[d]*(-1 if c=='/' else 1)
    elif c>'0':i+=I(c)-i or I(c,i+1)-i
 return "*" not in b

Ma version non minimisée trace en fait les trajectoires laser sur la carte, ce qui est plutôt joli:

>-+--\
..X..|
>-/--/
..X...

>----------\
1----------/
2----------1
3----------2
4----------3
5----------4
6----------5
7----------6
8----------7
9----------8
X----------9

>-@............*
>--@...........*
>---@..........*
>----@.........*
>-----@........*
>---X--@........
>-------@......*

/-------X+------\/3.....
@........|.....//\+\....
X........|....2\+1\/\...
\--------+----+///+++--<
.........X...//\\/+++--\
>--------+---+\+1+///-4|
4-------X/...\2\/3/\/..^

vvvvvvvvvvvvvvvvv
\\\\\\\\\\\\\\\\\
/////////////////
\\\\\\\\\\\\\\\\\
/////////////////
\\\\\\\\\\\\\\\\\
/////////////////
XXXXXXXXXXXXXXXX*

def debug(board,x,y):
    emit_dir = {
        "^":    ( 0, -1),
        "<":    (-1,  0),
        ">":    ( 1,  0),
        "v":    ( 0,  1),
    }
    class PortalException(Exception): pass
    xdir, ydir = emit_dir[board[y][x]]
    while True:
        # print "step (%d, %d) (%d, %d)" % (x, y, xdir, ydir)
        x += xdir
        y += ydir
        if y < 0 or y >= len(board) or x < 0 or x >= len(board[y]):
            return
        ch = board[y][x]
        if ch == '/':
            xdir, ydir = -ydir, -xdir
        elif ch == '\\':
            xdir, ydir = ydir, xdir
        elif ch in '@^><v':
            return
        elif ch == '*':
            board[y] = board[y][:x] + 'X' + board[y][x+1:]
        elif ch in '.-|':
            ch = ('-' if xdir else '|') if ch == '.' else '+'
            board[y] = board[y][:x] + ch + board[y][x+1:]
        elif ch in '123456789':
            try:
                for r in range(len(board)):
                    for c in range(len(board[r])):
                        if board[r][c] == ch and (r != y or c != x):
                            x, y = c, r
                            raise PortalException()
                raise Exception("could not find portal %s (%d,%d)" % (ch, x, y))
            except PortalException:
                pass
Volonté
la source
5

C # - 515 414 400 octets

Programme C # complet, aucune sortie sympa comme celle de Will. Fonctionne en suivant le chemin laser pour chacune des émissions individuelles et en conservant un tableau des cellules que nous avons visitées, afin que nous puissions vérifier que nous avons visité toutes les étoiles à la fin.Edit: agrégé un grand nombre d'octets en faisant tout 1D et en utilisant un char au lieu d'un int pour stocker le char actuel

w0lf m'a rappelé que j'avais une boucle for sous-utilisée en plein milieu de mon code, alors j'ai pensé que je ferais mieux de faire un dernier effort et de le mettre au travail, et maintenant je suis au nombre minimum absolu de bouclés bretelles. Je ne prétendrai pas aimer l'effondrement de la seconde boucle for, le code est horriblement désordonné maintenant, mais il a économisé quelques octets. Dans le processus, j'ai réécrit la gestion du portail. J'ai également trouvé une méthode plus courte pour effectuer le «déplacement» avec une opération conditionnelle imbriquée plutôt qu'agrégée.

Code golf:

using C=System.Console;class P{static void Main(){var S=C.In.ReadToEnd().Replace("\r","");int W=S.IndexOf('\n')+1,l=S.Length,i=l,d,m,n;var M=new int[l];for(char c;i-->0;)for(d="^<v>".IndexOf(c=S[m=i]);c>14&d>-1;d=(m+=d==2?W:d>0?d-2:-W)>=0&m<l&&"@^<v>".IndexOf(c=S[m])<0?d:-1)for(d=c==47?3-d:c==92?d^1:d,M[n=m]=1;c%49<9&&(m=S.IndexOf(c,m+1))==n|m<0;);for(;l-->0;)W*=S[l]==42?M[l]:1;C.WriteLine(W>0);}}

Code moins golfé:

using C=System.Console;

class P
{
    static void Main()
    {
        var S=C.In.ReadToEnd().Replace("\r",""); // read the grid, remove pesky carriage returns
        int W=S.IndexOf('\n')+1,l=S.Length,i=l,d,m,n; // find "width"
        var M=new int[l]; // defaults to 0s

        for(char c;i-->0;) // for each cell

            for(d="^<v>".IndexOf(c=S[m=i]); // find initial direction, if any
                c>14&d>-1; // loop only if we have direction
                d=(m+=d==2?W:d>0?d-2:-W) // move (after iteration)
                >=0&m<l&&"@^<v>".IndexOf(c=S[m])<0?d:-1) // terminate if we hit something or go off edge

                for(d=c==47?3-d:c==92?d^1:d, // mirrors
                    M[n=m]=1; // we have seen this spot
                    c%49<9&&(m=S.IndexOf(c,m+1))==n|m<0;); // portals

        for(;l-->0;) // for each cell
            W*=S[l]==42?M[l]:1; // if *, then mul by whether seen

        C.WriteLine(W>0);
    }
}

Le nouveau code de gestion du portail utilise le fait que la fonction String.IndexOf renvoie heureusement -1 (c'est-à-dire un caractère introuvable) si vous lui demandez de commencer à regarder 1 caractère au-delà de la chaîne (lève une exception si vous lui demandez de commencer plus loin). C'était une nouvelle pour moi, mais c'était terriblement pratique dans ce cas.

VisualMelon
la source
+1 Golf génial! Je viens de penser à un truc: vous pouvez prendre le m+=(d>0?d-2:0)+(d<3?d-1:0)*W;et le fourrer dans la for, comme ceci: for(char c;i-->0;m+=(d>0?d-2:0)+(d<3?d-1:0)*W). De cette façon, vous économiserez un caractère, car vous perdrez un point-virgule.
Cristian Lupascu
@ w0lf a fait un dernier effort et a réussi à réduire complètement les boucles for, merci pour le coup de
pouce