Le code le plus court par nombre de caractères pour entrer une représentation 2D d'une carte et sortir "vrai" ou "faux" en fonction de l'entrée .
Le plateau est composé de 4 types de tuiles:
# - 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 n'y a que n laser et seulement ne cible. Les murs doivent former un rectangle plein de n'importe quelle taille, où le laser et la cible sont placés à l'intérieur. Des murs à l'intérieur de la "pièce" sont possibles.
Les rayons laser tirent et se déplacent de leur origine à la direction vers laquelle ils pointent. Si un rayon laser frappe le mur, il s'arrête. Si un rayon laser frappe un miroir, il rebondit de 90 degrés dans la direction vers laquelle le miroir pointe. Les miroirs sont à deux faces, ce qui signifie que les deux faces sont "réfléchissantes" et peuvent faire rebondir un rayon de deux manières. Si un rayon laser frappe le laser (^v><
) lui-même, il est traité comme un mur (le faisceau laser détruit le beamer et il ne touchera donc jamais la cible).
Entrée: ########## #/\ # # # #\X # #>/# ########## Sortie: Vrai Entrée: ########## # vx # #/# # /# #\# ### ####### Sortie: False Entrée: ############# # # # #> # # # # # # # x # # # # ### ########## Sortie: False Entrée: ########## #/\/\/\ # # \\ // \\\ # # // \/\/\\ # # \/\/\/x ^ # ########## Sortie: vrai
Le nombre de codes comprend les entrées/sorties (c'est-à-dire le programme complet).
Perl, 251248246222214208203201193190180176173170 166 -> 160 caractères.
Solution avait 166 coups à la fin de ce concours, mais A. Rex a trouvé quelques façons de raser 6 autres personnages:
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 du tableau où $t{99*i+j}
contient le caractère à la ligne i, colonne j. Alors,
%d=split//,'>.^1<2v3' ; ($r)=grep{$d|=$d{$t{$_}}}%t
il recherche les éléments de %t
pour un caractère correspondant à > ^ <
ou v
, et définit simultanément $d
à une valeur comprise entre 0 et 3 qui indique la direction initiale du faisceau laser.
Au début de chaque itération dans la boucle principale, nous mettons à jour $d
si le faisceau est actuellement sur un miroir. XOR'ing par 3 donne le comportement correct pour un \
miroir et XOR'ing par 1 donne le comportement correct pour un /
miroir.
$d^=3*/\\/+m</>
Ensuite, la position actuelle $r
est mis à jour selon la direction actuelle.
$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}
Nous affectons le caractère à la position actuelle à $_
pour utiliser facilement les opérateurs de correspondance.
/[\/\\ ]/ && redo
Continuez si nous sommes sur un espace vide ou un personnage miroir. Sinon, nous terminons true
si nous sommes sur la cible ($_ =~ /x/
) et false
sinon.
Limitation: peut ne pas fonctionner sur les problèmes avec plus de 99 colonnes. Cette limitation pourrait être supprimée au détriment de 3 caractères supplémentaires,
Le premier saut de ligne peut être supprimé; 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{$1}/?$s=$1:1;redo}
Explication:
$/ = %d = (' ' => '>', '/' => '^', '\\' => 'v');
Si un faisceau se déplaçant vers la droite se heurte à un {espace vide, miroir vers le haut, miroir vers le bas}, il devient un {faisceau se déplaçant vers la droite, faisceau se déplaçant vers le haut, faisceau se déplaçant vers le bas}. Initialisez $/
En cours de route - heureusement, "6" n'est pas un caractère d'entrée valide.
$_ = <>;
Lisez le tableau dans $_
.
$s="#";
$s
Est le symbole de tout ce sur quoi se trouve le faisceau. Étant donné que 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 dans un sens autre que vers la droite, faites pivoter son symbole, puis faites pivoter la carte entière en place (en faisant également tourner les symboles des miroirs). C'est une rotation à gauche de 90 degrés, accomplie efficacement en inversant les lignes tout en transposant les lignes 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'''
Qui me permet de sauter une barre oblique inverse.
die "true\n" if />x/; die "false\n" if />#/;
Terminez avec le bon message si nous frappons la cible ou un mur.
$s = $1 if s/>(.)/$s$d{$1}/;
S'il y a un espace vide devant le laser, avancez. S'il y a un miroir devant le laser, avancez et faites pivoter le faisceau. Dans les deux cas, remettez le "symbole enregistré" dans l'ancien emplacement du faisceau et mettez la chose que nous venons d'écraser dans le symbole enregistré.
redo;
Répétez jusqu'à la fin. {...;redo}
Représente deux caractères de moins que for(;;){...}
et trois de moins que while(1){...}
.
#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;);}
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 à ce que a
est sous forme de caractères (*#a
). S'ils sont égaux, définissez le vecteur de mouvement sur b
(m=b
), Marquez ce caractère comme un mur (*p=1
) Et définissez le point de départ à l'emplacement actuel ( q=p
). Cette macro inclut la partie "else".
*q,G[999],*p=G;
w;
Déclarez certaines variables. * q
est l'emplacement actuel de la lumière. * G
est le plateau de jeu sous forme de tableau 1D. * p
est l'emplacement de lecture actuel lors du remplissage de 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 pour main
comme optimisation.)
for(;(*++p=getchar())>0;)
Parcourez tous les caractères en remplissant G
à l'aide de p
. Ignorez G[0]
Comme optimisation (pas besoin de gaspiller un caractère en écrivant 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
de haut en bas.
!w&*p<11
?w=p-G
:0;
Si le caractère actuel est un marqueur de fin de ligne (ASCII 10), définissez la largeur s'il n'a pas déjà été défini. Le G[0]
Ignoré nous permet d'écrire w=p-G
Au lieu de w=p-G+1
. De plus, cela termine la chaîne ?:
Des M
.
for(;
q+=m,
Déplacez la lumière par le vecteur de mouvement.
m=
*q&4
?(*q&1?-1:1)*(
m/w?m/w:m*w
)
Refléter le vecteur de mouvement.
:*q&9
?!puts(*q&1?"false":"true")
:m
;
S'il s'agit d'un mur ou x
, quittez avec le message approprié (m=0
Termine la boucle). Sinon, ne faites rien (noop; m=m
)
);
}
Je parierais que les gens attendaient celui-ci depuis longtemps. (Que voulez-vous dire, le défi est terminé et personne ne se soucie plus?)
Voici ... Je présente ici une solution
Il pèse un énorme 973 charaters (ou 688 si vous êtes suffisamment charitable pour ignorer les espaces, qui ne sont utilisés que pour le formatage et ne font rien dans le code réel).
Caveat: J'ai écrit mon propre interprète Befunge-93 en Perl il y a peu de temps, et malheureusement c'est tout ce que j'ai vraiment eu le temps de tester. Je suis raisonnablement confiant dans son exactitude en général, mais il pourrait avoir une limitation étrange en ce qui concerne EOF: Puisque <>
L'opérateur renvoie undef à la fin du fichier, il est traité comme un 0 dans le contexte numérique. Pour les implémentations basées sur C où EOF a une valeur différente (-1 disons), ce code peut 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"/":< >$3p$^>05g43p$ ^55
>,@ |!-"\" :_$43g:">"-!|> ^$32
*v"x":< >-^ ^4g52<>:"^" -#v_^
5>-!#v_"ror"vv$p34g51:<>#| !-"<":<#|
^2,,, ,,"er"<>v #^^#<>05g43p$$^>^
>52*"eurt",,,,,@>15g4 3p$$$$ ^#
>:"v"\:"<"\: "^" -!#^_-!#^_-! ^
> ^
Si vous n'êtes pas familier avec la syntaxe et le fonctionnement de Befunge, vérifiez ici .
Befunge est un langage basé sur la pile, mais il existe des commandes qui permettent d'écrire des caractères dans le code Befunge. J'en profite à deux endroits. Tout d'abord, je copie l'intégralité de l'entrée sur le tableau Befunge, mais j'ai localisé quelques lignes sous le code écrit réel. (Bien sûr, cela n'est jamais réellement visible lorsque le code s'exécute.)
L'autre endroit est près du coin supérieur gauche:
######
a#
######
Dans ce cas, la zone que j'ai mise en évidence ci-dessus est l'endroit où je stocke quelques coordonnées. La première colonne de la ligne du milieu est l'endroit où je stocke la coordonnée x pour la "position du curseur" actuelle; la deuxième colonne est l'endroit où je stocke la coordonnée y; les deux colonnes suivantes sont destinées à stocker les coordonnées x et y de la source de faisceau laser lorsqu'elle est trouvée; et la dernière colonne (avec le caractère 'a' dedans) est finalement remplacée pour contenir la direction actuelle du faisceau, qui change évidemment lorsque le chemin du faisceau est tracé.
Le programme commence par placer (0,27) comme position initiale du curseur. La saisie est ensuite lue un caractère à la fois et placée à la position du curseur; les nouvelles lignes font simplement augmenter la coordonnée y et la coordonnée x pour revenir à 0, tout comme un vrai retour chariot. Finalement, undef est lu par l'interpréteur et cette valeur de 0 caractère est utilisée pour signaler la fin de l'entrée et passer aux étapes d'itération laser. Lorsque le caractère laser [<> ^ v] est lu, il est également copié dans le référentiel de mémoire (sur le 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 en dessous du code réel traversé.
Ensuite, l'emplacement du faisceau est recopié dans les emplacements du curseur et l'itération suivante est effectuée:
S'il y a suffisamment de demande, j'essaierai de montrer exactement où dans le code tout cela est accompli.
La nouvelle ligne est juste là pour l'emballage
:|'v^><'.{|?}%{)}?:$@=?{.[10|?).~)1-1]=$+
:$|=' \/x'?\[.\2^.1^'true''false']=.4/!}do
Golfscript - 107 caractères
La nouvelle ligne est juste là pour clarté
10\:@?):&4:$;{0'>^<v'$(:$=@?:*>}do;
{[1 0&--1&]$=*+:*;[{$}{3$^}{1$^}{"true "}{"false"}]@*=' \/x'?=~5\:$>}do$
Comment ça fonctionne.
La première ligne détermine l'emplacement et la direction initiaux.
La deuxième ligne passe par la rotation chaque fois que le laser frappe un miroir.
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
Échantillons:
##########
# / \ #
# #
# \ x#
# > / #
##########
true
##########
# v x #
# / #
# /#
# \ #
##########
false
#############
# # #
# > # #
# # #
# # x #
# # #
#############
false
##########
#/\/\/\ #
#\\//\\\ #
#//\/\/\\#
#\/\/\/x^#
##########
true
##########
# / \ #
# #
#/ \ x#
#\> / #
##########
false
##########
# / \#
# / \ #
#/ \ x#
#\^/\ / #
##########
false
353 caractères en Ruby:
314277 caractères maintenant!
OK, 256 caractères en Ruby et maintenant j'ai terminé. Joli numéro rond pour m'arrêter. :)
247 caractères. Je ne peux pas m'arrêter.
223203 201 caractères en Ruby
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:
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 refactorisé:
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
294277253240 232 caractères, y compris les sauts de ligne:
(le premier caractère des lignes 4 et 5 est une tabulation, 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é Python avait même des points-virgules facultatifs.
L'idée clé derrière ce code est d'utiliser des nombres complexes pour représenter les positions et les directions. Les rangées sont l'axe imaginaire, augmentant vers le bas. Les colonnes sont l'axe réel, augmentant vers la droite.
l='>v<^';
Une liste des symboles laser. L'ordre est choisi pour 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 le tableau. Le premier élément est 1 (évalué comme vrai) afin que la boucle while s'exécute au moins une fois.
r=p=0
r
est le numéro de ligne actuel de l'entrée, p
est la position actuelle du faisceau laser.
while b[-1]:
Arrêtez de charger les données de la carte lorsque raw_input renvoie une chaîne vide
b+=[raw_input()];r+=1
ajoute la ligne d'entrée suivante à la carte et incrémente le compteur de lignes
for g in l:
Devinez tour à tour chaque direction du laser
c=b[r].find(g)
définit 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 définissez la position actuelle p
et la direction d
. d
est l'un des caractères de l
Après avoir chargé la carte dans b
, la position actuelle p
et la direction d
ont été définies sur celles de la source laser.
L'espace while' '<d:
A une valeur ASCII inférieure à tous les symboles de direction, nous l'utilisons donc comme indicateur d'arrêt.
z=l.find(d);
index du caractère de direction courant dans la chaîne l
. z
sera utilisé plus tard pour déterminer la nouvelle direction du faisceau à l'aide de la table x
et pour incrémenter la position.
p+=1j**z;
Incrémentez la position en utilisant une puissance de i. Par exemple, l.find('<')==2
-> i ^ 2 = -1, qui se déplacerait vers la gauche d'une colonne.
c=b[int(p.imag)][int(p.real)];
lire le caractère à la position actuelle
d=x.get(c,' '*4)[z]
recherche la nouvelle direction du faisceau dans la table de transformation. Si le caractère actuel n'existe pas dans la table, définissez d
sur espace.
print'#'<c
Affiche faux si nous nous arrêtons sur autre chose que la cible.
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, glissez toutes les entrées dans un grand tableau unidimensionnel (les tableaux 2D peuvent être mauvais pour le golf de code; utilisez simplement un tableau 1D et ajoutez/soustrayez la largeur d'une ligne à l'index pour monter/descendre une ligne).
Ensuite, nous calculons "w", la largeur d'une ligne d'entrée, et "c", la position de départ, en l'indexant dans notre tableau.
Définissons maintenant la fonction 'n' suivante 'n', qui prend une position courante 'c' et une direction 'd' qui est 0,1,2,3 pour haut, gauche, droite, bas.
L'index-epsilon 'e' et le 'new-direction-if-we-hit-a-slash' s 'sont calculés par une table. Par exemple, si la direction actuelle 'd' est 0 (haut), alors le premier élément du tableau dit "-w, 2" ce qui signifie que nous décrémentons l'index de w, et si nous frappons une barre oblique, la nouvelle direction est 2 (droite).
Maintenant, nous récursions dans la fonction suivante 'n' avec (1) l'index suivant ("c + e" - courant plus epsilon), et (2) la nouvelle direction, que nous calculons en regardant en avant pour voir ce qui est dans le tableau dans cette cellule suivante. Si le caractère d'anticipation est une barre oblique, la nouvelle direction est "s". S'il s'agit d'une barre oblique inverse, la nouvelle direction est de 3 s (notre choix de codage 0123 fait que cela fonctionne). Si c'est un espace, nous continuons simplement dans la même direction 'd'. Et s'il s'agit d'un autre caractère "c", le jeu se termine, affichant "vrai" si le caractère était "x" et faux sinon.
Pour commencer, nous appelons la fonction récursive 'n' avec la position initiale 'c' et la direction de départ (qui fait le codage initial de la direction en 0123).
Je pense que je peux probablement encore raser quelques personnages de plus, mais j'en suis assez content comme ça (et 255 est un bon chiffre).
Cette est était un port direct de la solution de Brian à C # 3, moins les interactions de la console. Ce n'est pas une entrée dans le défi car ce n'est pas un programme complet, je me demandais simplement comment certaines des constructions F # qu'il utilisait pouvaient être représentées en 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 assez 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 du code LINQ to Objects beaucoup plus compact:
var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
.First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));
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 simple machine à états (comme la plupart des affiches), rien d'extraordinaire. Je n'arrêtais pas de le réduire en utilisant tous les trucs auxquels je pouvais penser. Le bit XOR utilisé pour changer de direction (stocké sous forme d'entier dans la variable c
)) était une grande amélioration par rapport aux conditions que j'avais dans les versions précédentes.
Je soupçonne que le code qui incrémente x
et y
pourrait être raccourci. Voici la section du code qui effectue l'incrémentation:
c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1
Edit: J'ai pu raccourcir légèrement ce qui précède:
c<2&&y+=c*2-1;c>1&&x+=2*c-5
La direction actuelle du laser c
est enregistrée comme suit:
0 => haut 1 => bas 2 => gauche 3 => droite
Le code s'appuie sur ce fait pour incrémenter x
et y
du montant correct (0, 1 ou -1). J'ai essayé de réorganiser les numéros qui correspondent à chaque direction, à la recherche d'un arrangement qui me permettrait de faire des manipulations au niveau du bit pour incrémenter les valeurs, car j'ai l'impression que ce serait plus court que la version arithmétique.
Pesant 18203 caractères est une solution Python qui peut:
Il faut encore un peu ranger et je ne sais pas si la physique 2D dicte que le faisceau ne peut pas se traverser ...
#!/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, "\033[01;31m%s\033[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 = "%s\033[01;31m%s\033[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 = "%s\033[01;5;31m#\033[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 __== "__main__":
main()
Un script bash pour montrer le rapport d'erreur de couleur:
#!/bin/bash
declare -a TESTS
test() {
echo -e "\033[1m$1\033[0m"
tput sgr0
echo "$2" | ./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#
#\\^/\\ / #
##########"
Les unités utilisées dans 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
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, nous permettant d'utiliser l'astuce miroir XOR. Elle est également incroyablement fragile - toutes les lignes d'entrée doivent être de la même longueur) , par exemple.
Il casse sous la barre des 200 caractères - mais bon sang, il n'a toujours pas battu ces solutions Perl!
Bonjour, grignoteur!
:\'><v^'.{\?}%{)}?:P@=?{:O[1-1\10?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do
Lit l'entrée 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
Pour lire depuis stdin, remplacez la première ligne par ceci
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()
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;}}
Un peu 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 de caractères semble être de trouver la largeur de la carte et la position de la source laser. Des idées pour raccourcir cela?
JavaScript - 265 caractères
Mise à jour IV - Il y a de fortes chances que ce soit la dernière série de mises à jour, réussissant à enregistrer quelques caractères supplémentaires en passant à une boucle do-while et en réécrivant l'équation de mouvement.
Mise à jour III - Grâce à la suggestion de strager concernant la suppression de Math.abs () et la mise en place des variables dans l'espace de nom global, cela couplé à une réorganisation des affectations de variables a fait descendre le code à 282 personnages.
pdate II - Quelques mises à jour supplémentaires du code pour supprimer l'utilisation de! = -1 ainsi qu'une meilleure utilisation des variables pour des opérations plus longues.
pdate - Lorsque vous avez terminé et effectué quelques modifications en créant une référence à la fonction indexOf (merci LiraNuna!) Et en supprimant les parenthèses qui n'étaient pas nécessaires.
C'est ma première fois que je fais un golf de code, donc je ne sais pas à quel point cela pourrait être mieux, tout retour 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>
Le physicien en moi a noté que les opérations de propagation et de réflexion sont invariantes à inversion de temps, donc cette version, jette des rayons depuis la cible et vérifie si le arriver à l'émetteur laser.
Le reste de la mise en œuvre est très simple et provient plus ou moins exactement de mes efforts précédents.
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 liera la bibliothèque standard sans inclure aucun en-tête. Et je pense que j'ai fini, Strager m'a fait battre par un étirement considérable, même avec son aide.
J'espère plutôt que quelqu'un démontrera une manière plus subtile d'accomplir la tâche. Il n'y a rien de mal à cela, mais ce n'est pas de la magie profonde.
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
Maison des miroirs
Pas vraiment une entrée au défi, mais j'ai écrit un jeu basé sur ce concept (pas trop longtemps en arrière).
Il est écrit en Scala, open-source et disponible ici :
Il en fait un peu plus; traite des couleurs et de divers types de miroirs et d'appareils, mais la version 0.00001 a fait exactement ce que ce défi demande. J'ai cependant perdu cette version et elle n'a jamais été optimisée pour le nombre de personnages.
Première tentative, 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 =
Haskell, 395391383361 339 caractères (optimisé)
Utilise toujours 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
Je crois en la réutilisation du code, j'utiliserais l'un de vos codes comme API :).
met Board.new.validate (entrée)
32 caractères\o/... wohoooo
C++: 88 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");}
(18 sans en-têtes)
Comment ça fonctionne:
D'abord, toutes les lignes sont lues, puis le laser est trouvé. Les éléments suivants seront évalués à 0
tant qu'aucune flèche laser n'a encore été trouvée, et en même temps affectez à x
la position horizontale.
x=v[++y].find_first_of(p),!(x+1)
Ensuite, nous regardons dans quelle direction nous avons trouvé et le stockons dans i
. Les valeurs paires de i
sont en haut/à gauche ("décroissantes") et les valeurs impaires sont en bas/à droite ("croissantes"). Selon cette notion, d
("direction") et r
("orientation") sont définis. Nous indexons le tableau de pointeurs z
avec l'orientation et ajoutons la direction à l'entier que nous obtenons. La direction ne change que si nous frappons une barre oblique, alors qu'elle reste la même lorsque nous frappons une barre oblique inverse. Bien sûr, lorsque nous frappons un miroir, nous changeons toujours d'orientation (r = !r
).
C #
1020 caractères.1088 caractères (ajout d'une entrée de la console).925 caractères (variables refactorisées).
875 caractères (suppression de l'initialiseur de dictionnaire redondant; remplacé par Opérateurs binaires)
Je me suis fait un devoir de ne regarder personne d'autre avant de poster. Je suis sûr que cela pourrait être un peu LINQ. Et toute la méthode FindLaser dans la version lisible me semble terriblement louche. Mais ça marche et il est tard :)
Notez que la classe lisible comprend une méthode supplémentaire qui imprime l'arène actuelle lorsque 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 version finale du golf, 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;
}
}
}
Groovy @ 279 caractères
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
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 de m'avoir rappelé tr//
, c'est maintenant 250 caractères:
Mise à jour , en supprimant le m
dans m//
, la modification des deux boucles while
a permis de réaliser quelques économies; il n'y a maintenant qu'un seul espace requis.
(L:it;goto L
a 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
J'en ai rasé, mais à peinerivalise avec certains d'entre eux, bien que tard.
Ça a l'air un peu mieux car:
#!/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
Eh bien ... Honnêtement, cela devrait être explicite si vous comprenez que le @b
est un tableau de caractères de chaque ligne, et peut lire les instructions regexp et tr
simples.
Un peu tard pour le jeu, mais je ne peux pas résister à poster ma tentative 2d.
pdate légèrement modifié. Arrête maintenant correctement si l'émetteur est touché. A pincé l'idée de Brian pour IndexOfAny (dommage que la ligne soit si verbeuse). Je n'ai pas vraiment réussi à trouver comment obtenir ReadToEnd pour revenir de la console, donc je prends ce bit sur la confiance ...
Je suis satisfait de cette réponse, car si elle est 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))