高尔夫代码: 激光

挑战

按字符计数的最短代码输入一块板的二维表示,并输出“ true”或“ false”根据输入

这块板子由四种瓷砖组成:

 # - 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)

只有 一束激光一个目标。墙壁必须形成一个实心矩形的任何大小,其中激光和目标放置在里面。房间内的墙壁是可能的。

激光射线从它的原点射向它指向的方向。如果激光射到墙上,就会停止。如果一束激光射到一面镜子上,它会朝着镜子指向的方向反弹90度。镜子是双面的,意思是两面都是“反射的”,可以用两种方式反射光线。如果激光射线击中激光器(^v><)本身,它被当作一堵墙(激光束摧毁激光器,因此它永远不会击中目标)。

测试案例

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


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


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


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

代码计数包括输入/输出(即完整程序)。

12605 次浏览

F # ,36行,非常易读

好吧,只是想得到一个答案:

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

样本:

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


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


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


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


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


##########
#  /    \#
# / \    #
#/    \ x#
#\^/\ /  #
##########
false

C89(209个字符)

#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;);}

解释

如果你不理解 C,这个怪物可能很难理解。只是一个预先的警告。

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

这个小宏检查当前字符(*p)是否等于字符形式(*#a)中的 a。如果它们相等,将运动向量设置为 b(m=b) ,将该字符标记为墙(*p=1) ,并将起点设置为当前位置(q=p)。此宏包括“ else”部分。

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

声明一些变量。 q是光的当前位置。 * G是一个1D 数组的游戏板。 * 在填充 G时,p是当前读取位置。 w是板子的宽度 * 。

main(m){

显然 mainm是一个存储运动向量的变量。(它是 main的一个优化参数。)

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

循环遍历所有字符,使用 p填充 G。跳过 G[0]作为一个优化(没有必要浪费一个字符写 p再次在第三部分的 for)。

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

如果可能的话,使用上面提到的宏来定义激光器。-11分别对应于左侧和右侧,-ww上下对应。

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

如果当前字符是行尾标记(ASCII 10) ,则设置尚未设置的宽度。跳过的 G[0]允许我们编写 w=p-G而不是 w=p-G+1。此外,这完成了从 M?:链。

    for(;
q+=m,

通过运动矢量来移动光源。

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

反射运动矢量。

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

如果这是一个墙或 x,退出与适当的消息(m=0终止循环)。否则,什么也不做(noop; m=m)

    );
}

C (K & R)339个必要的字符后,更多的建议从 strager。

我心中的物理学家注意到,传播和反射操作是时间反转不变的,所以这个版本,抛射光线 射击目标和检查,看看是否到达激光发射器。

实现的其余部分非常直截了当,或多或少正是从我早期的前进努力中获得的。

压缩:

#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");}

未压缩:

#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");
}

没有输入验证,错误的输入会将其发送到一个无限循环中。在输入不大于99乘99的情况下正常工作。需要一个将链接标准库而不包含任何标头的编译器。我想我完成了,Strager 赢了我的一个相当大的延伸,即使在他的帮助下。

我更希望有人能展示一种更微妙的方式来完成这项任务。这没什么问题,但这不是深奥的魔法。

F # ,255个字符(仍然相当可读!) :

好吧,经过一晚上的休息,我改进了很多:

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])

让我们一行一行地讨论。

首先,将所有输入放入一个大的一维数组(2D 数组对于代码高尔夫来说可能不太好; 只要使用一维数组并将一行的宽度加/减到索引中,就可以向上/向下移动一行)。

接下来,我们通过索引到数组中来计算‘ w’(输入行的宽度)和‘ c’(起始位置)。

现在让我们定义‘ next’函数‘ n’,它接受当前位置‘ c’和方向‘ d’,‘ d’是0,1,2,3,表示向上,向左,向右,向下。

Index-epsilon‘ e’和 what-new-direct-if-we-hit-a-slash‘ s 由表计算。例如,如果当前方向‘ d’为0(up) ,那么表的第一个元素表示“-w,2”,这意味着我们将索引减去 w,如果我们点击一个斜杠,那么新方向是2(右)。

现在,我们递归到下一个函数’n’,(1)下一个索引(“ c + e”-当前加上 ε) ,(2)新的方向,我们通过向前查看下一个单元格中的数组来计算。如果前瞻字符为斜杠,则新方向为“ s”。如果是反斜杠,那么新的方向是3-s (我们选择的编码0123可以实现这个功能)。如果它是一个空间,我们只是沿着相同的方向‘ d’前进。如果是其他字符“ c”,那么游戏结束,如果字符为“ x”,则输出“ true”,否则输出“ false”。

首先,我们使用初始位置‘ c’和起始方向(将方向初始编码为0123)调用递归函数‘ n’。

我认为我可能仍然可以削减一些字符了它,但我很高兴与它喜欢这样(和255是一个不错的数字)。

Perl,166160个字符

208 203 201 193 190 2480 2481 2482 2483166-> 160个字符。

当比赛结束时,解决方案有166笔画,但是 A。雷克斯找到了一些方法去掉另外6个字符:

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,$/

第一行将输入加载到 %t中,%t是板上的一个表,其中 $t{99*i+j}行、 J列保存字符。然后,

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

它在 %t的元素中搜索与 > ^ <v匹配的字符,并同时将 $d设置为一个介于0和3之间的值,该值指示激光束的初始方向。

在主回路的每次迭代开始时,如果光束当前在镜子上,我们更新 $d。XOR‘ ing 乘以3表示 \镜像的正确行为,XOR‘ ing 乘以1表示 /镜像的正确行为。

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

接下来,根据当前方向更新当前位置 $r

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

我们将当前位置的字符赋给 $_,以便于使用匹配运算符。

/[\/\\ ]/ && redo

如果我们处于空白区域或镜像字符上,请继续。否则,如果我们在目标($_ =~ /x/)上,则终止 true,否则终止 false

限制: 不能处理超过99列的问题。这个限制可以通过增加3个字符来消除,

Ruby 中的353个字符:

现在是314个字符 > 277个字符!

好了,Ruby 中有256个字符,现在我完成了。这个整数不错。 :)

247个字符,停不下来。

在 Ruby 中,203 < strong > 201个字符

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)}

使用空格:

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)
}

轻微重构:

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

Perl 219
我的 perl 版本是 392342个字符长(我必须处理光束击中激光的情况) :
更新 ,感谢霍布斯提醒我 tr//,它现在是250个字符:
更新 ,删除 m//中的 m,更改两个 while循环带来了一些节省; 现在只需要一个空间。
(L:it;goto Ldo{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

我刮了一些,但它只是与这些竞争,虽然晚了。
它看起来好一点:

#!/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

如果您了解 @b是每行中的字符数组,并且可以读取简单的 regexp 和 tr语句,那么坦白地说,这应该是不言而喻的。

巨蟒

294 277 253 240232个字符,包括换行:

(第4行和第5行的第一个字符是制表符,而不是空格)

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

我都忘了 Python 还有可选的分号。

它是如何工作的

这段代码背后的关键思想是使用复数来表示位置和方向。行是虚轴,向下增加。柱子是真正的轴,向右增加。

激光符号列表。顺序的选择使得激光方向特性的指数与 sqrt (- 1)的幂相对应

x={'/':'^<v>','\\':'v>^<',' ':l};一个转换表,确定当光束离开不同的瓷砖时方向如何改变。瓷砖是关键,新的方向是值。

b=[1];控制着董事会。第一个元素为1(计算结果为 true) ,因此 while 循环至少运行一次。

r=p=0是当前行数的输入,p是当前位置的激光束。

当 raw _ input 返回空字符串时,while b[-1]:停止加载板数据

b+=[raw_input()];r+=1将下一行输入附加到板上并递增行计数器

依次猜测每个激光方向

c=b[r].find(g)将光柱设置为激光的位置,如果不在直线上(或指向不同的方向)则设置为 -1

如果我们发现一个激光器,那么设置当前的位置 p和方向 ddl中的一个字符

在将电路板加载到 b后,将电流位置 p和方向 d设置为激光源的位置。

while' '<d:空间的 ASCII 值比任何方向符号都低,所以我们用它作为停止标志。

l字符串中当前方向字符的 z=l.find(d);索引。z稍后将用于使用 x表确定新的光束方向和增加位置。

p+=1j**z;使用 i 的幂增加位置,例如,l.find('<')==2-> i ^ 2 = -1,它将移动到左边的一列。

读取当前位置的字符

d=x.get(c,' '*4)[z]在变换表中查找光束的新方向。如果表中不存在当前字符,则将 d设置为空格。

如果我们在目标以外的地方停下来,print'#'<c就会打印错误。

这个 曾经是是 Brian 对 C # 3的解决方案的一个直接端口,减去了控制台交互。 这并不是一个完整的程序,因此这不是一个挑战,我只是想知道他使用的一些 F # 结构是如何在 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};
}
}

编辑: 经过一些实验,下面是相当冗长的搜索代码:

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;
}
}
}

已经被一些更加紧凑的 LINQ to Objects 代码所取代:

var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
.First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));

Perl,177个字符

可以删除第一个换行符; 其他两个是强制性的。

$/=%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}

说明:

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

如果一个向右移动的光束进入一个{空间,上角镜,下角镜}它就变成一个向右移动的光束,向上移动的光束,向下移动的光束}。顺便初始化 $/——幸运的是,“6”不是一个有效的输入字符。

$_ = <>;

把黑板读入 $_

$s="#";

$s是光束现在所在位置的标志。由于激光发射器将被视为墙壁,设置这是一个墙壁开始。

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

如果激光束指向除了右边以外的任何方向,旋转它的符号,然后旋转整个板子(也旋转镜子的符号)。这是一个90度左旋转,有效地完成了反转的行,同时调换行和列,在一个稍微凶恶的 s///e与副作用。在高尔夫代码中,tr 是以 y'''的形式编写的,它允许我跳过反斜杠一个反斜杠。

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

如果我们击中目标或者墙壁,用正确的信息结束。

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

如果激光器前面有空隙,就往前走。如果激光器前面有一面镜子,向前移动并旋转光束。在任何一种情况下,将“保存的符号”放回原来的光束位置,并将我们刚才覆盖的内容放入保存的符号中。

redo;

重复,直到终止。 {...;redo}for(;;){...}少两个字符,比 while(1){...}少三个字符。

镜屋

这不是一个真正的挑战,但我写了一个游戏基于这个概念(不太久回来)。

它是用 Scala 编写的,开源且可用的 给你:

它的功能稍微多一点; 处理颜色和各种类型的镜像和设备,但是版本0.00001完全满足了这个挑战的要求。我已经失去了那个版本,但它从来没有优化为字符数量无论如何。

C #

1020个字符。
1088个字符(从控制台添加输入)。
925个字符(重构变量)。
875个字符(删除冗余 Dictionary 初始化器; 更改为 Binary & 操作符)

在发帖之前不要看别人的。我敢肯定,它可以 LINQ 了一点。在我看来,可读版本中的整个 FindLaser 方法非常可疑。但是,它起作用了,而且已经很晚了:)

请注意,可读类包括一个额外的方法,打印出当前竞技场的激光移动。

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);}}

可读版本(不完全是最终的高尔夫版本,但前提相同) :

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;
}
}
}

我相信代码重用,我会用你的一个代码作为 API:)。

puts Board.new.validate(input)

32个字符

C # 3.0

259个字符

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;}}

可读性稍高:

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;
}
}

字符的主要浪费似乎是在寻找地图的宽度和激光源的位置。有什么办法能缩短这段时间吗?

Haskell,395 391 383 361339个字符(优化)

仍然使用通用状态机,而不是任何聪明的东西:

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}

一个可读的版本:

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

Groovy@279个字符

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

在18203个字符的情况下,Python 解决方案可以:

  • 应付外面的镜子 房间
  • 在没有“房间”的情况下,根据2D 的限制计算轨迹(说明书中说了很多关于“房间”中必须有什么的内容,但是如果房间必须存在的话就没有)
  • 报告错误

它仍然需要整理一些,我不知道如果二维物理规定,光束不能越过自己..。

#!/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 __name__ == "__main__":
main()

显示颜色错误报告的 bash 脚本:

#!/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#
#\\^/\\ /  #
##########"

开发中使用的单元测试:

#!/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 + + : 388字符

#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没有页眉)


工作原理:

首先,将所有线条读入,然后,找到激光器。以下将评估到 0,只要没有激光箭头被发现,并在同一时间分配给 x的水平位置。

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

然后我们看看我们发现的方向,并存储在 ii的偶数值为顶部/左侧(“减少”) ,奇数值为底部/右侧(“增加”)。根据这个概念,设置 d(“方向”)和 r(“方向”)。我们索引带有方向的指针数组 z,并将方向添加到我们得到的整数中。只有当我们按下斜杠时方向才会改变,而当我们按下反斜杠时方向保持不变。当然,当我们碰到镜子时,我们总是改变方向(r = !r)。

Ruby 176个字符

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}

我使用了一个简单的状态机(像大多数海报一样) ,没什么特别的。我只是不停地用我能想到的所有技巧削减它。用于改变方向的按位 XOR (存储为变量 c中的整数)比我在早期版本中使用的条件语句有了很大的改进。

我怀疑递增 xy的代码可以缩短。下面是执行递增操作的代码部分:

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

编辑 : 我可以稍微缩短上面的内容:

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

激光器 c的当前方向储存如下:

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

代码依赖于这个事实来增加 xy的正确数量(0、1或 -1)。我尝试重新排列映射到每个方向的数字,寻找一种安排,让我做一些按位操作来增加值,因为我有一种烦人的感觉,它会比算术版本更短。

JavaScript-265个字符

更新 IV -这可能是最后一轮更新,通过切换到 do-while 循环并重写移动方程,设法节省了几个字符。

更新 III ——多亏了 strager 关于删除 Math.abs ()并将变量放入全局名称空间的建议,再加上对变量赋值的一些重新排列,使代码减少到了282个字符。

更新 II -对代码进行一些更新以删除使用!= -1以及更好地使用变量进行更长的操作。

更新 -通过创建 indexOf 函数的引用进行一些修改(感谢 LiraNuna!)去掉不需要的括号。

这是我第一次做一个代码高尔夫球,所以我不知道这能有多好,任何反馈都是感激的。

完全最小化版本:

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}

原版附评论:

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;
}

测试网页:

<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>

C + ASCII,197个字符:

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");}

这个 C 解决方案假设一个 ASCII 字符集,允许我们使用 XOR 镜像技巧。它也非常脆弱——例如,所有输入行的长度必须相同。

它突破了200个字符的大关——但该死的,仍然没有打败那些 Perl 解决方案!

Golfscript-83个字符(我和 strager 的混搭)

新线只是用来包装的

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

Golfscript-107字符

清晰的新线路就在那里

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

它是如何工作的。

第一行算出初始位置和方向。
每当激光打在镜子上,第二条线就会转动。

Golfscript (83个字符)

你好啊,小不点!

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

巨蟒 -152

从一个名为“ 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

若要从 stdin 读取,请将第一行替换为

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

如果需要小写的 true/false,请将最后一行更改为

print`D<5`.lower()

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

PostScript ,359字节

第一次尝试,有很多改进的空间..。

/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 =

F #-454(大约)

比赛有点晚了,但还是忍不住发了我的第二次尝试。

更新 稍作修改。如果发射机被击中,现在正确停止。偷了 Brian 的 IndexOfAny(遗憾的是这行太冗长了)。我实际上还没有设法让 ReadToEnd 从控制台返回,所以我相信这一点..。

我对这个答案很满意,尽管它很短,但仍然相当可读。

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))

我敢打赌,人们等这一天已经等了很久了。(你是什么意思,挑战结束了,没人再关心了?)

请看... 我在这里提出一个解决方案

Befunge-93!

它占用了大量的 973字符(或者 688,如果你足够仁慈地忽略空格,它只用于格式化,在实际代码中没有任何作用)。

警告 : 不久前,我用 Perl 编写了自己的 Befunge-93解释器,不幸的是,我只有这么多时间来测试它。一般来说,我对它的正确性相当有信心,但是它对 EOF 可能有一个奇怪的限制: 因为 Perl 的 <>操作符在文件末尾返回 undef,所以在数字上下文中将其处理为0。对于基于 C 的实现,其中 EOF 具有不同的值(比如 -1) ,这段代码可能无法工作。

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"\:"<"\: "^"   -!#^_-!#^_-!      ^
>                       ^

解释

如果您不熟悉 Befunge 语法和操作,请检查 给你

Befunge 是一种基于堆栈的语言,但是有一些命令允许用户向 Befunge 代码中写入字符。我在两个地方利用了这一点。首先,我将整个输入复制到 Befunge 板上,但是位于实际编写代码的下面几行。(当然,这在代码运行时是不可见的。)

另一个地方靠近左上角:

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

在本例中,我在上面突出显示的区域是存储两个坐标的地方。中间第一列是我存储当前“光标位置”的 x 坐标的地方; 第二列是我存储 y 坐标的地方; 接下来的两列是用来存储激光光源的 x 坐标和 y 坐标,当它被找到的时候; 最后一列(包含‘ a’字符)最终被覆盖以包含当前光束的方向,当光束的路径被跟踪时,它显然会改变。

程序首先将(0,27)作为光标的初始位置。然后每次读取一个字符并将其放置在光标位置; 换行只会导致 y 坐标增加,x 坐标返回到0,就像实际的回车一样。最终解释器读取 undef,并使用0字符值作为输入结束的信号,然后进入激光迭代步骤。当读取激光字符[ < > ^ v ]时,激光字符也被复制到内存存储库(在“ a”字符上) ,它的坐标被复制到左侧的列。

所有这一切的最终结果是,整个文件基本上被复制到 Befunge 代码中,略低于实际的代码。

然后,将光束位置复制回光标位置,并执行以下迭代:

  • 检查当前光束的方向,并适当地增加或减少光标的坐标。(我首先这样做是为了避免一开始就要处理激光束的拐角问题。)
  • 在那个位置读这个角色。
  • 如果字符为“ #”,则在堆栈、打印和结束上放置换行符和“ false”。
  • 将其与所有光束字符[ < > ^ v ]进行比较; 如果有匹配,也打印“ false n”并结束。
  • 如果字符是空格,则清空堆栈并继续。
  • 如果这个字符是一个斜线,把光束的方向放到堆栈上,然后依次与每个方向字符进行比较。当找到一个新方向时,新方向将存储在代码中的同一位置,循环将重复执行。
  • 如果字符是反斜杠,那么基本上要做与上面相同的事情(除了对反斜杠进行适当的映射)。
  • 如果字符是“ x”,我们就已经达到目标。打印“ true n”并退出。
  • 如果字符都不是,打印“ error n”并退出。

如果有足够的需求,我将尝试指出在代码中完成这一切的确切位置。