Code Golf: Лазеры

Задача

Самый короткий код по количеству символов для ввода двумерного представления платы и вывода "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><), он рассматривается как стена (лазерный луч разрушает луч и поэтому никогда не попадает в цель).

Тестовые примеры

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

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

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

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

Количество кодов включает ввод/вывод (т.е. полную программу).

Ответ 1

Perl, 166 160 символов

Perl, 251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 → 160 символов.

У решения было 166 ударов, когда этот конкурс закончился, но А. Рекс нашел пару способов сбрить еще 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 для символа, который соответствует > ^ < или v, и одновременно устанавливает $d на значение от 0 до 3, которое указывает начальное направление лазерного луча.

В начале каждой итерации в основном цикле мы обновляем $d, если луч в данный момент находится на зеркале. XOR'ing by 3 дает правильное поведение для зеркала \, а XOR'ing на 1 дает правильное поведение для зеркала /.

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

Затем текущая позиция $r обновляется в соответствии с текущим направлением.

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

Мы назначаем символ в текущей позиции $_ для удобного использования операторов сопоставления.

/[\/\\ ]/ && redo

Продолжайте, если мы находимся на пустое место или символ зеркала. В противном случае мы завершаем true, если мы находимся на цели ($_ =~ /x/) и false в противном случае.

Ограничение: может не работать над проблемами с более чем 99 столбцами. Это ограничение можно удалить за счет еще 3 символов,

Ответ 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" не является допустимым вводом char.

$_ = <>;

Прочитайте плату в $_.

$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){...}.

Ответ 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 является игровым полем как 1D-массив.  * p - текущее место чтения при заполнении G.  * w - ширина доски.

main(m){

Очевидный main. m - переменная, сохраняющая вектор движения. (Это параметр main как оптимизация.)

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

Прокрутите все символы, заполнив G, используя p. Пропустите G[0] как оптимизацию (нет необходимости тратить символ, пишущий p снова в третьей части for).

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

Используйте вышеупомянутый макрос, чтобы определить лазер, если это возможно. -1 и 1 соответствуют соответственно слева и справа и -w и w вверх и вниз.

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

    );
}

Ответ 4

Я бы пообещал, что люди ждали этого за время LOOOOONG. (Что вы имеете в виду, задача решена, и никто больше не заботится?)

Вот... Я здесь представляю решение в

Befunge-93!

Он весит в колоссальных 973 charaters (или 688, если вы достаточно благотворительны, чтобы игнорировать пробелы, которые используются только для форматирования и ничего не делают в реальном коде).

Caveat: Недавно я написал собственный интерпретатор Befunge-93 в Perl, и, к сожалению, это все, что я действительно успел проверить. Я достаточно уверен в своей правильности в целом, но у него может быть странное ограничение по отношению к 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, немного ниже фактического пройденного кода.

Затем местоположение луча копируется обратно в места курсора, и выполняется следующая итерация:

  • Проверьте текущее направление луча и увеличьте или уменьшите координаты курсора соответствующим образом. (Я делаю это сначала, чтобы не иметь дело с угловым корпусом лазерного луча прямо на первом ходу.)
  • Прочитайте символ в этом месте.
  • Если символ "#", поместите новую строку и "ложь" в стек, распечатайте и завершите.
  • Сравните его со всеми символами луча [< > ^ v]; если есть совпадение, также напечатайте "false\n" и закончите.
  • Если символ - это пробел, очистите стек и продолжайте.
  • Если символ - это косая черта, получите направление луча в стек и сравните его с каждым из символов направления по очереди. Когда один найден, новое направление сохраняется в том же месте в коде, и цикл повторяется.
  • Если символ является обратным слэшем, сделайте в основном то же самое, что и выше (за исключением правильного отображения для обратной косой черты).
  • Если символ "x", мы попали в цель. Распечатайте "true\n" и выйдите.
  • Если символ не является ничем, напечатайте "error\n" и выйдите.

Если этого достаточно, я попытаюсь указать, где именно в коде все это выполнено.

Ответ 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

Ответ 6

Golfscript - 83 символа (mashup of my и strager's)

Новая строка только для упаковки

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

Golfscript - 107 символов

Новая строка только для ясности

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

Как это работает.

Первая строка определяет исходное местоположение и направление. Вторая линия проходит через поворот, когда лазер попадает в зеркало.

Ответ 7

353 символов в Ruby:

314 277 символов сейчас!

ОК, 256 символов в Ruby, и теперь я закончил. Хороший круглый номер, чтобы остановиться.:)С >

247 символов. Я не могу остановиться.

223 203 201 символ в Ruby

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

С пробелами:

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

Ответ 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='>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 и направление d. d является одним из символов в l

После загрузки платы в b текущая позиция p и направление d были установлены в соответствии с настройками источника лазера.

while' '<d: пространство имеет меньшее значение ASCII, чем любой из символов направления, поэтому мы используем его как флаг остановки.

z=l.find(d); указатель текущего направления char в строке l. z используется позже для определения направления нового луча с использованием таблицы x и для увеличения положения.

p+=1j**z; увеличивайте позицию с помощью силы i. Например, l.find('<')==2 → я ^ 2 = -1, который перемещается влево один столбец.

c=b[int(p.imag)][int(p.real)]; прочитайте char в текущей позиции

d=x.get(c,' '*4)[z] найдите новое направление для луча в таблице преобразования. Если текущий char не существует в таблице, установите d в пробел.

print'#'<c print false, если мы остановились на чем-либо, кроме целевого.

Ответ 9

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

Проговорите его по строкам.

Во-первых, slurp весь вход в большой одномерный массив (2D-массивы могут быть плохими для кодового гольфа; просто используйте 1D-массив и добавьте/вычтите ширину одной строки для индекса, чтобы перемещать вверх/вниз по строке).

Затем мы вычисляем "w", ширину входной строки и "c", начальную позицию, путем индексирования в наш массив.

Теперь давайте определим "следующую" функцию "n" , которая занимает текущую позицию "c" и направление "d", которое равно 0,1,2,3 для вверх, влево, вправо, вниз.

Индекс-epsilon 'e' и значение what-new-direction-if-we-hit-a-slash 'вычисляются таблицей. Например, если текущее направление "d" равно 0 (вверх), то первый элемент таблицы говорит "-w, 2", что означает, что мы уменьшаем индекс на w, и если мы нажмем на косую черту, новое направление равно 2 (справа).

Теперь мы возвращаемся к следующей функции "n" с (1) следующим индексом ( "c + e" - current plus epsilon) и (2) новым направлением, которое мы вычисляем, заглядывая вперед, чтобы увидеть, что массив в следующей ячейке. Если lookahead char является косой чертой, новое направление - 's'. Если это обратная косая черта, новое направление составляет 3 с (наш выбор кодировки 0123 делает эту работу). Если это пространство, мы просто продолжаем идти в том же направлении 'd'. И если это любой другой символ "c", то игра заканчивается, печатая "true", если char был "x" и false в противном случае.

Чтобы отбросить вещи, мы вызываем рекурсивную функцию "n" с начальной позицией "c" и начальным направлением (которое делает начальное кодирование направления на 0123).

Я думаю, что, возможно, я все еще сбрил еще несколько персонажей, но я очень доволен этим (и 255 - хорошее число).

Ответ 10

Этот был прямым портом решения Брайана для С# 3, минус консольные взаимодействия. Это не вход в вызов, так как это не полная программа, мне просто интересно, как некоторые из конструкций 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));

Ответ 11

Взвешивание в 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 origin to the direction it 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 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 position
        beam_pos[axis] += offset

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

            #Get the beam 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 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 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()

A bash script, чтобы показать отчет об ошибках цвета:

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

Unittests, используемые в разработке:

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

Ответ 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}

Я использовал простую машину состояний (как и большинство плакатов), ничего необычного. Я просто продолжал стирать его, используя каждый трюк, о котором я мог думать. Побитовое XOR, используемое для изменения направления (сохраненное как целое число в переменной c), было большим улучшением по сравнению с условностями, которые я имел в более ранних версиях.

У меня есть подозрение, что код, который увеличивает x и y, может быть короче. Вот раздел кода, который выполняет приращение:

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

Код полагается на этот факт, чтобы увеличить x и y на правильную сумму (0, 1 или -1). Я попытался перестроить, какие числа отобразятся в каждом направлении, ища договоренность, которая позволила бы мне делать побитовые манипуляции, чтобы увеличить значения, потому что я чувствую, что это будет короче, чем арифметическая версия.

Ответ 13

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

Основная трата символов, похоже, заключается в нахождении ширины карты и положения источника лазера. Любые идеи, как сократить это?

Ответ 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!

Ответ 15

Golfscript (83 символа)

Здравствуйте, gnibbler!

:\'><v^'.{\?}%{)}?:[email protected]=?{: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

Для чтения из stdin замените первую строку этим

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

Если вам нужна строчная истина/ложная строчка, последняя строка в

print`D<5`.lower()

Ответ 17

JavaScript - 265 символов

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

Ответ 18

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. Требуется компилятор, который свяжет стандартную библиотеку без включения любого из заголовков. И я думаю, что я закончил, Страгр меня сильно ударил, даже с его помощью.

Я скорее надеюсь, что кто-то продемонстрирует более тонкий способ выполнить задачу. В этом нет ничего плохого, но это не глубокая магия.

Ответ 19

Дом Зеркалов

Не настоящий вход в вызов, но я написал игру, основанную на этой концепции (не слишком давно).

Он написан в Scala, с открытым исходным кодом и доступен здесь:

Это немного больше; имеет дело с цветами и различными типами зеркал и устройств, но версия 0.00001 сделала именно то, что задает этот вызов. Я потерял эту версию, хотя она никогда не была оптимизирована для символьного числа.

Ответ 20

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

Ответ 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:).

  puts Board.new.validate(input)

32 символа \o/... wohoooo

Ответ 24

С++: 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).

Ответ 25

С#

1020 символов.
1088 символов (добавлен ввод с консоли).
925 символов (реорганизованные переменные).
875 символов (удален избыточный словарь инициализатор, изменен на двоичный и операторы)

Сделал точку, чтобы не смотреть на кого-либо еще до публикации. Я уверен, что это может быть LINQ'd немного. И весь метод 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;
        }
    }
}

Ответ 26

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

Ответ 27

Perl 219
Моя версия perl 392 342 символа длиной (мне пришлось обработать случай попадания луча на лазер):
Обновите, спасибо Хоббс за то, что напомнили мне tr//, теперь 250 символов:
Обновление, удалив m в m//, изменение двух циклов while принесло несколько сбережений; там теперь требуется только одно место.
(L:it;goto L имеет ту же длину, что и do{it;redo}):

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

Я немного побрил, но он едва просто конкурирует с некоторыми из них, хотя и поздно.
Это выглядит немного лучше:

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

Ответ 28

F # - 454 (или около того)

Бит поздно в игре, но не может устоять перед отправкой моей 2-й попытки.

Обновить немного. Теперь прекращается правильно, если передатчик попал. Идея Pinched 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))