【问题标题】:Code Golf: Lasers代码高尔夫:激光
【发布时间】:2010-12-01 14:08:45
【问题描述】:

挑战

按字符计数的最短代码输入棋盘的 2D 表示,并根据输入输出“真”或“假”

棋盘由 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&gt;&lt;) 本身,则会将其视为一堵墙(激光束会破坏光束器,因此它永远不会击中目标)。

测试用例

输入: ########## # / \ # ## # \ X# # > / # ########## 输出: 真的 输入: ########## #vx# # / # # /# # \ # ########## 输出: 错误的 输入: ############# ### # > # # ### # # X # ### ############# 输出: 错误的 输入: ########## #/\/\/\ # #\\//\\\ # #//\/\/\\# #\/\/\/x^# ########## 输出: 真的

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

【问题讨论】:

  • IMMA CHARGIN' MAH LAZER!
  • 太棒了
  • 不要越过横梁
  • @GameFreak:这已经真的老了。
  • 那个“^”真的是头上戴了个怪胎激光的鲨鱼吗?

标签: language-agnostic code-golf rosetta-stone


【解决方案1】:

Perl,166 160 个字符

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

本次比赛结束时,Solution 有 166 个笔划,但 A. Rex 找到了几种方法可以再刮掉 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{99*i+j}i 行、j 列保存字符。那么,

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

它在%t 的元素中搜索与&gt; ^ &lt;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 个字符为代价来消除此限制,

【讨论】:

  • 好的,到了 323 个字符。 =D
  • 我可以将 99 更改为 1E5 以使其非常健壮,但会牺牲 3 个字符。
  • 您的最佳解决方案会在帖子顶部更明显。
  • 但是使用正则表达式来旋转棋盘?那是病了。这就像一个自动的 20 杆奖金。
  • @mobrule:保存六笔:将第一行重新排序为s!.!$t{$s++}=$&amp;!ge,$s=$r+=99for&lt;&gt;;,更改%d=split//,.." to %d=..=~/./g, and change grep{..}%t`到grep..,%t
【解决方案2】:

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

【讨论】:

  • 旋转板子...疯狂。正则表达式...更疯狂。 O_o
  • LiraNuna:我选择把它当作一种恭维。
  • 高尔夫结束了。你怎么能打败用正则表达式旋转二维板?!
  • 这个解决方案正确吗?我在第一个测试用例中得到了 false 的输出。
  • wtf? perl 程序员是巫师。
【解决方案3】:

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 是一维数组的游戏板。 * p 是填充G 时的当前读取位置。 * w 是板子的宽度。

main(m){

很明显mainm 是一个存储运动矢量的变量。 (这是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 终止循环)。否则,什么都不做(noop;m=m

    );
}

【讨论】:

  • 呃!当我的公寓大楼里的火警响起时,我正在研究 C 解决方案。现在我被打败了。不错的解决方案。
  • 我认为在交换和交换/否定步骤中使用临时变量可以节省几个字符。
  • @Artelius,是的,我意识到了这一点,还有其他一些事情。谢谢。
  • TCC 实际上不喜欢 g.c:3: declaration expected 的无类型声明和错误:(
  • 删除puts 的声明有所帮助,但不足以使其低于 170。不过,209 相当不错,所以我想我会保留它。谢谢你们的帮助,伙计们。对此,我真的非常感激。 =](任何可以推翻那些 Perl 女巫的东西!)
【解决方案4】:

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

看……我在这里提出了一个解决方案

Befunge-93!

它的重量高达 973 个字符(或 688 如果您足够慈善以忽略空格,它仅用于格式化并且在实际代码中没有任何作用) .

警告:不久前,我用 Perl 编写了自己的 Befunge-93 解释器,不幸的是,这就是我真正有时间测试它的全部内容。一般来说,我对它的正确性有相当的信心,但它可能对 EOF 有一个奇怪的限制:由于 Perl 的 &lt;&gt; 运算符在文件末尾返回 undef,因此在数字上下文中将其处理为 0。对于 EOF 具有不同值(例如 -1)的基于 C 的实现,此代码可能不起作用。

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 语法和操作,请查看here

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”并退出。

如果有足够的需求,我会尝试指出代码中的确切位置。

【讨论】:

  • +1 - 只是因为它可能被误解为在记事本中打开的 EXE。
  • 嗯...神圣的****。我搞砸了 Befunge,这真的非常非常令人印象深刻。
  • 用混淆的语言编写高尔夫代码......比如花生酱和辣椒!
【解决方案5】:

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

【讨论】:

  • 我真的能读懂这个!太神奇了!
  • Java/C# 代码高尔夫是按行而不是字符计算的。这就是障碍。
  • 在 Visual Studio 2010 中使用 F# 失败。Seq.to_list 不存在(好的,将其更改为 toList),然后是第 25 行,不完整的模式匹配。
  • 是的,现在将 to_list 更改为 toList。不完全匹配警告很好;这是代码高尔夫,所以我没有做如下代码:| _ -> 以“不可能”失败
【解决方案6】:

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$

它是如何工作的。

第一行计算出初始位置和方向。
每当激光击中镜子时,第二行就会逐步转向。

【讨论】:

    【解决方案7】:

    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
    

    【讨论】:

    • 但是...您可以将 ch 重命名为 C 或任何其他 1 个字符的字母以节省 2 个字符!
    • 好吧好吧……我实际上意识到整个变量是不必要的,因为我只使用了一次。这个和其他几个改进将它减少到 247 个字符。
    • 没有i++(而不是i+=1)?
    • 不。你可以做 ++i,但它只会让它真的像以前一样积极。
    • 我喜欢压缩版:#;p
    【解决方案8】:

    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='&gt;v&lt;^'; 激光符号列表。选择顺序使得激光方向字符的索引对应于 sqrt(-1) 的幂

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

    b=[1]; 持有董事会。第一个元素是 1(评估为 true),因此 while 循环将至少运行一次。

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

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

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

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

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

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

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

    while' '&lt;d: 空格的 ASCII 值低于任何方向符号,因此我们将其用作停止标志。

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

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

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

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

    print'#'&lt;c 如果我们停在目标以外的任何地方,则打印 false。

    【讨论】:

    • p+=1j**z:太好了。
    【解决方案9】:

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

    【讨论】:

    • 天啊。这是一个很好的例子来展示强大的 linq 和 c# 已经变得多么强大。 1+ 因为我是 C# 的忠实粉丝。 x)
    【解决方案10】:

    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。

    index-epsilon 'e' 和 what-new-direction-if-we-hit-a-slash 's' 由表格计算。例如,如果当前方向“d”为 0(向上),则表的第一个元素为“-w,2”,这意味着我们将索引减少 w,如果我们点击斜线,则新方向为 2 (右)。

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

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

    我想我可能还可以再剪掉几个字符,但我对这样的结果很满意(255 是个不错的数字)。

    【讨论】:

      【解决方案11】:

      18203 个字符是一个 Python 解决方案,它可以:

      • 应对外部镜子 “房间”
      • 根据 2D 限制计算没有“房间”时的轨迹(规范说了很多关于“房间”中必须有什么但如果房间必须存在则没有)
      • 报告错误

      它仍然需要稍微整理一下,我不知道 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
      

      【讨论】:

      • 激光物理规定光束可以穿过自身。上面的评论是重要的文化参考。
      • 乌龟和野兔编码高尔夫的方法。提供明显太多字符的东西(比当前获胜者多 91 倍),但要注意规范的每个字母。不过,缓慢而稳定的工作通常会减少我的合同工作量。
      • 您的单元测试似乎缺少某些部分。它在“self.NOT_DIRECTIO”处被切断
      • @BioGeek - 达到帖子长度的限制;)。除了 BASH 样式测试之外,还会显示颜色突出显示。
      【解决方案12】:

      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}
      

      我使用了一个简单的状态机(就像大多数海报一样),没什么特别的。我只是用我能想到的每一个技巧不断地削减它。用于改变方向的按位异或(以整数形式存储在变量 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 => 向上 1 => 向下 2 => 左 3 => 对

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

      【讨论】:

        【解决方案13】:

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

        chars 的主要浪费似乎在于寻找地图的宽度和激光源的位置。任何想法如何缩短这个?

        【讨论】:

        • 我不确定这是否更短,但这是我寻找激光和寻找宽度的机会:使用 L=List;使用 P=System.Drawing.Point;使用 L=List;L r=new L(){"v","","^"};P p=new P();r.ForEach(a =>{int c = 0;v .ForEach(s =>{c++;if(s.IndexOf(a)!=-1){pX=s.IndexOf(a);pY= c;}});});int l=v[0] 。长度; v 是一个包含表格的 List,它输出一个表示激光位置的 Point + 一个表示宽度的 int
        • 更好:使用 L=List;L l=new L(4){"v", "", "^" };var point=new{x =0,y=0};int c=0;l.ForEach(a=>{m.ForEach(s=>{if(s.IndexOf(a)!=-1){point=new{x=s .IndexOf(a),y=c};}});c++;});int w=m[0].Length;
        • Problems 要求提供完整的程序,而不是函数。
        • while(1)怎么样
        【解决方案14】:

        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 解决方案!

        【讨论】:

        • =哦! +1!很高兴打败我。 =]
        • 这里最好的解决方案是假设“所有行的长度相同”。高尔夫和战争都是公平的。
        • 如果需要行的长度不同,我会为其添加一个测试用例。但我清楚地说这是故意的:)
        【解决方案15】:

        Golfscript(83 个字符)

        你好,咬人!

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

        【讨论】:

          【解决方案16】:

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

          要从标准输入读取替换第一行

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

          如果你需要小写的真/假,把最后一行改成

          print`D<5`.lower()
          

          【讨论】:

          • True 更改为true 和将False 更改为false 需要多少个字符? ;-)
          • 您不能通过将“printD&lt;5”更改为“print D
          • @wallacoloo,当然可以。只有小写的真/假才需要
          【解决方案17】:

          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}
          

          带有 cmets 的原始版本:

          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>
          

          【讨论】:

          • 如何输入?我想测试并验证这一点。此外,如果您将 ref 保存到 a.indexOf,您可以节省很多字符
          • 请将index != -1 替换为index &gt; 0! (希望没有人将 lazer 放在左上角,因此不会返回 0。=])您可以链接 var 语句或完全摆脱它们(将变量放在全局命名空间中)。我认为Math.abs(m)==1 可以替换为m==-1|m==1movement = ...; location += movement 可以优化为 location += movement = 吗?
          • @strager- 刚刚看到你的评论,看起来你在我更新代码时发布了它,最多 300 个字符。我会看看消除 Math.abs() 能做些什么。
          • function(a){return g.indexOf(a)} 在最近的 JavaScript 版本中可以替换为 function(a)g.indexOf(a)
          【解决方案18】:

          镜屋

          不是真正的挑战入口,但我根据这个概念写了一个游戏(不久前)。

          它是用 Scala 编写的,开源且可用here

          它做得更多;处理颜色和各种类型的镜子和设备,但版本 0.00001 正是这个挑战所要求的。不过我已经丢失了那个版本,而且它从来没有针对字符数进行优化。

          【讨论】:

          • 您是否可以上传在 Windows 下运行的编译版本而无需安装 scala?
          • 有一个包含 Scala 库的版本。查看下载列表。但无论如何,如果你现在已经安装了 Scala,我很高兴我能让你这样做 :)
          【解决方案19】:

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

          未压缩(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");
          }
          

          没有输入验证,错误的输入会将其送入无限循环。输入不大于 99 x 99 时正常工作。需要一个编译器,该编译器将链接标准库而不包括任何头文件。我想我已经完成了,strager has me beat 相当长的一段时间,即使有他的帮助。

          我宁愿希望有人展示一种更巧妙的方式来完成这项任务。这并没有错,但它并不是深奥的魔法。

          【讨论】:

          • 全局变量上不需要=0,因为它们默认初始化为0。用十进制等效的字符常量替换字符常量。使用&gt;0 而不是!=EOF 来检查EOF(和\0)。你可能可以#define 去掉case 中的一些代码,就像我对if 所做的那样。不需要puts 中的额外\n,因为puts 无论如何都必须打印换行符。 for(;;)while(1) 短。希望这可以帮助。 =]
          • @strager:谢谢。我总是反复尝试这些,因为我不这么认为......
          • "There is no input validation" - 不应该有。为了方便高尔夫球手,除非另有说明,否则输入假定始终是“干净的”。
          • @dmckee,别担心,我们的 Code Golf 职业选手也在迭代工作。然而,我们通常从一开始就使用一些技巧(比如我提到的一半),但这是有经验的。 =]
          • 除非我数错,否则程序是 390 个字符,而不是 380 个。
          【解决方案20】:

          Ruby - 146 个字符

          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
          

          【讨论】:

            【解决方案21】:

            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 =
            

            【讨论】:

              【解决方案22】:

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

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

              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
              

              【讨论】:

                【解决方案23】:

                我相信代码重用,我会使用您的代码作为 API :)。

                将 Board.new.validate(input)

                32 个字符 \o/...呜呜呜

                【讨论】:

                • 打败你:p Board.new.validate 输入 26 个字符 \o/
                【解决方案24】:

                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)
                

                然后我们查看找到的方向并将其存储在i 中。 i 的偶数值是顶部/左侧(“递减”),奇数值是底部/右侧(“递增”)。根据该概念,设置了d(“方向”)和r(“方向”)。我们用方向索引指针数组z,并将方向添加到我们得到的整数上。只有当我们击中斜线时方向才会改变,而当我们击中反斜线时方向保持不变。当然,当我们碰到镜子时,我们总是会改变方向(r = !r)。

                【讨论】:

                • 你让我自己做 C++ 解决方案。 =]
                • @strager,不过这越来越无聊了。让我们做一个在编译时显示“真”或“假”的解决方案xD
                • 添加了解释,因为我认为我会保留它:)
                【解决方案25】:

                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
                

                【讨论】:

                  【解决方案26】:

                  C#

                  1020 个字符。
                  1088 个字符(从控制台添加输入)。
                  925 个字符(重构变量)。 875 个字符(删除了多余的 Dictionary 初始化程序;更改为 Binary & 运算符)

                  发帖前请注意不要看其他人的帖子。我敢肯定它可能是LINQ了一点。可读版本中的整个 FindLaser 方法对我来说似乎非常可疑。但是,它有效,但已经晚了:)

                  请注意,可读类包括一个附加方法,当激光移动时打印出当前 Arena。

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

                  【讨论】:

                  • 程序应该接受输入。最常见于标准输入。
                  【解决方案27】:

                  Perl 219
                  我的 perl 版本是 392 342 个字符长(我必须处理光束击中激光的情况):
                  更新,感谢 Hobbs 提醒我 @987654321 @,现在是 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 是每行中的字符数组,并且可以阅读简单的正则表达式和tr 语句,这应该是不言自明的。

                  【讨论】:

                  • 提示:你可以缩短你的镜像代码。 $_=$s;tr/^v&lt;&gt;/&lt;&gt;^v/$_=$s;tr/v^&lt;&gt;/&lt;&gt;^v/ 分别。另外,您不需要m// 中的m
                  • 抱歉,让第二个$_=$s;tr/v^&gt;&lt;/&lt;&gt;^v/;
                  • 您还有几个if m/.../,可能是if/.../,每次弹出两个字符。
                  • 你可以用y///代替tr///来保存两个字符。
                  【解决方案28】:

                  F# - 454(或附近)

                  游戏有点晚了,但忍不住发布我的 2d 尝试。

                  更新略有修改。如果发射器被击中,现在会正确停止。扼杀了布赖恩对 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))
                  

                  【讨论】:

                  • 它们是装饰品。检查我的其他挑战,这只是一个格式化的事情。
                  • @LiraNuna,好吧,事实证明,这个迭代无论如何都会吃掉它们:)
                  • 与一维实现进行比较会很好。左右加/减1,上下加/减w。我希望你能节省不少字符
                  • @gnibbler,Brian 已经做到了,我不确定我能不能打败他,但我可能会试一试。
                  猜你喜欢
                  • 2010-12-17
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-28
                  • 2011-02-08
                  • 2010-10-24
                  相关资源
                  最近更新 更多