Code Golf: Lasers

Le défi

le code le plus court par nombre de caractères pour entrer une représentation 2D d'une carte, et la sortie 'vrai' ou 'faux' selon l'entrée .

la planche est faite de 4 types de carreaux:

 # - A solid wall
 x - The target the laser has to hit
 / or  - Mirrors pointing to a direction (depends on laser direction)
 v, ^, > or < - The laser pointing to a direction (down, up, right and left respectively)

Il y a seulement un laser et seulement une cible . Les murs doivent former un rectangle solide de toute taille, où le laser et cibles sont placées à l'intérieur. Les murs à l'intérieur de la chambre sont possibles.

rayon Laser coups de feu et se déplace à partir de son origine à la direction de pointage. Si un rayon laser touche le mur, il s'arrête. Si un rayon laser frappe un miroir, il rebondit à 90 degrés dans la direction du miroir. Les miroirs sont à deux côtés, ce qui signifie que les deux côtés sont "réfléchissants" et peuvent faire rebondir un rayon de deux façons. Si un rayon laser frappe le laser ( ^v>< ) lui-même, il est traité comme un mur (faisceau laser détruit le beamer et donc, il ne serez jamais atteindre la cible).

cas D'essai

Input:
    ##########
    #   /   #
    #        #
    #      x#
    # >   /  #
    ########## 
Output:
    true

Input:
    ##########
    #   v x  #
    # /      #
    #       /#
    #       #
    ##########
Output:    
    false

Input:
    #############
    #     #     #
    # >   #     #
    #     #     #
    #     #   x #
    #     #     #
    #############
Output:
    false

Input:
    ##########
    #///  #
    #//\ #
    #////#
    #///x^#
    ##########
Output:
    true

le nombre de codes comprend les entrées/sorties (I. e programme complet).

152
demandé sur LiraNuna 2009-09-26 03:55:29

28 réponses

Perl, 166 160 caractères

Perl, 251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 --> 160 jars.

Solution a eu 166 coups à la fin de ce concours, mais A. Rex a trouvé quelques façons de raser 6 autres caractères:

s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;%d='>.^1<2v3'=~/./g;($r)=grep$d|=$d{$t{$_}},%t;
{$_=$t{$r+=(1,-99,-1,99)[$d^=3*/\/+m</>]};/[\/\ ]/&&redo}die/x/?true:false,$/

la première ligne charge l'entrée dans %t , une table de la carte où $t{99*i+j} tient le caractère à la ligne i , colonne j . Puis,

%d=split//,'>.^1<2v3' ; ($r)=grep{$d|=$d{$t{$_}}}%t

il recherche les éléments de %t pour un caractère qui correspond à > ^ < ou v , et place simultanément $d à une valeur entre 0 et 3 qui indique la direction initiale du faisceau laser.

Au début de chaque itération de la boucle principale, nous mettons à jour $d si le faisceau est actuellement sur un miroir. Utilise XOR par 3 donne le comportement correct pour un \ miroir et utilise XOR par 1 donne le comportement correct pour un miroir / .

$d^=3*/\/+m</>

ensuite, la position actuelle $r est mise à jour en fonction de la direction actuelle.

$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}

nous assignons le caractère à la position actuelle à $_ pour faire un usage commode des opérateurs de match.

/[\/\ ]/ && redo

continuer si nous sommes sur un espace vide ou un caractère miroir. Sinon nous terminons true si nous sommes sur la cible ( $_ =~ /x/ ) et false autrement.

Limitation: ne peut pas travailler sur des problèmes avec plus de 99 colonnes. Cette limitation pourrait être supprimée aux frais de 3 autres caractères,

78
répondu mob 2009-11-02 23:45:00

Perl, 177 Caractères

le premier linebreak peut être enlevé; les deux autres sont obligatoires.

$/=%d=split//,' >/^\v';$_=<>;$s='#';{
y/v<^/>v</?do{my$o;$o.=" 
"while s/.$/$o.=$&,""/meg;y'/\'\/'for$o,$s;$_=$o}:/>x/?die"true
":/>#/?die"false
":s/>(.)/$s$d{}/?$s=:1;redo}

explication:

$/ = %d = (' ' => '>', '/' => '^', '\' => 'v');

si un faisceau de déplacement droit pénètre dans un {espace vide, miroir à angle en haut, miroir à angle en bas}, il devient un {faisceau de déplacement droit, faisceau de déplacement en haut, faisceau de déplacement en bas}. Initialiser $/ en cours de route -- heureusement" 6 " n'est pas un char d'entrée valide.

$_ = <>;

Lire $_ sur le tableau .

$s="#";

$s est le symbole de ce que le faisceau est assis sur le dessus de maintenant. Puisque l'émetteur laser doit être traité comme un mur, définissez-le comme un mur pour commencer.

if (tr/v<^/>v</) {
  my $o;
  $o .= "\n" while s/.$/$o .= $&, ""/meg;
  tr,/\,\/, for $o, $s;
  $_ = $o;
}

si le faisceau laser pointe de quelque manière que ce soit, sauf vers la droite, tourner son symbole, puis tourner toute la carte en place (en tournant également les symboles pour les miroirs). C'est un 90 degrés à gauche rotation, effectuée efficacement en inversant les rangées tout en transposant les rangées et les colonnes, dans un s///e légèrement diabolique avec des effets secondaires. Dans le code golfé, le tr est écrit sous la forme y''' ce qui me permet de sauter un backslash.

die "true\n" if />x/; die "false\n" if />#/;

se termine par le bon message si nous touchons la cible ou un mur.

$s =  if s/>(.)/$s$d{}/;

s'il y a un espace vide devant le laser, avancez. Si il y a un miroir devant le laser, avancer et tourner le faisceau. Dans les deux cas, mettre le sauver "symbole" dans l'ancien faisceau emplacement, et de mettre la chose que nous juste écrasait sauvé symbole.

redo;

répéter jusqu'à la fin. {...;redo} est deux caractères de moins que for(;;){...} et trois de moins que while(1){...} .

75
répondu hobbs 2009-09-28 19:31:43

C89 (209 caractères)

#define M(a,b)*p==*#a?m=b,*p=1,q=p:
*q,G[999],*p=G;w;main(m){for(;(*++p=getchar())>0;)M(<,-1)M
(>,1)M(^,-w)M(v,w)!w&*p<11?w=p-G:0;for(;q+=m,m=*q&4?(*q&1?
-1:1)*(m/w?m/w:m*w):*q&9?!puts(*q&1?"false":"true"):m;);}

explication

Cette monstruosité sera probablement difficile à suivre si vous ne comprenez pas C. Juste un avertissement.

#define M(a,b)*p==*#a?m=b,*p=1,q=p:

cette petite macro vérifie si le caractère courant ( *p ) est égal à tout ce que a est sous forme de caractère ( *#a ). S'ils sont égaux, réglez le vecteur de mouvement sur b ( m=b ), marquez ceci caractère comme un mur ( *p=1 ), et placer le point de départ à l'emplacement actuel ( q=p ). Cette macro inclut la partie" else".

*q,G[999],*p=G;
w;

déclarez quelques variables. * q est l'emplacement actuel du feu. * G est le plateau de jeu comme un tableau 1D. * p est l'emplacement de lecture courant lorsque l'on entre G . * w est la largeur de la planche.

main(m){

évident main . m est une variable stockant le vecteur de mouvement. (C'est un paramètre de main comme une optimisation.)

    for(;(*++p=getchar())>0;)

boucle à travers tous les caractères, peuplant G en utilisant p . Sauter G[0] comme une optimisation (pas besoin de gaspiller un caractère écrit p à nouveau dans la troisième partie du for ).

        M(<,-1)
        M(>,1)
        M(^,-w)
        M(v,w)

utilisez la macro susmentionnée pour définir le lazer, si possible. -1 et 1 correspondent respectivement à gauche et à droite, et -w et w en haut et en bas.

        !w&*p<11
            ?w=p-G
            :0;

si le caractère courant est un marqueur de fin de ligne (ASCII 10), définissez la largeur si elle n'a pas déjà été définie. Le saut G[0] nous permet d'écrire w=p-G au lieu de w=p-G+1 . En outre, cela termine la chaîne ?: de la M 's.

    for(;
        q+=m,

Déplacer la lumière par le vecteur de mouvement.

        m=
        *q&4
            ?(*q&1?-1:1)*(
                m/w?m/w:m*w
            )

reflètent le vecteur du mouvement.

            :*q&9
                ?!puts(*q&1?"false":"true")
                :m
        ;

s'il s'agit d'un mur ou x , quitter avec le message approprié ( m=0 termine la boucle). Sinon, ne rien faire (noop; m=m )

    );
}
39
répondu strager 2009-09-28 22:56:23

je parierais que les gens l'attendaient depuis longtemps. (Comment ça, le défi est terminé et tout le monde s'en fout?)

Voici... Je présente ici une solution

Befunge-93!

il pèse à un whopping 973 charaters (ou 688 si vous êtes assez charitable pour ignorer whitespace, qui est seulement utilisé pour le formatage et ne fait rien dans code réel).

Caveat : j'ai écrit mon propre interprète Befunge-93 à Perl il y a peu de temps, et malheureusement c'est tout ce que j'ai eu le temps de tester. Je suis raisonnablement confiant dans son exactitude en général, mais il pourrait avoir une limite étrange en ce qui concerne EOF: puisque L'opérateur <> de Perl retourne undef à la fin du fichier, ceci est traité comme un 0 dans le contexte numérique. Pour les implémentations basées sur C où EOF a valeur différente (-1 dire), ce code pourrait ne pas fonctionner.

003pv   >~v>  #v_"a"43g-!#v_23g03p33v>v
>39#<*v   ::   >:52*-!v   >"rorrE",vg2*
######1   >^vp31+1g31$_03g13gp vv,,<15,
    a#3     >0v       vp30+1g30<>,,#3^@
######p $     0vg34"a"<   >       >vp
^<v>  > ^   p3<>-#v_:05g-!|>:15g-!| $
 >     v^     <   <   <   >^v-g52:< $ 
  v _  >52*"eslaf",,vv|-g53:_      v   
  : ^-"#">#:< #@,,,,<<>:43p0 v0 p34< 
  >">"-!vgv<  ^0p33g31p32-1g3<       
 ^     <#g1|-g34_v#-g34_v#-g34"><v^"<<<<
    v!<^<33>13g1v>03g1-v>03g1+03p$v  $$
>^  _#-v 1>g1-1v>+13pv >03p       v  pp
^_:"^"^#|^g30 <3#   $<           $<>^33
 ^!-"<":<>"v"v^># p#$<>            $^44
^      >#^#_ :" "-#v_ ^   >         ^gg
v  g34$<   ^!<v"/":< >p$^>05g43p$ ^55
 >,@   |!-"\"  :_g:">"-!|>      ^
 *v"x":<      >-^    ^4g52<>:"^" -#v_^
 5>-!#v_"ror"vv$p34g51:<>#|  !-"<":<#|
 ^2,,, ,,"er"<>v      #^^#<>05g43p$$^>^
      >52*"eurt",,,,,@>15g4 3p$$$$  ^#
>:"v"\:"<"\: "^"   -!#^_-!#^_-!      ^
               >                       ^

explication

si vous n'êtes pas familier avec la syntaxe et l'opération de Befunge, cochez ici .

Befunge est un langage basé sur la pile, mais il y a des commandes qui permettent d'écrire des caractères au code Befunge. J'en profite en deux endroits. Tout d'abord, j'ai copié toute la saisie sur le tableau de bord de Befunge, mais j'ai trouvé quelques les lignes sous le code écrit. (Bien sûr, ce n'est jamais réellement visible lorsque le code s'exécute.)

l'autre endroit est près de l'en haut à gauche:

######
    a#
######

dans ce cas, la zone que j'ai soulignée ci-dessus est celle où je stocke quelques coordonnées. La première colonne dans la rangée du milieu est là où je stocke la coordonnée x pour la "position du curseur"; la deuxième colonne est là où je stocke la coordonnée y; les deux colonnes suivantes sont pour stocker les coordonnées x et y de la source du faisceau laser lorsque celle - ci est trouvée; et la colonne finale (avec le caractère "a") est éventuellement réécrite pour contenir la direction du faisceau actuel, qui change évidemment lorsque la trajectoire du faisceau est tracée.

le programme commence par placer (0,27) comme position initiale du curseur. Puis l'entrée est lue un caractère à la fois et placée dans la position du curseur; newlines provoque simplement l'augmentation de la coordonnée y et la coordonnée x à retour à 0, comme un vrai retour en calèche. Finalement, UNEF est lu par l'interpréteur et cette valeur 0 caractère est utilisée pour signaler la fin de l'entrée et passer aux étapes de l'itération laser. Lorsque le caractère laser [<>^v] est lu, il est également copié dans le dépôt mémoire (au-dessus du caractère 'a') et ses coordonnées sont copiées dans les colonnes juste à gauche.

le résultat final de tout cela est que le fichier entier est essentiellement copié dans le code Befunge, un peu de chemins sous le code réellement traversé.

ensuite, l'emplacement du faisceau est recopié dans les emplacements du curseur, et l'itération suivante est effectuée:

  • vérifier la direction du faisceau actuel et incrémenter ou décrémenter les coordonnées du curseur de façon appropriée. (Je le fais d'abord pour éviter d'avoir à faire face au boîtier de coin du faisceau laser dès le premier mouvement.)
  • lire le caractère à ce emplacement.
  • si le caractère est "#", mettez newline et "false" sur la pile, imprimez et terminez.
  • comparez - le à tous les caractères de faisceau [<>^v]; s'il y a une correspondance, Imprimez aussi "false\N" et fin.
  • si le caractère est un espace, vider la pile et continuer.
  • si le caractère est une barre oblique vers l'avant, obtenez la direction du faisceau sur la pile et comparez-la à chacun des caractères de direction à tour de rôle. Lorsque une est trouvée, la nouvelle direction est stockée au même endroit dans le code et la boucle se répète.
  • si le caractère est un antislash, faire essentiellement la même chose que ci-dessus (sauf avec le mapping approprié pour antislash).
  • si le personnage est "x", nous avons atteint la cible. Print "vrai\n" et la sortie.
  • si le caractère n'est aucun de ceux-ci, imprimer" error\n " et quitter.

si la demande est suffisante pour cela, je vais essayer de montrer exactement où dans le code tout cela est accompli.

36
répondu Platinum Azure 2010-07-23 15:52:02

F#, 36 lignes, très lisible

Ok, juste pour obtenir une réponse:

let ReadInput() =
    let mutable line = System.Console.ReadLine()
    let X = line.Length 
    let mutable lines = []
    while line <> null do
        lines <- Seq.to_list line :: lines
        line <- System.Console.ReadLine()
    lines <- List.rev lines
    X, lines.Length, lines

let X,Y,a = ReadInput()
let mutable p = 0,0,'v'
for y in 0..Y-1 do
    for x in 0..X-1 do 
        printf "%c" a.[y].[x]
        match a.[y].[x] with 
        |'v'|'^'|'<'|'>' -> p <- x,y,a.[y].[x]
        |_ -> ()
    printfn ""

let NEXT = dict [ '>', (1,0,'^','v')
                  'v', (0,1,'<','>')
                  '<', (-1,0,'v','^')
                  '^', (0,-1,'>','<') ]
let next(x,y,d) =
    let dx, dy, s, b = NEXT.[d]
    x+dx,y+dy,(match a.[y+dy].[x+dx] with
               | '/' -> s
               | '\'-> b
               | '#'|'v'|'^'|'>'|'<' -> printfn "false"; exit 0
               | 'x' -> printfn "true"; exit 0
               | ' ' -> d)

while true do
    p <- next p    

Exemples:

##########
#   / \  #
#        #
#   \   x#
# >   /  #
##########
true

##########
#   v x  #
# /      #
#       /#
#   \    #
##########
false

#############
#     #     #
# >   #     #
#     #     #
#     #   x #
#     #     #
#############
false

##########
#/\/\/\  #
#\//\\ #
#//\/\/\#
#\/\/\/x^#
##########
true

##########
#   / \  #
#        #
#/    \ x#
#\>   /  #
##########
false

##########
#  /    \#
# / \    #
#/    \ x#
#\^/\ /  #
##########
false
29
répondu Brian 2009-09-26 01:26:42

Golfscript - 83 caractères (mashup de la mine et de strager)

Le retour à la ligne est juste ici pour l'habillage

:|'v^><'.{|?}%{)}?:$@=?{.[10|?).~)1-1]=$+
:$|=' \/x'?\[.^.1^'true''false']=.4/!}do

Golfscript-107 chars

Le retour à la ligne est juste là pour clarté

10\:@?):&4:$;{0'>^<v'$(:$=@?:*>}do;
{[1 0&--1&]$=*+:*;[{$}{3$^}{1$^}{"true "}{"false"}]@*=' \/x'?=~5\:$>}do$

Comment il fonctionne.

la première ligne indique l'emplacement et la direction initiaux.

Deuxième ligne il tourne à chaque fois que le laser frappe un miroir.

29
répondu gnibbler 2009-11-10 22:35:43

353 chars en rubis:

314 277 tout de suite!

OK, 256 caractères en rubis et maintenant c'est fini. Le bon moment pour arrêter. :)

247 caractères. Je ne peux pas arrêter.

223 203 201 chars en rubis

d=x=y=-1;b=readlines.each{|l|d<0&&(d="^>v<".index l[x]if x=l.index(/[>^v<]/)
y+=1)};loop{c=b[y+=[-1,0,1,0][d]][x+=[0,1,0,-1][d]]
c==47?d=[1,0,3,2][d]:c==92?d=3-d:c==35?(p !1;exit):c<?x?0:(p !!1;exit)}

avec espace blanc:

d = x = y = -1
b = readlines.each { |l|
  d < 0 && (d = "^>v<".index l[x] if x = l.index(/[>^v<]/); y += 1)
}

loop {
  c = b[y += [-1, 0, 1, 0][d]][x += [0, 1, 0, -1][d]]

  c == 47 ? d = [1, 0, 3, 2][d] :
  c == 92 ? d = 3 - d :
  c == 35 ? (p !1; exit) :
  c < ?x ? 0 : (p !!1; exit)
}

légèrement remanié:

board = readlines

direction = x = y = -1
board.each do |line|
  if direction < 0
    x = line.index(/[>^v<]/)
    if x
      direction = "^>v<".index line[x]
    end
    y += 1
  end
end

loop do
  x += [0, 1, 0, -1][direction]
  y += [-1, 0, 1, 0][direction]

  ch = board[y][x].chr
  case ch
  when "/"
    direction = [1, 0, 3, 2][direction]
  when "\"
    direction = 3 - direction
  when "x"
    puts "true"
    exit
  when "#"
    puts "false"
    exit
  end
end
18
répondu Jeremy Ruten 2009-09-28 03:20:59

Python

294 277 253 240 232 caractères, y compris les nouvelles lignes:

(le premier caractère des lignes 4 et 5 est un onglet, pas des espaces)

l='>v<^';x={'/':'^<v>','\':'v>^<',' ':l};b=[1];r=p=0
while b[-1]:
 b+=[raw_input()];r+=1
 for g in l:
    c=b[r].find(g)
    if-1<c:p=c+1j*r;d=g
while' '<d:z=l.find(d);p+=1j**z;c=b[int(p.imag)][int(p.real)];d=x.get(c,' '*4)[z]
print'#'<c

J'avais oublié que Python avait même des points-virgule optionnels.

Comment ça marche

l'idée clé derrière ce le code utilise des nombres complexes pour représenter les positions et les directions. Les lignes sont l'axe imaginaire, croissant vers le bas. Les colonnes sont l'axe réel, croissant vers la droite.

l='>v<^'; liste des symboles laser. L'ordre est choisi de façon à ce que l'indice d'un caractère de direction laser corresponde à une puissance de sqrt(-1)

x={'/':'^<v>','\':'v>^<',' ':l}; une table de transformation déterminant comment la direction change lorsque la poutre quitte différentes tuiles. La tuile est la clé, et les nouvelles directions sont les valeurs.

b=[1]; tient la planche. Le premier élément est 1 (évaluée comme true), de sorte que la boucle while sera exécuté au moins une fois.

r=p=0 r est le numéro de ligne courant de l'entrée, p est la position actuelle du faisceau laser.

while b[-1]: arrêter le chargement des données de la carte lorsque raw_input retourne une chaîne vide

b+=[raw_input()];r+=1 ajoute la ligne suivante d'entrée au tableau et incrémente le compteur de ligne

for g in l: devinez chaque direction laser tour à tour

c=b[r].find(g) réglez la colonne à l'emplacement du laser ou -1 si elle n'est pas dans la ligne (ou pointe dans une direction différente)

if-1<c:p=c+1j*r;d=g si nous avons trouvé un laser, alors réglez la position actuelle p et la direction d . d est l'un des caractères dans l

après avoir chargé la carte dans b , la position actuelle p et la direction d ont été réglées à celles de la source laser.

while' '<d: l'espace a une valeur ASCII inférieure à tous les symboles de direction, donc nous l'utilisons comme un drapeau d'arrêt.

z=l.find(d); indice de la direction actuelle de caractère dans le l de la chaîne. z est utilisé plus tard pour déterminer la nouvelle direction du faisceau en utilisant la table x , et pour augmenter la position.

p+=1j**z; incrémenter la position à l'aide d'un pouvoir de je. Par exemple, l.find('<')==2 -> i^2 = -1, ce qui permettrait à la gauche de la colonne.

c=b[int(p.imag)][int(p.real)]; lire l'étiquette à la position actuelle

d=x.get(c,' '*4)[z] cherchez la nouvelle direction du faisceau dans la table de transformation. Si le char n'existe pas dans la table, puis mettez d dans l'espace.

print'#'<c imprime false si on s'est arrêté sur autre chose que la cible.

17
répondu Theran 2009-10-27 07:19:57

ce est est un port direct de la solution de Brian vers C#3, moins les interactions avec la console. Ce n'est pas une entrée dans le challenge puisqu'il ne s'agit pas d'un programme complet, je me demandais juste comment certaines des F# constructions qu'il a utilisées pourraient être représentées dans C#.

bool Run(string input) {
    var a = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
    var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
             .First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));
    var NEXT = new[] {
            new {d = '>', dx = 1, dy = 0, s = '^', b = 'v'},
            new {d = 'v', dx = 0, dy = 1, s = '<', b = '>'},
            new {d = '<', dx = -1, dy = 0, s = 'v', b = '^'},
            new {d = '^', dx = 0, dy = -1, s = '>', b = '<'}
        }.ToDictionary(x => x.d);
    while (true) {
        var n = NEXT[p.d];
        int x = p.x + n.dx,
            y = p.y + n.dy;
        var d = a[y][x];
        switch (d) {
            case '/':  d = n.s; break;
            case '\': d = n.b; break;
            case ' ':  d = p.d; break;
            default: return d == 'x';
        }
        p = new {x, y, d};
    }
}

Edit: après quelques expérimentations, le code de recherche plutôt verbeux suivant:

int X = a[0].Length, Y = a.Length;
var p = new {x = 0, y = 0, d = 'v'};
for (var y = 0; y < Y; y++) {
    for (var x = 0; x < X; x++) {
        var d = a[y][x];
        switch (d) {
            case 'v': case '^': case '<': case '>':
                p = new {x, y, d}; break;
        }
    }
}

a été remplacé par un LINQ beaucoup plus compact pour les objets code:

var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
         .First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));
16
répondu Nathan Baulch 2009-09-26 04:41:37

F#, 255 caractères (et encore assez lisible!):

Ok, après une nuit de repos, j'ai beaucoup amélioré cela:

let a=System.Console.In.ReadToEnd()
let w,c=a.IndexOf"\n"+1,a.IndexOfAny[|'^';'<';'>';'v'|]
let rec n(c,d)=
 let e,s=[|-w,2;-1,3;1,0;w,1|].[d]
 n(c+e,match a.[c+e]with|'/'->s|'\'->3-s|' '->d|c->printfn"%A"(c='x');exit 0)
n(c,"^<>v".IndexOf a.[c])

parlons-en ligne par ligne.

tout d'abord, slurp toutes les entrées dans un grand tableau unidimensionnel (tableaux 2D peut être mauvais pour le golf de code; il suffit d'utiliser un tableau 1D et ajouter/soustraire la largeur d'une ligne à l'index pour se déplacer vers le haut/vers le bas d'une ligne).

Ensuite, nous calculons 'w', la largeur d'un ligne d'entrée, et 'c', la position de départ, en indexant dans notre tableau.

maintenant, définissons la fonction' next 'N', qui prend une position courante 'c' et une direction 'd' qui est 0,1,2,3 vers le haut,gauche,droite,vers le bas.

L'indice-epsilon " e " et le nouveau-direction-si-on-hit-a-slash 's' sont calculées par un tableau. Par exemple, si la direction actuelle 'd' est 0 (up), alors le premier élément du tableau dit "- w, 2" ce qui signifie que nous décrémentons l'indice par w, et si nous frappons une barre oblique la nouvelle direction est 2 (à droite).

maintenant nous revenons dans la fonction suivante 'n' Avec (1) le prochain indice ("C+e" - courant plus epsilon), et (2) la nouvelle direction, que nous calculons en regardant à l'avance pour voir ce qui est dans le tableau dans cette cellule suivante. Si le lookahead char est un slash, la nouvelle direction est 's'. Si c'est un antislash, la nouvelle direction est 3-s (notre choix d'encodage 0123 fait ce travail). Si c'est un espace, on peut continuer dans la même direction "d". Et si c'est un autre caractère 'c', alors le jeu se termine, en imprimant 'true' si le caractère était 'x' et false autrement.

pour lancer les choses, nous appelons la fonction récursive 'n' avec la position initiale 'c' et la direction de départ (qui fait le codage initial de direction dans 0123).

je pense que je peux probablement encore raser un peu plus de personnages, mais je suis assez satisfait de cette façon (et 255 est un bon nombre).

16
répondu Brian 2009-09-26 18:55:22

la pesée à 18203 caractères est une solution Python qui peut:

  • la 'salle'
  • calculez la trajectoire quand il n'y a pas de "chambre" sur la base des limitations 2D (le spec dit beaucoup sur ce qui doit être dans la "chambre" mais pas si la chambre doit exister)
  • rapport d'erreurs

Il doit encore rangé un peu et je ne sais pas si 2D la physique veut que le faisceau ne se croise pas...

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
The shortest code by character count to input a 2D representation of a board, 
and output 'true' or 'false' according to the input.

The board is made out of 4 types of tiles:

# - A solid wall
x - The target the laser has to hit
/ or \ - Mirrors pointing to a direction (depends on laser direction)
v, ^, > or < - The laser pointing to a direction (down, up, right and left
respectively)

There is only one laser and only one target. Walls must form a solid rectangle 
of any size, where the laser and target are placed inside. Walls inside the
'room' are possible.

Laser ray shots and travels from it's origin to the direction it's pointing. If
a laser ray hits the wall, it stops. If a laser ray hits a mirror, it is bounces
90 degrees to the direction the mirror points to. Mirrors are two sided, meaning
both sides are 'reflective' and may bounce a ray in two ways. If a laser ray
hits the laser (^v><) itself, it is treated as a wall (laser beam destroys the
beamer and so it'll never hit the target).
"""



SOLID_WALL, TARGET, MIRROR_NE_SW, MIRROR_NW_SE, LASER_DOWN, LASER_UP, \
LASER_RIGHT, LASER_LEFT = range(8)

MIRRORS = (MIRROR_NE_SW, MIRROR_NW_SE)

LASERS = (LASER_DOWN, LASER_UP, LASER_RIGHT, LASER_LEFT)

DOWN, UP, RIGHT, LEFT = range(4)

LASER_DIRECTIONS = {
    LASER_DOWN : DOWN,
    LASER_UP   : UP,
    LASER_RIGHT: RIGHT,
    LASER_LEFT : LEFT
}

ROW, COLUMN = range(2)

RELATIVE_POSITIONS = {
    DOWN : (ROW,     1),
    UP   : (ROW,    -1),
    RIGHT: (COLUMN,  1),
    LEFT : (COLUMN, -1)
}

TILES = {"#" : SOLID_WALL,
         "x" : TARGET,
         "/" : MIRROR_NE_SW,
         "\": MIRROR_NW_SE,
         "v" : LASER_DOWN,
         "^" : LASER_UP,
         ">" : LASER_RIGHT,
         "<" : LASER_LEFT}

REFLECTIONS = {MIRROR_NE_SW: {DOWN : LEFT,
                              UP   : RIGHT,
                              RIGHT: UP,
                              LEFT : DOWN},
               MIRROR_NW_SE: {DOWN : RIGHT,
                              UP   : LEFT,
                              RIGHT: DOWN,
                              LEFT : UP}}



def does_laser_hit_target(tiles):
    """
        Follows a lasers trajectory around a grid of tiles determining if it
        will reach the target.

        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    #Obtain the position of the laser
    laser_pos = get_laser_pos(tiles)

    #Retrieve the laser's tile
    laser = get_tile(tiles, laser_pos)

    #Create an editable starting point for the beam
    beam_pos = list(laser_pos)

    #Create an editable direction for the beam
    beam_dir = LASER_DIRECTIONS[laser]

    #Cache the number of rows
    number_of_rows = len(tiles)

    #Keep on looping until an ultimate conclusion
    while True:

        #Discover the axis and offset the beam is travelling to
        axis, offset = RELATIVE_POSITIONS[beam_dir]

        #Modify the beam's position
        beam_pos[axis] += offset

        #Allow for a wrap around in this 2D scenario
        try:

            #Get the beam's new tile
            tile = get_tile(tiles, beam_pos)

        #Perform wrapping
        except IndexError:

            #Obtain the row position
            row_pos = beam_pos[ROW]

            #Handle vertical wrapping
            if axis == ROW:

                #Handle going off the top
                if row_pos == -1:

                    #Move beam to the bottom
                    beam_pos[ROW] = number_of_rows - 1

                #Handle going off the bottom
                elif row_pos == number_of_rows:

                    #Move beam to the top
                    beam_pos[ROW] = 0

            #Handle horizontal wrapping
            elif axis == COLUMN:

                #Obtain the row
                row = tiles[row_pos]

                #Calculate the number of columns
                number_of_cols = len(row)

                #Obtain the column position
                col_pos = beam_pos[COLUMN]

                #Handle going off the left hand side
                if col_pos == -1:

                    #Move beam to the right hand side
                    beam_pos[COLUMN] = number_of_cols - 1

                #Handle going off the right hand side
                elif col_pos == number_of_cols:

                    #Move beam to the left hand side
                    beam_pos[COLUMN] = 0

            #Get the beam's new tile
            tile = get_tile(tiles, beam_pos)

        #Handle hitting a wall or the laser
        if tile in LASERS \
        or tile == SOLID_WALL:
            return False

        #Handle hitting the target
        if tile == TARGET:
            return True

        #Handle hitting a mirror
        if tile in MIRRORS:
            beam_dir = reflect(tile, beam_dir)

def get_laser_pos(tiles):
    """
        Returns the current laser position or an exception.

        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    #Calculate the number of rows
    number_of_rows = len(tiles)

    #Loop through each row by index
    for row_pos in range(number_of_rows):

        #Obtain the current row
        row = tiles[row_pos]

        #Calculate the number of columns
        number_of_cols = len(row)

        #Loop through each column by index
        for col_pos in range(number_of_cols):

            #Obtain the current column
            tile = row[col_pos]

            #Handle finding a laser
            if tile in LASERS:

                #Return the laser's position
                return row_pos, col_pos

def get_tile(tiles, pos):
    """
        Retrieves a tile at the position specified.

        Keyword arguments:
        pos --- a row/column position of the tile
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    #Obtain the row position
    row_pos = pos[ROW]

    #Obtain the column position
    col_pos = pos[COLUMN]

    #Obtain the row
    row = tiles[row_pos]

    #Obtain the tile
    tile = row[col_pos]

    #Return the tile
    return tile

def get_wall_pos(tiles, reverse=False):
    """
        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
        reverse --- whether to search in reverse order or not (defaults to no)
    """

    number_of_rows = len(tiles)

    row_iter = range(number_of_rows)

    if reverse:
        row_iter = reversed(row_iter)

    for row_pos in row_iter:
        row = tiles[row_pos]

        number_of_cols = len(row)

        col_iter = range(number_of_cols)

        if reverse:
            col_iter = reversed(col_iter)

        for col_pos in col_iter:
            tile = row[col_pos]

            if tile == SOLID_WALL:
                pos = row_pos, col_pos

                if reverse:
                    offset = -1
                else:
                    offset = 1

                for axis in ROW, COLUMN:
                    next_pos = list(pos)

                    next_pos[axis] += offset

                    try:
                        next_tile = get_tile(tiles, next_pos)
                    except IndexError:
                        next_tile = None

                    if next_tile != SOLID_WALL:
                        raise WallOutsideRoomError(row_pos, col_pos)

                return pos

def identify_tile(tile):
    """
        Returns a symbolic value for every identified tile or None.

        Keyword arguments:
        tile --- the tile to identify
    """

    #Safely lookup the tile
    try:

        #Return known tiles
        return TILES[tile]

    #Handle unknown tiles
    except KeyError:

        #Return a default value
        return

def main():
    """
        Takes a board from STDIN and either returns a result to STDOUT or an
        error to STDERR.

        Called when this file is run on the command line.
    """

    #As this function is the only one to use this module, and it can only be
    #called once in this configuration, it makes sense to only import it here.
    import sys

    #Reads the board from standard input.
    board = sys.stdin.read()

    #Safely handles outside input
    try:

        #Calculates the result of shooting the laser
        result = shoot_laser(board)

    #Handles multiple item errors
    except (MultipleLaserError, MultipleTargetError) as error:

        #Display the error
        sys.stderr.write("%s\n" % str(error))

        #Loop through all the duplicated item symbols
        for symbol in error.symbols:

            #Highlight each symbol in green
            board = board.replace(symbol, "3[01;31m%s3[m" % symbol)

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #Handles item missing errors
    except (NoLaserError, NoTargetError) as error:

        #Display the error
        sys.stderr.write("%s\n" % str(error))

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #Handles errors caused by symbols
    except (OutsideRoomError, WallNotRectangleError) as error:

        #Displays the error
        sys.stderr.write("%s\n" % str(error))

        lines = board.split("\n")

        line = lines[error.row_pos]

        before = line[:error.col_pos]

        after = line[error.col_pos + 1:]

        symbol = line[error.col_pos]

        line = "%s3[01;31m%s3[m%s" % (before, symbol, after)

        lines[error.row_pos] = line

        board = "\n".join(lines)

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #Handles errors caused by non-solid walls
    except WallNotSolidError as error:

        #Displays the error
        sys.stderr.write("%s\n" % str(error))

        lines = board.split("\n")

        line = lines[error.row_pos]

        before = line[:error.col_pos]

        after = line[error.col_pos + 1:]

        symbol = line[error.col_pos]

        line = "%s3[01;5;31m#3[m%s" % (before, after)

        lines[error.row_pos] = line

        board = "\n".join(lines)

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #If a result was returned
    else:

        #Converts the result into a string
        result_str = str(result)

        #Makes the string lowercase
        lower_result = result_str.lower()

        #Returns the result
        sys.stdout.write("%s\n" % lower_result)

def parse_board(board):
    """
        Interprets the raw board syntax and returns a grid of tiles.

        Keyword arguments:
        board --- the board containing the tiles (walls, laser, target, etc)
    """

    #Create a container for all the lines
    tiles = list()

    #Loop through all the lines of the board
    for line in board.split("\n"):

        #Identify all the tiles on the line 
        row = [identify_tile(tile) for tile in line]

        #Add the row to the container
        tiles.append(row)

    #Return the container
    return tiles

def reflect(mirror, direction):
    """
        Returns an updated laser direction after it has been reflected on a
        mirror.

        Keyword arguments:
        mirror --- the mirror to reflect the laser from
        direction --- the direction the laser is travelling in
    """

    try:
        direction_lookup = REFLECTIONS[mirror]
    except KeyError:
        raise TypeError("%s is not a mirror.", mirror)

    try:
        return direction_lookup[direction]
    except KeyError:
        raise TypeError("%s is not a direction.", direction)

def shoot_laser(board):
    """
        Shoots the boards laser and returns whether it will hit the target.

        Keyword arguments:
        board --- the board containing the tiles (walls, laser, target, etc)
    """

    tiles = parse_board(board)

    validate_board(tiles)

    return does_laser_hit_target(tiles)

def validate_board(tiles):
    """
        Checks an board to see if it is valid and raises an exception if not.

        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    found_laser = False
    found_target = False

    try:
        n_wall, w_wall = get_wall_pos(tiles)
        s_wall, e_wall = get_wall_pos(tiles, reverse=True)
    except TypeError:
        n_wall = e_wall = s_wall = w_wall = None

    number_of_rows = len(tiles)

    for row_pos in range(number_of_rows):
        row = tiles[row_pos]

        number_of_cols = len(row)

        for col_pos in range(number_of_cols):

            tile = row[col_pos]

            if ((row_pos in (n_wall, s_wall) and
                 col_pos in range(w_wall, e_wall))
                or
                (col_pos in (e_wall, w_wall) and
                 row_pos in range(n_wall, s_wall))):
                if tile != SOLID_WALL:
                    raise WallNotSolidError(row_pos, col_pos)
            elif (n_wall != None and
                  (row_pos < n_wall or
                   col_pos > e_wall or
                   row_pos > s_wall or
                   col_pos < w_wall)):

                if tile in LASERS:
                    raise LaserOutsideRoomError(row_pos, col_pos)
                elif tile == TARGET:
                    raise TargetOutsideRoomError(row_pos, col_pos)
                elif tile == SOLID_WALL:
                    if not (row_pos >= n_wall and
                            col_pos <= e_wall and
                            row_pos <= s_wall and
                            col_pos >= w_wall):
                        raise WallOutsideRoomError(row_pos, col_pos)
            else:
                if tile in LASERS:
                    if not found_laser:
                        found_laser = True
                    else:
                        raise MultipleLaserError(row_pos, col_pos)
                elif tile == TARGET:
                    if not found_target:
                        found_target = True
                    else:
                        raise MultipleTargetError(row_pos, col_pos)

    if not found_laser:
        raise NoLaserError(tiles)

    if not found_target:
        raise NoTargetError(tiles)



class LasersError(Exception):
    """Parent Error Class for all errors raised."""

    pass

class NoLaserError(LasersError):
    """Indicates that there are no lasers on the board."""

    symbols = "^v><"

    def __str__ (self):
        return "No laser (%s) to fire." % ", ".join(self.symbols)

class NoTargetError(LasersError):
    """Indicates that there are no targets on the board."""

    symbols = "x"

    def __str__ (self):
        return "No target (%s) to hit." % ", ".join(self.symbols)

class MultipleLaserError(LasersError):
    """Indicates that there is more than one laser on the board."""

    symbols = "^v><"

    def __str__ (self):
        return "Too many lasers (%s) to fire, only one is allowed." % \
               ", ".join(self.symbols)

class MultipleTargetError(LasersError):
    """Indicates that there is more than one target on the board."""

    symbols = "x"

    def __str__ (self):
        return "Too many targets (%s) to hit, only one is allowed." % \
               ", ".join(self.symbols)

class WallNotSolidError(LasersError):
    """Indicates that the perimeter wall is not solid."""

    __slots__ = ("__row_pos", "__col_pos", "n_wall", "s_wall", "e_wall",
                 "w_wall")

    def __init__(self, row_pos, col_pos):
        self.__row_pos = row_pos
        self.__col_pos = col_pos

    def __str__ (self):
        return "Walls must form a solid rectangle."

    def __get_row_pos(self):
        return self.__row_pos

    def __get_col_pos(self):
        return self.__col_pos

    row_pos = property(__get_row_pos)
    col_pos = property(__get_col_pos)

class WallNotRectangleError(LasersError):
    """Indicates that the perimeter wall is not a rectangle."""

    __slots__ = ("__row_pos", "__col_pos")

    def __init__(self, row_pos, col_pos):
        self.__row_pos = row_pos
        self.__col_pos = col_pos

    def __str__ (self):
        return "Walls must form a rectangle."

    def __get_row_pos(self):
        return self.__row_pos

    def __get_col_pos(self):
        return self.__col_pos

    row_pos = property(__get_row_pos)
    col_pos = property(__get_col_pos)

class OutsideRoomError(LasersError):
    """Indicates an item is outside of the perimeter wall."""

    __slots__ = ("__row_pos", "__col_pos", "__name")

    def __init__(self, row_pos, col_pos, name):
        self.__row_pos = row_pos
        self.__col_pos = col_pos
        self.__name = name

    def __str__ (self):
        return "A %s was found outside of a 'room'." % self.__name

    def __get_row_pos(self):
        return self.__row_pos

    def __get_col_pos(self):
        return self.__col_pos

    row_pos = property(__get_row_pos)
    col_pos = property(__get_col_pos)

class LaserOutsideRoomError(OutsideRoomError):
    """Indicates the laser is outside of the perimeter wall."""

    def __init__ (self, row_pos, col_pos):
        OutsideRoomError.__init__(self, row_pos, col_pos, "laser")

class TargetOutsideRoomError(OutsideRoomError):
    """Indicates the target is outside of the perimeter wall."""

    def __init__ (self, row_pos, col_pos):
        OutsideRoomError.__init__(self, row_pos, col_pos, "target")

class WallOutsideRoomError(OutsideRoomError):
    """Indicates that there is a wall outside of the perimeter wall."""

    def __init__ (self, row_pos, col_pos):
        OutsideRoomError.__init__(self, row_pos, col_pos, "wall")



if __name__ == "__main__":
    main()

Un script bash pour montrer la couleur de rapport d'erreur:

#!/bin/bash

declare -a TESTS

test() {
    echo -e "3[1m3[0m"
    tput sgr0
    echo "" | ./lasers.py
    echo
}

test \
"no laser" \
"    ##########
    #     x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"multiple lasers" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \  ^ #
    ##########"

test \
"no target" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"multiple targets" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \  x #
    ##########"

test \
"wall not solid" \
"    ##### ####
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"laser_outside_room" \
"    ##########
 >  #     x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"laser before room" \
" >  ##########
    #     x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"laser row before room" \
"   >
    ##########
    #     x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"laser after room" \
"    ##########
    #     x  #
    # /      #
    #       /#
    #   \    #
    ##########  >"

test \
"laser row after room" \
"    ##########
    #     x  #
    # /      #
    #       /#
    #   \    #
    ##########
  > "

test \
"target outside room" \
"    ##########
 x  #   v    #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"target before room" \
" x  ##########
    #   v    #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"target row before room" \
"   x
    ##########
    #   v    #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"target after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \    #
    ##########   x"

test \
"target row after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \    #
    ##########
  x "

test \
"wall outside room" \
"    ##########
 #  #   v    #
    # /      #
    #       /#
    #   \  x #
    ##########"

test \
"wall before room" \
" #  ##########
    #   v    #
    # /      #
    #       /#
    #   \  x #
    ##########"

test \
"wall row before room" \
"    #
    ##########
    #   v    #
    # /      #
    #       /#
    #   \  x #
    ##########"

test \
"wall after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \  x #
    ########## #"

test \
"wall row after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \  x #
    ##########
  #"

test \
"mirror outside room positive" \
"    ##########
 /  #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## "

test \
"mirrors outside room negative" \
"    ##########
 \  #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"mirror before room positive" \
" \  ##########
    #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## "

test \
"mirrors before room negative" \
" /  ##########
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"mirror row before room positive" \
"     \
    ##########
    #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## "

test \
"mirrors row before room negative" \
"     \
    ##########
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"mirror after row positive" \
"    ##########
    #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## /  "

test \
"mirrors after row negative" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########   /  "

test \
"mirror row after row positive" \
"    ##########
    #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## 
 /  "

test \
"mirrors row after row negative" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ########## 
 /  "

test \
"laser hitting laser" \
"    ##########
    #   v   \#
    #        #
    #        #
    #x  \   /#
    ##########"

test \
"mirrors positive" \
"    ##########
    #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## "

test \
"mirrors negative" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########"

test \
"wall collision" \
"    #############
    #     #     #
    # >   #     #
    #     #     #
    #     #   x #
    #     #     #
    #############"

test \
"extreme example" \
"    ##########
    #/\/\/\  #
    #\\//\\\ #
    #//\/\/\\#
    #\/\/\/x^#
    ##########"

test \
"brian example 1" \
"##########
#   / \  #
#        #
#/    \ x#
#\>   /  #
##########"

test \
"brian example 2" \
"##########
#  /    \#
# / \    #
#/    \ x#
#\^/\ /  #
##########"

La unittests utilisés pour le développement:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest

from lasers import *

class TestTileRecognition(unittest.TestCase):
    def test_solid_wall(self):
        self.assertEqual(SOLID_WALL, identify_tile("#"))

    def test_target(self):
        self.assertEqual(TARGET, identify_tile("x"))

    def test_mirror_ne_sw(self):
        self.assertEqual(MIRROR_NE_SW, identify_tile("/"))

    def test_mirror_nw_se(self):
        self.assertEqual(MIRROR_NW_SE, identify_tile("\"))

    def test_laser_down(self):
        self.assertEqual(LASER_DOWN, identify_tile("v"))

    def test_laser_up(self):
        self.assertEqual(LASER_UP, identify_tile("^"))

    def test_laser_right(self):
        self.assertEqual(LASER_RIGHT, identify_tile(">"))

    def test_laser_left(self):
        self.assertEqual(LASER_LEFT, identify_tile("<"))

    def test_other(self):
        self.assertEqual(None, identify_tile(" "))

class TestReflection(unittest.TestCase):
    def setUp(self):
        self.DIRECTION = LEFT
        self.NOT_DIRECTIO
11
répondu Metalshark 2009-09-27 20:17:48

Ruby, 176 caractères

x=!0;y=0;e="^v<>#x";b=readlines;b.map{|l|(x||=l=~/[v^<>]/)||y+=1};c=e.index(b[y][x])
loop{c<2&&y+=c*2-1;c>1&&x+=2*c-5;e.index(n=b[y][x])&&(p n==?x;exit);c^='  \/'.index(n)||0}

j'ai utilisé une machine d'état simple (comme la plupart des affiches), rien de fantaisie. J'ai juste continué à le tailler en utilisant tous les trucs que j'ai pu imaginer. Le bitwise XOR utilisé pour changer de direction (stocké comme un entier dans la variable c ) était une grande amélioration par rapport aux conditionnels que j'avais dans les versions précédentes.

je soupçonne que le code qui augmente x et y pourrait être faites plus court. Voici la section du code qui fait l'incrémentation:

c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1

Edit : j'ai pu raccourcir légèrement le ci-dessus:

c<2&&y+=c*2-1;c>1&&x+=2*c-5

la direction du courant du laser c est stockée comme suit:

0 => up
1 => down
2 => left
3 => right

le code s'appuie sur ce fait pour augmenter x et y par le montant correct (0, 1, ou -1). J'ai essayé de réorganiser les nombres qui carte pour chaque direction, à la recherche d'un arrangement qui permettrait permettez-moi de faire quelques bit à bit de manipulation pour incrémenter les valeurs, parce que j'ai un sentiment tenace qu'il serait plus courte que l'arithmétique version.

11
répondu Mike Spross 2009-09-29 06:33:15

C# 3.0

259 caractères

bool S(char[]m){var w=Array.FindIndex(m,x=>x<11)+1;var s=Array.FindIndex(m,x=>x>50&x!=92&x<119);var t=m[s];var d=t<61?-1:t<63?1:t<95?-w:w;var u=0;while(0<1){s+=d;u=m[s];if(u>119)return 0<1;if(u==47|u==92)d+=d>0?-w-1:w+1;else if(u!=32)return 0>1;d=u>47?-d:d;}}

légèrement plus lisible:

bool Simulate(char[] m)
{
    var w = Array.FindIndex(m, x => x < 11) + 1;
    var s = Array.FindIndex(m, x => x > 50 & x != 92 & x < 119);
    var t = m[s];
    var d = t < 61 ? -1 : t < 63 ? 1 : t < 95 ? -w : w;
    var u = 0;
    while (0 < 1)
    {
        s += d;
        u = m[s];
        if (u > 119)
            return 0 < 1;
        if (u == 47 | u == 92)
            d += d > 0 ? -w - 1 : w + 1;
        else if (u != 32)
            return 0 > 1;
        d = u > 47 ? -d : d;
    }
}

le principal gaspillage des chars semble être de trouver la largeur de la carte et la position de la source laser. Une idée pour raccourcir ça?

9
répondu Noldorin 2009-09-26 13:30:29

C + ASCII, 197 caractères:

G[999],*p=G,w,z,t,*b;main(){for(;(*p++=t=getchar()^32)>=0;w=w|t-42?w:p-G)z=t^86?t^126?t^28?t^30?z:55:68:56:75,b=z?b:p;for(;t=z^55?z^68?z^56?z^75?0:w:-w:-1:1;z^=*b)b+=t;puts(*b^88?"false":"true");}

cette solution c suppose un jeu de caractères ASCII, ce qui nous permet d'utiliser L'astuce du miroir XOR. C'est aussi incroyablement fragile - toutes les lignes d'entrée doivent être de la même longueur, par exemple.

il casse sous la marque de 200 caractères - mais bon sang, toujours pas battu ces solutions Perl!

9
répondu caf 2009-09-29 09:48:38

Golfscript (83 caractères)

Bonjour, gnibbler!

:\'><v^'.{\?}%{)}?:P@=?{:O[1-1?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do
9
répondu strager 2009-11-10 22:14:35

Python-152

lit entrée à partir d'un fichier appelé" L "

A=open("L").read()
W=A.find('\n')+1
D=P=-1
while P<0:D+=1;P=A.find(">^<v"[D])
while D<4:P+=[1,-W,-1,W][D];D=[D,D^3,D^1,4,5][' \/x'.find(A[P])]
print D<5

remplacer la première ligne par

."
import os;A=os.read(0,1e9)

si vous avez besoin de true/false en minuscules, changez la dernière ligne en

print`D<5`.lower()
9
répondu gnibbler 2010-02-12 02:54:50

JavaScript-265 Caractères

Update IV - les chances sont que ce sera la dernière série de mises à jour, géré pour sauver un couple plus de caractères en passant à une boucle do-while et réécrire l'équation de mouvement.

Update III - merci à la suggestion de strager en ce qui concerne la suppression des mathématiques.abs() et en mettant les variables dans l'espace de nom global, cela couplé avec un certain réarrangement des assignations de variables a réduit le code à 282 caractères.

Update II - D'autres mises à jour du code pour supprimer l'utilisation de != -1 ainsi qu'une meilleure utilisation de variables pour plus d'opérations.

Update - quand à travers et a fait quelques changements en créant une référence à l'indexOf fonction (merci LiraNuna!) et la suppression de la parenthèse qui n'étaient pas nécessaires.

c'est la première fois que je fais un golf en code, donc je ne suis pas sûr que ce soit mieux, tout Feedback est apprécié.

version entièrement minimisée:

a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("\n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="\"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}

version originale avec commentaires:

character; length; loc; movement; temp;
function checkMaze(maze) {
        // Use a shorter indexOf function
        character = function(string) { return maze.indexOf(string); }
        // Get the length of the maze
        length = character("\n") + 1;
        // Get the location of the laser in the string
        character = maze[loc = temp = character("v") > 0 ? temp :
                               temp = character("^") > 0 ? temp :
                               temp = character("<") > 0 ? temp : character(">")];
        // Get the intial direction that we should travel
        movement = character == "<" ? -1 :
                   character == ">" ? 1 :
                   character == "^" ? -length : length;
        // Move along until we reach the end
        do {
            // Get the current character
            temp = movement == -1 | movement == 1;
            character = maze[loc += movement = character == "\" ? temp ? length * movement : movement > 0 ? 1 : -1 :
                                               character == "/" ? temp ? -length * movement : movement > 0 ? 1 : -1 : movement];                                   
            // Have we hit a target?
            temp = character == "x";
            // Have we hit a wall?
        } while (character != "#" ^ temp);
        // temp will be false if we hit the target
        return temp;
    }

page Web à tester avec:

<html>
  <head>
    <title>Code Golf - Lasers</title>
    <script type="text/javascript">
    a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("\n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="\"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}
    </script>
  </head>
  <body>
    <textarea id="maze" rows="10" cols="10"></textarea>
    <button id="checkMaze" onclick="alert(f(document.getElementById('maze').value))">Maze</button>
  </body>
</html>
7
répondu Rob Z 2009-09-29 19:38:05

Maison des Miroirs

pas une entrée réelle au défi, mais j'ai écrit un jeu basé sur ce concept (pas trop longtemps en arrière).

C'est écrit en Scala, open-source et disponible ici :

il fait un peu plus; traite des couleurs et divers types de miroirs et appareils, mais la version 0.00001 a fait exactement ce que ce défi demande. J'ai perdu cette version et il n'a jamais été optimisé pour le compte de caractère de toute façon.

6
répondu HRJ 2009-09-26 07:28:54

c (K&R) 339 caractères nécessaires à la recherche de suggestions de strager.

le physicien en moi a noté que les opérations de propagation et de réflexion sont invariantes d'inversion de temps, donc cette version, jette des rayons de la cible et vérifie si l'arrivée à l'émetteur laser.

le reste de la mise en œuvre est très simple et est repris plus ou moins exactement de mon précédent, aller de l'avant.

comprimé:

#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;s(d,e,Z){for(;;)switch(m[x+=d][y+=e]){C'^':R 1==e;
C'>':R-1==d;C'v':R-1==e;C'<':R 1==d;C'#':C'x':R 0;C 92:e=-e;d=-d;C'/':c=d;
d=-e;e=-c;}}main(){while((c=getchar())>0)c==10?i=0,j++:(c==120?x=i,y=j:
i,m[i++][j]=c);puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");}

non compressé(ish):

#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;
s(d,e,Z)
{
  for(;;)
    switch(m[x+=d][y+=e]){
    C'^': 
      R 1==e;
    C'>': 
      R-1==d;
    C'v': 
      R-1==e;
    C'<': 
      R 1==d;
    C'#':
    C'x':
      R 0;
    C 92:
      e=-e;
      d=-d;
    C'/':
      c=d;
      d=-e;
      e=-c;
    }
}
main(){
  while((c=getchar())>0)
    c==10?i=0,j++:
      (c==120?x=i,y=j:i,m[i++][j]=c);
  puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");
}

il n'y a pas de validation d'entrée, et une mauvaise entrée peut l'envoyer dans une boucle infinie. Fonctionne correctement avec une entrée ne dépassant pas 99 par 99. Nécessite un compilateur qui reliera la bibliothèque standard sans inclure aucun des en-têtes. Et je pense que je suis fait, strager m'a battu par un effort considérable, même avec son aide.

j'espère plutôt que quelqu'un montrera une façon plus subtile d'accomplir la tâche. Il n'y a rien de mal à cela, mais ce n'est pas de la magie profonde.

6
répondu dmckee 2017-05-23 12:32:56

Ruby-146 Chars

A=$<.read
W=A.index('
')+1
until
q=A.index(">^<v"[d=d ?d+1:0])
end
while d<4
d=[d,d^3,d^1,4,5][(' \/x'.index(A[q+=[1,-W,-1,W][d]])or 4)]
end
p 5>d
6
répondu John La Rooy 2009-11-09 05:02:56

PostScript , 359 octets

premier essai, beaucoup de place pour l'amélioration...

/a[{(%stdin)(r)file 99 string readline not{exit}if}loop]def a{{[(^)(>)(<)(v)]{2
copy search{stop}if pop pop}forall}forall}stopped/r count 7 sub def pop
length/c exch def[(>)0(^)1(<)2(v)3>>exch get/d exch def{/r r[0 -1 0 1]d get
add def/c c[1 0 -1 0]d get add def[32 0 47 1 92 3>>a r get c get .knownget
not{exit}if/d exch d xor def}loop a r get c get 120 eq =
5
répondu KirarinSnow 2009-11-10 23:20:12

Haskell, 395 391 383 361 339 caractères (optimisés)

utilise encore une machine d'état générique, plutôt que quelque chose d'intelligent:

k="<>^v"
o(Just x)=x
s y(h:t)=case b of{[]->s(y+1)t;(c:_)->(c,length a,y)}where(a,b)=break(flip elem k)h
r a = f$s 0 a where f(c,x,y)=case i(a!!v!!u)"x /\"["true",g k,g"v^><",g"^v<>"]of{Just r->r;_->"false"}where{i x y=lookup x.zip y;j=o.i c k;u=j[x-1,x+1,x,x];v=j[y,y,y-1,y+1];g t=f(j t,u,v)}
main=do{z<-getContents;putStrLn$r$lines z}

Une version lisible:

k="<>^v"    -- "key" for direction
o(Just x)=x -- "only" handle successful search
s y(h:t)=case b of  -- find "start" state
  []->s(y+1)t
  (c:_)->(c,length a,y)
 where (a,b)=break(flip elem k)h
r a = f$s 0 a where -- "run" the state machine (iterate with f)
 f(c,x,y)=case i(a!!v!!u)"x /\"["true",g k,g"v^><",g"^v<>"] of
   Just r->r
   _->"false"
  where
   i x y=lookup x.zip y -- "index" with x using y as key
   j=o.i c k -- use c as index k as key; assume success
   u=j[x-1,x+1,x,x] -- new x coord
   v=j[y,y,y-1,y+1] -- new y coord
   g t=f(j t,u,v) -- recurse; use t for new direction
main=do
 z<-getContents
 putStrLn$r$lines z
4
répondu comingstorm 2009-09-27 00:50:44

je crois en la réutilisation de Code, j'utiliserais un de vos codes comme API :).

  puts Board.new.validate(input)

32 caractères \o/... wohoooooo

3
répondu Rishav Rastogi 2009-09-26 09:10:40

c++: 388 caractères

#include<iostream>
#include<string>
#include<deque>
#include<cstring>
#define w v[y][x]
using namespace std;size_t y,x,*z[]={&y,&x};int main(){string p="^v<>",s;deque<string>v;
while(getline(cin,s))v.push_back(s);while(x=v[++y].find_first_of(p),!(x+1));int 
i=p.find(w),d=i%2*2-1,r=i/2;do while(*z[r]+=d,w=='/'?d=-d,0:w==' ');while(r=!r,
!strchr("#x<^v>",w));cout<<(w=='x'?"true":"false");}

( 318 sans en-têtes)


Comment cela fonctionne:

D'abord, toutes les lignes sont lues, ensuite, le laser est trouvé. Ce qui suit va évaluer à 0 aussi longtemps qu'aucune flèche laser n'a été trouvé encore, et le même temps attribuer à x la position horizontale.

x=v[++y].find_first_of(p),!(x+1)

ensuite, nous regardons dans quelle direction nous avons trouvé et le stocker dans i . Les valeurs égales de i sont haut/gauche ("décroissant") et les valeurs impaires sont bas/droite ("croissant"). Selon cette notion, d ("direction") et r ("orientation") sont définis. Nous indexons le tableau de pointeur z avec l'orientation et ajoutons la direction à l'entier que nous obtenons. La direction change seulement si on frappe une barre oblique, alors qu'elle reste la même quand on frappe une barre oblique. Bien sûr, quand nous cliquez sur un miroir, puis nous changeons toujours d'orientation ( r = !r ).

3
répondu Johannes Schaub - litb 2009-09-28 04:22:58

Groovy @ 279 charatres

m=/[<>^v]/
i={'><v^'.indexOf(it)}
n=['<':{y--},'>':{y++},'^':{x--},'v':{x++}]
a=['x':{1},'\':{'v^><'[i(d)]},'/':{'^v<>'[i(d)]},'#':{},' ':{d}]
b=[]
System.in.eachLine {b<<it.inject([]) {r,c->if(c==~m){x=b.size;y=r.size;d=c};r<<c}}
while(d==~m){n[d]();d=a[b[x][y]]()}
println !!d
2
répondu Reverend Gonzo 2009-09-27 06:37:03

c#

1020 caractères.

1088 caractères (Entrée ajoutée à partir de la console).

925 caractères (variables modifiées).

875 caractères (suppression de l'initialiseur redondant du dictionnaire; remplacé par binaire & operators)

a fait un Point de ne pas regarder quelqu'un d'autre avant poster. Je suis sûr que ça pourrait être un peu plus haut. Et toute la méthode FindLaser dans la version lisible me semble affreusement louche. Mais, ça marche et c'est la fin :)

Note la classe lisible comprend une méthode supplémentaire qui imprime l'arène courante au fur et à mesure que le laser se déplace.

class L{static void Main(){
A=new Dictionary<Point,string>();
var l=Console.ReadLine();int y=0;
while(l!=""){var a=l.ToCharArray();
for(int x=0;x<a.Count();x++)
A.Add(new Point(x,y),l[x].ToString());
y++;l=Console.ReadLine();}new L();}
static Dictionary<Point,string>A;Point P,O,N,S,W,E;
public L(){N=S=W=E=new Point(0,-1);S.Offset(0,2);
W.Offset(-1,1);E.Offset(1,1);D();
Console.WriteLine(F());}bool F(){
var l=A[P];int m=O.X,n=O.Y,o=P.X,p=P.Y;
bool x=o==m,y=p==n,a=x&p<n,b=x&p>n,c=y&o>m,d=y&o<m;
if(l=="\"){if(a)T(W);if(b)T(E);if(c)T(S);
if(d)T(N);if(F())return true;}
if(l=="/"){if(a)T(E);if(b)T(W);if(c)T(N);
if(d)T(S);if(F())return true;}return l=="x";}
void T(Point p){O=P;do P.Offset(p);
while(!("\,/,#,x".Split(',')).Contains(A[P]));}
void D(){P=A.Where(x=>("^,v,>,<".Split(',')).
Contains(x.Value)).First().Key;var c=A[P];
if(c=="^")T(N);if(c=="v")T(S);if(c=="<")T(W);
if(c==">")T(E);}}

Version Lisible (pas tout à fait la finale de golf version, mais même principe):

class Laser
{
    private Dictionary<Point, string> Arena;
    private readonly List<string> LaserChars;
    private readonly List<string> OtherChars;

    private Point Position;
    private Point OldPosition;
    private readonly Point North;
    private readonly Point South;
    private readonly Point West;
    private readonly Point East;

    public Laser( List<string> arena )
    {
        SplitArena( arena );
        LaserChars = new List<string> { "^", "v", ">", "<" };
        OtherChars = new List<string> { "\", "/", "#", "x" };
        North = new Point( 0, -1 );
        South = new Point( 0, 1 );
        West = new Point( -1, 0 );
        East = new Point( 1, 0 );
        FindLaser();
        Console.WriteLine( FindTarget() );
    }

    private void SplitArena( List<string> arena )
    {
        Arena = new Dictionary<Point, string>();
        int y = 0;
        foreach( string str in arena )
        {
            var line = str.ToCharArray();
            for( int x = 0; x < line.Count(); x++ )
            {
                Arena.Add( new Point( x, y ), line[x].ToString() );
            }
            y++;
        }
    }

    private void DrawArena()
    {
        Console.Clear();
        var d = new Dictionary<Point, string>( Arena );

        d[Position] = "*";
        foreach( KeyValuePair<Point, string> p in d )
        {
            if( p.Key.X == 0 )
                Console.WriteLine();

            Console.Write( p.Value );
        }
        System.Threading.Thread.Sleep( 400 );
    }

    private bool FindTarget()
    {
        DrawArena();

        string chr = Arena[Position];

        switch( chr )
        {
            case "\":
                if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
                {
                    OffSet( West );
                }
                else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
                {
                    OffSet( East );
                }
                else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
                {
                    OffSet( South );
                }
                else
                {
                    OffSet( North );
                }
                if( FindTarget() )
                {
                    return true;
                }
                break;
            case "/":
                if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
                {
                    OffSet( East );
                }
                else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
                {
                    OffSet( West );
                }
                else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
                {
                    OffSet( North );
                }
                else
                {
                    OffSet( South );
                }
                if( FindTarget() )
                {
                    return true;
                }
                break;
            case "x":
                return true;
            case "#":
                return false;
        }
        return false;
    }

    private void OffSet( Point p )
    {
        OldPosition = Position;
        do
        {
            Position.Offset( p );
        } while( !OtherChars.Contains( Arena[Position] ) );
    }

    private void FindLaser()
    {
        Position = Arena.Where( x => LaserChars.Contains( x.Value ) ).First().Key;

        switch( Arena[Position] )
        {
            case "^":
                OffSet( North );
                break;
            case "v":
                OffSet( South );
                break;
            case "<":
                OffSet( West );
                break;
            case ">":
                OffSet( East );
                break;
        }
    }
}
2
répondu Metro Smurf 2009-09-28 17:53:10

Perl 219

Ma version perl est 392 342 caractères de long (j'ai dû gérer le cas du faisceau frappant le laser):

mise à jour , Merci Hobbs pour me rappeler de tr// , il est maintenant 250 caractères:

mise à jour , en supprimant le m dans m// , en changeant les deux while boucles apporté quelques économies; il n'y a maintenant qu'un espace requis.

( L:it;goto L est la même longueur que do{it;redo} ):

@b=map{($y,$x,$s)=($a,$-[0],$&)if/[<>^v]/;$a++;[split//]}<>;L:$_=$s;$x++if/>/;
$x--if/</;$y++if/v/;$y--if/\^/;$_=$b[$y][$x];die"true\n"if/x/;die"false\n"if
/[<>^v#]/;$s=~tr/<>^v/^v<>/if/\/;$s=~tr/<>^v/v^></if/\//;goto L

je me suis rasé certains, mais il à peine juste en concurrence avec certains de ces cas, mais retard.

Il semble un peu mieux que:

#!/usr/bin/perl
@b = map {
    ($y, $x, $s) = ($a, $-[0], $&) if /[<>^v]/;
    $a++;
    [split//]
} <>;
L:
    $_ = $s;
    $x++ if />/;
    $x-- if /</;
    $y++ if /v/;
    $y-- if /\^/;
    $_ = $b[$y][$x];
    die "true\n"  if /x/;
    die "false\n" if /[<>^v#]/;
    $s =~ tr/<>^v/^v<>/ if /\/;
    $s =~ tr/<>^v/v^></ if /\//;
goto L

bien... Honnêtement, cela devrait être explicite si vous comprenez que le @b est un tableau de des caractères dans chaque ligne, et peut lire les instructions simples regexp et tr .

0
répondu dlamblin 2009-09-28 17:03:52

F# - 454 (ou aux alentours)

un peu tard pour le jeu, mais je ne peux pas résister à poster ma tentative 2d.

mise à Jour légèrement modifié. S'arrête désormais correctement si l'émetteur est touché. Pincé L'idée de Brian pour IndexOfAny (honte que la ligne est si verbeux). Je n'ai pas vraiment réussi à trouver comment obtenir ReadToEnd pour revenir de la Console, donc je prends ce morceau sur la confiance...

je suis content de ce réponse, comme si elle était assez courte, elle est encore assez lisible.

let s=System.Console.In.ReadToEnd()       //(Not sure how to get this to work!)
let w=s.IndexOf('\n')+1                   //width
let h=(s.Length+1)/w                      //height
//wodge into a 2d array
let a=Microsoft.FSharp.Collections.Array2D.init h (w-1)(fun y x -> s.[y*w+x])
let p=s.IndexOfAny[|'^';'<';'>';'v'|]     //get start pos
let (dx,dy)=                              //get initial direction
 match "^<>v".IndexOf(s.[p]) with
 |0->(0,-1)
 |1->(-1,0)
 |2->(1,0)
 |_->(0,1)
let mutable(x,y)=(p%w,p/w)                //translate into x,y coords
let rec f(dx,dy)=
 x<-x+dx;y<-y+dy                          //mutate coords on each call
 match a.[y,x] with
 |' '->f(dx,dy)                           //keep going same direction
 |'/'->f(-dy,-dx)                         //switch dx/dy and change sign
 |'\'->f(dy,dx)                          //switch dx/dy and keep sign
 |'x'->"true"
 |_->"false"
System.Console.Write(f(dx,dy))
0
répondu Benjol 2010-01-26 08:44:43