Код Гольф: Лазеры

152

Соревнование

Самый короткий код за счет символов для ввода двумерного представления доски и вывода «истина» или «ложь» в соответствии с вводом .

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

Контрольные примеры

Входные данные:
    ##########
    # / \ #
    # #
    # \ Икс#
    #> / #
    ########## 
Вывод:
    правда

Входные данные:
    ##########
    # vx #
    # / #
    # / #
    # \ #
    ##########
Вывод:    
    ложный

Входные данные:
    #############
    # # #
    #> # #
    # # #
    # # Икс #
    # # #
    #############
Вывод:
    ложный

Входные данные:
    ##########
    # / \ / \ / \ #
    # \\ // \\\ #
    # // \ / \ / \\ #
    # \ / \ / \ / Х ^ #
    ##########
Вывод:
    правда

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

LiraNuna
источник
84
IMMA CHARGIN 'MAH LAZER!
Олафур Вааге
37
Это потрясающе .
GManNickG
33
НЕ ПЕРЕКРЕСТАЙТЕ
ЛУЧИ
49
@ GameFreak: Это становится действительно старым.
Артелиус
24
Это «^» на самом деле акула с чертовым лазером на голове?
Натан Фегер

Ответы:

78

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

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

На момент окончания конкурса у Solution было 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 на 3 дает правильное поведение для \зеркала, а XOR'ing на 1 дает правильное поведение для /зеркала.

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

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

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

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

/[\/\\ ]/ && redo

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

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

моб
источник
Хорошо, дошло до 323 символов. = D
Страгер
5
Я могу изменить 99 на 1E5, чтобы сделать его очень устойчивым за счет 3 символов.
моб
2
Ваше лучшее решение будет более заметным в верхней части поста.
Стрэджер
13
Но используя регулярные выражения для поворота доски? Это было больно. Это похоже на автоматический бонус 20 ударов.
моб
1
@mobrule: Сохранить шесть штрихов: изменить порядок первой строки как s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;, изменить %d=split//,.." to % d = .. = ~ /./ g , and change grep {..}% t` наgrep..,%t
А. Рекс
75

Perl, 177 символов

Первый разрыв строки может быть удален; два других являются обязательными.

$/=%d=split//,' >/^\v';$_=<>;$s='#';{
y/v<^/>v</?do{my$o;$o.=" 
"while s/.$/$o.=$&,""/meg;y'/\\'\/'for$o,$s;$_=$o}:/>x/?die"true
":/>#/?die"false
":s/>(.)/$s$d{$1}/?$s=$1:1;redo}

Объяснение:

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

Если движущийся вправо луч попадает в {пустое пространство, наклонное зеркало, наклонное зеркало}, то он становится {движущимся вправо пучком, движущимся вниз пучком}. Инициализируйте $/по пути - к счастью, «6» не является допустимым входным символом.

$_ = <>;

Прочитайте доску в $_.

$s="#";

$sявляется символом того, на чем сейчас сидит луч. Поскольку лазерный излучатель следует рассматривать как стену, для начала установите его в качестве стены.

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

Если лазерный луч направлен в любую сторону, кроме правой, поверните его символ, а затем поверните всю доску на место (также поворачивая символы для зеркал). Это поворот на 90 градусов влево, который достигается путем реверсирования рядов при транспонировании строк и столбцов, с немного излишним s///eпобочным эффектом. В гольф-коде tr написано в форме, y'''которая позволяет мне пропустить обратную косую черту.

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

Прекратите с правильным сообщением, если мы поразим цель или стену.

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

Если перед лазером пустое пространство, двигайтесь вперед. Если перед лазером есть зеркало, двигайтесь вперед и вращайте луч. В любом случае, поместите «сохраненный символ» обратно в старое местоположение луча и поместите только что перезаписанное в сохраненный символ.

redo;

Повторите до конца. {...;redo}на два символа меньше for(;;){...}и на три меньше while(1){...}.

хоббс
источник
4
Поворот доски ... Сумасшедший. Regexp ... Безумный. O_o
strager
39
Ты ... Ты монстр!
LiraNuna
4
LiraNuna: Я хочу принять это как комплимент.
Хоббс
21
Гольф окончен. Как вы можете победить вращение 2D доски с регулярными выражениями ?!
Конрад Рудольф
13
WTF? Программисты Perl - волшебники.
Йоханнес Шауб -
39

C89 (209 знаков)

#define M(a,b)*p==*#a?m=b,*p=1,q=p:
*q,G[999],*p=G;w;main(m){for(;(*++p=getchar())>0;)M(<,-1)M
(>,1)M(^,-w)M(v,w)!w&*p<11?w=p-G:0;for(;q+=m,m=*q&4?(*q&1?
-1:1)*(m/w?m/w:m*w):*q&9?!puts(*q&1?"false":"true"):m;);}

объяснение

Этому чудовищу, вероятно, будет трудно следовать, если вы не понимаете C. Просто предупреждение.

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

Этот маленький макрос проверяет, *pравен ли текущий символ ( ) тому, что aнаходится в символьной форме ( *#a). Если они равны, установите вектор движения на b( m=b), отметьте этого персонажа как стену ( *p=1) и установите в качестве начальной точки текущее местоположение ( q=p). Этот макрос включает в себя «еще» часть.

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

Объявите некоторые переменные. * qтекущее местоположение источника света. * Gэто игровое поле в виде одномерного массива. * 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)

    );
}
страгер
источник
8
Тьфу! Я работал над решением C, когда в моем жилом комплексе прозвучала пожарная сигнализация. Теперь я получил удар. Хорошее решение, хотя.
rlbond
Метинкс, использующий временную переменную для ваших шагов подкачки и свопинга / отрицания, сэкономит вам пару символов.
Артелиус
@ Артелиус, да, я это понял и кое-что еще. Спасибо.
Стрэджер
1
TCC фактически не любит нетипизированные объявления и ошибки g.c:3: declaration expected:(
Марк Рушаков
2
Удаление putsдекларации помогло, но не настолько, чтобы довести ее до 170. 209 довольно хорошо, поэтому, я думаю, я оставлю это на этом. Спасибо за помощь ребята. Я очень ценю это. =] (Что-нибудь, чтобы свергнуть этих колдунов Perl!)
Страгер
36

Могу поспорить, что люди ждали этого в течение НЕСКОЛЬКО времени. (Что ты имеешь в виду, вызов закончен, и никого больше не волнует?)

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

Befunge-93!

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

Предостережение : я недавно написал свой собственный интерпретатор Befunge-93 на Perl, и, к сожалению, это все, что у меня действительно было время, чтобы протестировать его. Я достаточно уверен в его правильности в целом, но у него может быть странное ограничение в отношении EOF: поскольку <>оператор Perl возвращает undef в конце файла, это обрабатывается как 0 в числовом контексте. Для реализаций на основе Си, где 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 источника лазерного луча, когда он найден; и последний столбец (с символом «а» в нем) в конечном итоге перезаписывается, чтобы содержать текущее направление луча, которое, очевидно, изменяется при трассировке пути луча.

Программа начинается с размещения (0,27) в качестве начальной позиции курсора. Затем ввод читается по одному символу за раз и помещается в позицию курсора; Символы новой строки просто приводят к увеличению координаты y, а координаты x возвращаются к 0, точно так же, как реальный возврат каретки. В конечном счете интерпретатор читает undef, и это значение 0 символов используется, чтобы сигнализировать об окончании ввода и перейти к этапам итерации лазера. Когда читается символ лазера [<> ^ v], он также копируется в хранилище памяти (над символом «a»), а его координаты копируются в столбцы слева.

Конечным результатом всего этого является то, что весь файл в основном копируется в код Befunge, немного ниже фактического пройденного кода.

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

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

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

Платиновая Лазурь
источник
14
+1 - только потому, что он может быть неверно истолкован как файл EXE, открытый в блокноте.
Кайл Розендо
1
Хм ... святой ****. Я испортил Befunge, и это действительно очень впечатляет.
Almo
Кодируйте гольф на запутанных языках ... как арахисовое масло и кайенский перец!
Wberry
29

F #, 36 строк, очень читабельный

Хорошо, просто чтобы получить ответ там:

let ReadInput() =
    let mutable line = System.Console.ReadLine()
    let X = line.Length 
    let mutable lines = []
    while line <> null do
        lines <- Seq.to_list line :: lines
        line <- System.Console.ReadLine()
    lines <- List.rev lines
    X, lines.Length, lines

let X,Y,a = ReadInput()
let mutable p = 0,0,'v'
for y in 0..Y-1 do
    for x in 0..X-1 do 
        printf "%c" a.[y].[x]
        match a.[y].[x] with 
        |'v'|'^'|'<'|'>' -> p <- x,y,a.[y].[x]
        |_ -> ()
    printfn ""

let NEXT = dict [ '>', (1,0,'^','v')
                  'v', (0,1,'<','>')
                  '<', (-1,0,'v','^')
                  '^', (0,-1,'>','<') ]
let next(x,y,d) =
    let dx, dy, s, b = NEXT.[d]
    x+dx,y+dy,(match a.[y+dy].[x+dx] with
               | '/' -> s
               | '\\'-> b
               | '#'|'v'|'^'|'>'|'<' -> printfn "false"; exit 0
               | 'x' -> printfn "true"; exit 0
               | ' ' -> d)

while true do
    p <- next p    

Образцы:

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

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

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

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

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

##########
#  /    \#
# / \    #
#/    \ x#
#\^/\ /  #
##########
false
Брайан
источник
54
Я МОГУ ЧИТАТЬ ЭТО ОДИН! Чудотворная!
Джефф Этвуд
17
Гольф-код Java / C # считается по строкам, а не по символам. Это гандикап.
Натан Фегер
3
@strager не угнетает в течение 3 лет, когда вы наняты для поддержки кода, а первоначальный разработчик давно ушел.
Натан Фегер,
Это не помогает при использовании F # в Visual Studio 2010. Seq.to_list не существует (хорошо, изменил его на toList), а затем в строке 25 - неполное сопоставление с образцом.
Рассел
2
Да, измените to_list на toList сейчас. Предупреждение о неполном совпадении в порядке; это код гольф, поэтому я не делал код, как: | _ -> failwith "невозможно"
Брайан
29

Golfscript - 83 символа (коллаж из мейджеров и страджеров)

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

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

Golfscript - 107 символов

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

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

Как это устроено.

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

Гнибблер
источник
18

353 символа в рубине:

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

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

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

223 203 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 символов!
LiraNuna
Хорошо, хорошо, хорошо ... Я действительно понял, что вся эта переменная не нужна, поскольку я использую ее только один раз. Это и пара других улучшений сократили его до 247 символов.
Джереми Рутен
1
Нет i++(вместо i+=1)?
LiraNuna
6
Нет. Вы можете сделать ++ i, но это действительно делает его действительно таким же позитивным, как и раньше.
DigitalRoss,
Мне нравится сжатая версия: #; p
SeanJA
17

питон

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-> i ^ 2 = -1, который переместился бы влево на один столбец.

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

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

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

Theran
источник
9
p+=1j**z: Это мило.
dmckee --- котенок экс-модератора
16

Это есть был прямым портом решения Брайана на 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));
оборота Натан Баулч
источник
8
О, мой бог. Какой хороший пример, чтобы продемонстрировать, насколько могущественными стали linq и c #. 1+, потому что я большой фанат C #. x)
Emiswelt
16

F #, 255 символов (и все же довольно читабельно!):

Хорошо, после ночного отдыха я значительно улучшила это:

let a=System.Console.In.ReadToEnd()
let w,c=a.IndexOf"\n"+1,a.IndexOfAny[|'^';'<';'>';'v'|]
let rec n(c,d)=
 let e,s=[|-w,2;-1,3;1,0;w,1|].[d]
 n(c+e,match a.[c+e]with|'/'->s|'\\'->3-s|' '->d|c->printfn"%A"(c='x');exit 0)
n(c,"^<>v".IndexOf a.[c])

Давайте поговорим об этом построчно.

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

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

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

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

Теперь мы вернемся к следующей функции 'n' с (1) следующим индексом ("c + e" - current plus epsilon) и (2) новым направлением, которое мы вычисляем, посмотрев вперед, чтобы увидеть, что находится в массиве в эта следующая клетка. Если заглядывающий символ - косая черта, новое направление - «s». Если это обратная косая черта, новое направление - 3 с (наш выбор кодировки 0123 делает эту работу). Если это пробел, мы просто продолжаем двигаться в том же направлении «д». И если это любой другой символ «c», то игра заканчивается, выводя «true», если char был «x», и 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
оборота Металшарк
источник
6
Лазерная физика диктует, что луч может пересекать сам себя. Приведенный выше комментарий является важной культурной ссылкой.
dmckee --- котенок экс-модератора
5
Черепаха и Заяц подходят к кодированию гольфа. Поставьте что-нибудь с явно слишком большим количеством символов (в 91 раз больше, чем у текущего победителя), но обратите внимание на каждую букву спецификации. Медленный и устойчивый, как правило, дает мне меньше работы по контракту, хотя.
Metalshark
Ваш юнит-тест, кажется, пропускает какую-то часть. Это отключено в "self.NOT_DIRECTIO"
BioGeek
@BioGeek - достигнуть предела того, как длина сообщения;). Кроме того, тесты стиля BASH демонстрируют цветовую подсветку.
Metalshark
11

Рубин, 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 => вверх
1 => вниз
2 => слева
3 => правильно

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

Майк Спросс
источник
9

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

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

нолдорин
источник
Я не уверен, что это короче, но это мой шанс найти лазер и найти ширину: используя L = список <string>; используя P = System.Drawing.Point; используя L = список <string>; L r = новый 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] .Length; v представляет собой List <string>, содержащий таблицу, и он выводит Point, представляющую положение лазера + int, представляющий ширину
RCIX
лучше: используя L = List <string>; 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;
RCIX
4
Проблемы требуют полной программы, а не функции.
Стрэджер
как насчет while(1)
SSpoke
9

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!

кафе
источник
= O! +1! Позволяет мне бить. =]
Страгер
2
Большинство хороших решений здесь делают предположение «все линии имеют одинаковую длину». Все честно в гольфе и войне.
Хоббс
Если бы требовалось, чтобы строки были разной длины, я бы добавил для этого тестовый пример. но я четко сказал, что это было
сделано
9

Golfscript (83 символа)

Привет, Гнибблер!

:\'><v^'.{\?}%{)}?:P@=?{:O[1-1\10?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do
страгер
источник
3
golfscript: perl ~ = 1: 1.7
Джон Ла Рой
9

Питон - 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()
гнибблер
источник
Сколько символов требуется, чтобы измениться Trueна trueи Falseна false? ;-)
моб
Не могли бы вы удалить 1 символ, изменив «печать D<5» на «печать D <5»? Или мне чего-то не хватает?
Ponkadoodle
@wallacoloo, конечно можно. Это нужно только для строчной / истинной / ложной информации
John La Rooy
7

JavaScript - 265 символов

Обновление IV - Вероятность того, что это будет последний раунд обновлений, удалось сохранить еще пару символов, переключившись на цикл выполнения и переписав уравнение движения.

Обновление 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>
Rob Z
источник
как он принимает данные? Я хочу проверить и проверить это. Кроме того, вы можете сохранить много символов, если сохраните ссылку в a.indexOf
LiraNuna
Заменить index != -1с index > 0пожалуйста! (Надеюсь, никто не помещает лазер в верхнем левом углу, поэтому 0не будет возвращен. =]) Вы можете связывать varоператоры или вообще избавляться от них (помещая переменные в глобальное пространство имен). Я думаю Math.abs(m)==1можно заменить на m==-1|m==1. Можно movement = ...; location += movementоптимизировать до location += movement =?
Страгер
@ strager- Только что увидел ваш комментарий, похоже, вы опубликовали его, пока я обновлял код, до 300 символов. Я посмотрю, что я могу сделать с устранением Math.abs ().
rjzii
function(a){return g.indexOf(a)}может быть заменено function(a)g.indexOf(a)в последних версиях JavaScript.
user1686
6

Дом Зеркала

Не факт, что вступление в испытание, но я написал игру, основанную на этой концепции (не так давно).

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

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

HRJ
источник
Можно ли загрузить загруженную версию, которая работает под Windows, без необходимости установки scala?
Милан,
Существует версия с включенными библиотеками Scala. Посмотрите на список загрузок. Но в любом случае, если вы уже установили Scala, я рад, что я заставил вас сделать это :)
HRJ
6

c (K & R) 339 необходимых символов после дополнительных предложений от Страгера.

Мой физик отметил, что операции распространения и отражения не зависят от времени, поэтому эта версия отбрасывает лучи от цели и проверяет, не прибыл ли лазерный излучатель.

Остальная часть реализации очень прямолинейна и более или менее точно взята из моих более ранних усилий.

Сжатый:

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

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

dmckee
источник
Нет необходимости =0использовать глобальные переменные, поскольку они по умолчанию инициализируются в 0. Замените символьные константы их эквивалентами в десятичном виде. Используйте >0вместо того, !=EOFчтобы проверять EOF (и \0). Вы, вероятно, можете удалить #defineчасть кода, caseкак я сделал с if. Нет необходимости в экстра \nв putsкачестве putsдолжен напечатать строку в любом случае. for(;;)короче чем while(1). Надеюсь это поможет. =]
Страгер
@strager: Спасибо. Я всегда прихожу к этому многократно, потому что я так не думаю ...
dmckee --- котенок экс-модератора
2
"There is no input validation"- Там не должно быть никаких. Чтобы упростить игру в гольф, предполагается, что ввод всегда будет «чистым», если не указано иное.
LiraNuna
@ dmckee, не волнуйтесь, мы, профессионалы Code Golf, работаем итеративно. Тем не менее, мы обычно используем некоторые приемы с самого начала (например, половину тех, что я упомянул), но это приходит с опытом. =]
Страгер
Если я не считаю неправильно, в программе 390 символов, а не 380.
Страгер
6

Рубин - 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
Джон Ла Рой
источник
5

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 =
KirarinSnow
источник
4

Хаскелл, 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
comingstorm
источник
3

Я верю в повторное использование кода, я бы использовал один из ваших кодов в качестве API :).

  ставит Board.new.validate (вход)

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

Ришав Растоги
источник
6
это двойной призрак!
Джефф Этвуд
3
Удар вас к этому: p Board.new.validate ввод 26 символов \ o /
Алессандра Перейра
3

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 ++. =]
Страгер
2
@strager, это становится скучно, хотя. Давайте
создадим
добавил объяснение, так как я думаю, что я буду держать это в этом :)
Йоханнес Шауб - Lit
2

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
Преподобный Гонзо
источник
2

C #

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

Сделал пункт, чтобы не смотреть на кого-либо еще перед публикацией. Я уверен, что это может быть немного LINQ. И весь метод FindLaser в читаемой версии кажется мне ужасно подозрительным. Но это работает, и уже поздно :)

Обратите внимание, что читаемый класс включает дополнительный метод, который распечатывает текущую арену при движении лазера.

class L{static void Main(){
A=new Dictionary<Point,string>();
var l=Console.ReadLine();int y=0;
while(l!=""){var a=l.ToCharArray();
for(int x=0;x<a.Count();x++)
A.Add(new Point(x,y),l[x].ToString());
y++;l=Console.ReadLine();}new L();}
static Dictionary<Point,string>A;Point P,O,N,S,W,E;
public L(){N=S=W=E=new Point(0,-1);S.Offset(0,2);
W.Offset(-1,1);E.Offset(1,1);D();
Console.WriteLine(F());}bool F(){
var l=A[P];int m=O.X,n=O.Y,o=P.X,p=P.Y;
bool x=o==m,y=p==n,a=x&p<n,b=x&p>n,c=y&o>m,d=y&o<m;
if(l=="\\"){if(a)T(W);if(b)T(E);if(c)T(S);
if(d)T(N);if(F())return true;}
if(l=="/"){if(a)T(E);if(b)T(W);if(c)T(N);
if(d)T(S);if(F())return true;}return l=="x";}
void T(Point p){O=P;do P.Offset(p);
while(!("\\,/,#,x".Split(',')).Contains(A[P]));}
void D(){P=A.Where(x=>("^,v,>,<".Split(',')).
Contains(x.Value)).First().Key;var c=A[P];
if(c=="^")T(N);if(c=="v")T(S);if(c=="<")T(W);
if(c==">")T(E);}}

Читаемая версия (не совсем финальная версия для гольфа, но та же предпосылка):

class Laser
{
    private Dictionary<Point, string> Arena;
    private readonly List<string> LaserChars;
    private readonly List<string> OtherChars;

    private Point Position;
    private Point OldPosition;
    private readonly Point North;
    private readonly Point South;
    private readonly Point West;
    private readonly Point East;

    public Laser( List<string> arena )
    {
        SplitArena( arena );
        LaserChars = new List<string> { "^", "v", ">", "<" };
        OtherChars = new List<string> { "\\", "/", "#", "x" };
        North = new Point( 0, -1 );
        South = new Point( 0, 1 );
        West = new Point( -1, 0 );
        East = new Point( 1, 0 );
        FindLaser();
        Console.WriteLine( FindTarget() );
    }

    private void SplitArena( List<string> arena )
    {
        Arena = new Dictionary<Point, string>();
        int y = 0;
        foreach( string str in arena )
        {
            var line = str.ToCharArray();
            for( int x = 0; x < line.Count(); x++ )
            {
                Arena.Add( new Point( x, y ), line[x].ToString() );
            }
            y++;
        }
    }

    private void DrawArena()
    {
        Console.Clear();
        var d = new Dictionary<Point, string>( Arena );

        d[Position] = "*";
        foreach( KeyValuePair<Point, string> p in d )
        {
            if( p.Key.X == 0 )
                Console.WriteLine();

            Console.Write( p.Value );
        }
        System.Threading.Thread.Sleep( 400 );
    }

    private bool FindTarget()
    {
        DrawArena();

        string chr = Arena[Position];

        switch( chr )
        {
            case "\\":
                if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
                {
                    OffSet( West );
                }
                else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
                {
                    OffSet( East );
                }
                else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
                {
                    OffSet( South );
                }
                else
                {
                    OffSet( North );
                }
                if( FindTarget() )
                {
                    return true;
                }
                break;
            case "/":
                if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
                {
                    OffSet( East );
                }
                else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
                {
                    OffSet( West );
                }
                else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
                {
                    OffSet( North );
                }
                else
                {
                    OffSet( South );
                }
                if( FindTarget() )
                {
                    return true;
                }
                break;
            case "x":
                return true;
            case "#":
                return false;
        }
        return false;
    }

    private void OffSet( Point p )
    {
        OldPosition = Position;
        do
        {
            Position.Offset( p );
        } while( !OtherChars.Contains( Arena[Position] ) );
    }

    private void FindLaser()
    {
        Position = Arena.Where( x => LaserChars.Contains( x.Value ) ).First().Key;

        switch( Arena[Position] )
        {
            case "^":
                OffSet( North );
                break;
            case "v":
                OffSet( South );
                break;
            case "<":
                OffSet( West );
                break;
            case ">":
                OffSet( East );
                break;
        }
    }
}
Метро Смурф
источник
2
Программа должна принять участие. Чаще всего из stdin.
LiraNuna
0

Perl 219
Мой Perl версия 392 342 символов (я должен был обработать случай луча удара лазера):
Update , спасибо Hobbs за напоминание мне 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операторы.

оборота Дламблин
источник
Совет: вы можете сократить свой зеркальный код вверх. $_=$s;tr/^v<>/<>^v/и $_=$s;tr/v^<>/<>^v/соответственно. Кроме того, вам не нужно mв m//.
Хоббс
Извините, сделайте это вторым$_=$s;tr/v^></<>^v/;
Хоббс
У вас еще есть несколько, if m/.../которые могут if/.../сохранить два символа в поп.
Хоббс
Вы можете использовать y///вместо того, tr///чтобы сохранить два символа.
Платиновая Лазурь
0

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

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

Обновление изменено незначительно. Теперь правильно останавливается, если передатчик попал. Удержал идею Брайана для 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
@LiraNuna, хорошо, как оказалось, эта итерация все равно их съедает :)
Бенджол
Было бы неплохо сравнить с 1-й реализацией. Просто добавьте / вычтите 1 для левого и правого и добавьте / вычтите w для увеличения и уменьшения. Я ожидаю, что вы сэкономите немало символов
Джон Ла Руи,
@gnibbler, Брайан уже сделал это, я не уверен, что смогу победить его, но я мог бы попробовать.
Бенджол