代码高尔夫:激光

152

挑战

以字符计数最少的代码输入一个2D的棋盘表示,并根据输入输出“true”或“false”

棋盘由4种类型的方块组成:

 # - 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><) 本身,则将其视为墙壁(激光束会摧毁发射器,因此它永远不会击中目标)。

测试用例

输入:
    ##########
    #   / \  #
    #        #
    #   \   x#
    # >   /  #
    ########## 
输出:
    true

输入:
    ##########
    #   v x  #
    # /      #
    #       /#
    #   \    #
    ##########
输出:    
    false

输入:
    #############
    #     #     #
    # >   #     #
    #     #     #
    #     #   x #
    #     #     #
    #############
输出:
    false

输入:
    ##########
    #/\/\/\  #
    #\\//\\\ #
    #//\/\/\\#
    #\/\/\/x^#
    ##########
输出:
    true

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


84
我要开始充电了,准备发射激光! - Ólafur Waage
33
不要交叉激光束。 - GManNickG
9
有史以来最复杂的代码高尔夫? - womp
49
@GameFreak: 这已经真的很陈旧了。 - Artelius
24
那个'^'实际上是一只头上带有激光的鲨鱼吗? - Nathan Feger
显示剩余14条评论
28个回答

78

Perl, 166 160个字符

在这场比赛结束时,这个方案需要166个字符,但A.Rex发现了几种方法来深度缩减6个字符:

Perl,251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 --> 160个字符。

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{99*i+j}保存了在行i、列j处的字符。
%d=split//,'>.^1<2v3' ; ($r)=grep{$d|=$d{$t{$_}}}%t

它搜索%t元素,查找与> ^ <v匹配的字符,并同时将$d设置为0到3之间的值,表示激光束的初始方向。

在主循环的每次迭代开始时,如果光束当前位于镜子上,我们会更新$d。通过异或3可以得到\镜子的正确行为,而异或1可以得到/镜子的正确行为。

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

接下来,根据当前方向,更新当前位置$r
$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}

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

/[\/\\ ]/ && redo

如果我们处于一个空格或镜像字符上,则继续。否则,如果我们在目标上($_ =~ /x/),则终止并返回true,否则返回false
限制:可能无法处理超过99列的问题。为了消除这个限制需要增加3个字符。

5
我可以将99改为1E5,以3个字符为代价使其更加强大稳健。 - mob
2
你最好的解决方案应该放在帖子的顶部,这样更加显眼。 - strager
13
使用正则表达式旋转棋盘?那太酷了。这就像是一个自动的20杆加分。 - mob
1
@mobrule:省六个击键:将第一行重新排序为s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;,将%d=split//,..更改为%d=..=~/./g,并将grep{..}%t更改为grep..,%t - A. Rex
谢谢A. Rex -- 这些是更好的建议。 - mob
显示剩余11条评论

75

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){...} 少三个字符。


4
旋转面板……疯狂。正则表达式……更加疯狂。O_o - strager
4
LiraNuna:我选择把这当成一种赞美。 - hobbs
21
高尔夫比赛结束了。如何使用正则表达式旋转一个二维板?! - Konrad Rudolph
2
我想打败你,但我不知道怎么做。 ;D - strager
13
什么?Perl程序员就像魔法师一样厉害。 - Johannes Schaub - litb
显示剩余8条评论

39

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数组表示游戏板。 * p 是在填充G时的当前读取位置。 * w 是棋盘的宽度。

main(m){

显然的main函数。变量m用于存储移动向量。(作为优化选项它是传递给main函数的一个参数。)

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

循环遍历所有字符,使用p填充G。跳过G[0]以进行优化(在for的第三部分中再次写入p是没有必要的)。

        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 终止循环);否则,不做任何操作(m=m)。
    );
}

8
啊!我正在处理一份C语言的解决方案,突然公寓大楼的火警响起。现在我没能按时完成了。不过解决方案还不错。 - rlbond
悄悄地在 Perl 下面加入了 1。不错。 - dmckee --- ex-moderator kitten
1
TCC实际上不喜欢无类型声明,并且会出现g.c:3: declaration expected的错误:( - Mark Rushakoff
只需满足于这样一个事实,我的程序要依赖于几十兆字节的C代码才能运行,而你的几乎是自包含的。在这种情况下,结果在15%范围内就算很好了。过去一天里,你还比我删除了更多代码。 :) - hobbs
2
删除 puts 的声明有所帮助,但还不足以将其降至 170。209 已经相当不错了,所以我想我会保留它。感谢你们的帮助,我非常感激。 =](为了打败那些 Perl 女巫,我们愿意付出一切!) - strager
显示剩余18条评论

36

我敢打赌人们已经等待这个问题很久了。(你说什么?挑战结束了,没人关心了?)

看啊... 我在这里呈现了一个Befunge-93的解决方案!

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,就像真正的回车一样。最终解释器会读取未定义的值并使用该0字符值来信号输入结束并继续进行激光迭代步骤。当读取到激光字符[<>^v]时,它也会被复制到内存存储库中(覆盖'a'字符),并将其坐标复制到左侧的列中。
所有这些的最终结果是整个文件基本上都被复制到Befunge代码中,离实际遍历的代码稍微有些距离。
之后,光束位置被复制回光标位置,并执行以下迭代:
- 检查当前光束方向并相应地递增或递减光标坐标。(我首先这样做是为了避免处理激光光束在第一次移动时就位于拐角的情况。) - 读取该位置处的字符。 - 如果字符是“#”,则将换行符和“false”放入堆栈中,打印并结束。 - 将其与所有光束字符[<>^v]进行比较;如果有匹配项,则还要打印“false\n”并结束。 - 如果字符为空格,则清空堆栈并继续。 - 如果字符是斜线,将光束方向放入堆栈中,并依次将其与每个方向字符进行比较。找到一个后,在代码的同一位置存储新方向,然后重复循环。 - 如果字符是反斜杠,基本上要执行与上述相同的操作(除了使用反斜杠的适当映射)。 - 如果字符是“x”,我们击中了目标。打印“true\n”并退出。 - 如果字符不是这些字符之一,则打印“error\n”并退出。
如果有足够的需求,我会尝试指出所有这些都在代码的哪个位置完成。

14
+1 - 只是因为它可能被误解为在记事本中打开的exe文件。 - Kyle Rosendo
1
嗯...天啊。我曾经尝试过Befunge,但这真的非常非常令人印象深刻。 - Almo
混淆语言的代码高尔夫就像花生酱和辣椒粉! - wberry

29

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

54
我真的能读懂这个!太神奇了! - Jeff Atwood
17
Java/C#的代码高尔夫计分是按照行数而不是字符数来统计的,这就是它的难点。 - Nathan Feger
3
@strager,三年后当你被聘为维护代码的人,原开发人员早已离开时,这并不令人沮丧。 - Nathan Feger
在Visual Studio 2010中使用F#会失败。Seq.to_list不存在(好的,将其更改为toList),然后第25行存在不完整的模式匹配。 - Russell
2
是的,现在将 to_list 更改为 toList。不完全匹配警告很好;这是代码高尔夫比赛,所以我没有编写像 | _ -> failwith "impossible" 这样的代码。 - Brian
太棒了,谢谢Brian。是我不好,它只是不能在交互模式下运行(console.ReadLine())。 - Russell

29

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$

工作原理。

第一行确定了初始位置和方向。
第二行通过在激光击中镜子时进行转向来执行步骤。


18

在Ruby中有353个字符:

现在只有314个字符了,现在是277个字符!

好的,在Ruby中有256个字符,我完成了。一个不错的数字停下来。 :)

247个字符。我停不下来。

223 203 现在在Ruby中有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

好的,好的...我其实意识到整个变量都是不必要的,因为我只使用了一次。这和其他几个改进将其缩减到了247个字符。 - Paige Ruten
1
没有i++(而是使用i+=1)? - LiraNuna
6
不行。你可以使用++i,但它只是使得它比以前更加正面而已。 - DigitalRoss
你可以通过将 puts "true" 改为 puts !!0,将 puts "false" 改为 puts !0 来节省更多字符。 - Mike Spross
哎呀,没事了。如果我仔细阅读,就会意识到“重构”版本是为了展示可读性而存在的。 - Mike Spross
显示剩余2条评论

17

Python

294 277 253 240 232字符,包括换行符:

(第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还有可选的分号。

它是如何工作的

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

l='>v<^'; 激光符号列表。顺序是为了使激光方向字符的索引对应于sqrt(-1)的幂。

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

b=[1]; 保存棋盘。第一个元素为1(计算为true),以便while循环至少运行一次。

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

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

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

for g in l: 逐个猜测每个激光方向

c=b[r].find(g) 将列设置为激光的位置,如果它不在行中(或指向不同方向),则为-1。

if-1<c:p=c+1j*r;d=g 如果我们找到了激光,则设置当前位置p和方向ddl中的一个字符

将板加载到b后,当前位置p和方向d已设置为激光源的位置和方向。

while' '<d: 空格比任何方向符号的ASCII值都要低,因此我们将其用作停止标志。

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

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

c=b[int(p.imag)][int(p.real)]; 读取当前位置的字符

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

print'#'<c 如果我们停在目标以外的任何内容上,则打印false。


9
p+=1j**z: 这很棒。 - dmckee --- ex-moderator kitten

16

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

让我们逐行解析它。

首先,将所有输入读入一个大的一维数组中(二维数组对于代码竞赛可能不好处理;只需使用一维数组,并添加/减去一行的宽度以移动到上/下一行)。

接下来,通过索引进入数组,计算出输入行的宽度'w'和起始位置'c'。

现在让我们定义'next'函数'n',它接受当前位置'c'和方向'd',其中0、1、2、3分别代表上、左、右、下。

索引-epsilon 'e'和如果我们碰到斜杠应该采取什么新方向 's'是通过表格计算的。例如,如果当前方向'd'为0(上),则表格的第一个元素表示“-w,2”,这意味着我们将索引减少w,如果我们碰到斜杠,则新方向是2(右)。

现在我们用(1)下一个索引(“c + e” - 当前加上 epsilon),和(2)新方向来递归到下一个函数'n',我们通过查看下一个单元格中数组中的内容来计算新方向。如果前瞻字符是斜杠,则新方向是's'。如果是反斜杠,则新方向为3-s(我们选择将0123编码使其起作用)。如果是空格,则我们继续朝着相同的方向'd'前进。如果是其他任何字符'c',则游戏结束,并打印出'true'(如果字符为'x'),否则打印'false'。

为了启动程序,我们使用初始位置'c'和起始方向来调用递归函数'n'(这也对方向进行了初始编码为0123)。

我认为我可能还可以再减少一些字符,但我已经对它感到非常满意(而且255是一个不错的数字)。


16

这是 Brian 解决方案的 C#3 直接移植版本,去掉了控制台交互。 由于不是完整的程序,所以这并不是挑战的一部分,我只是想知道如何在 C# 中表示他使用的一些 F# 构造。

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

8
哇塞,这个例子真是展示了linq和c#的强大。我作为一个狂热的c#粉丝,给它点赞!x) - Emiswelt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接