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


152

Змагання

Найкоротший код за кількістю символів для введення двовимірного представлення дошки та виведення "true" або "false" відповідно до вводу .

Дошка виготовлена ​​з 4-х видів плитки:

 # - A solid wall
 x - The target the laser has to hit
 / or \ - Mirrors pointing to a direction (depends on laser direction)
 v, ^, > or < - The laser pointing to a direction (down, up, right and left respectively)

Є лише один лазер і лише одна мішень . Стіни повинні утворювати суцільний прямокутник будь-якого розміру, де лазер і ціль розміщені всередині. Стіни всередині «кімнати» можливі.

Лазерні знімки і рухаються від свого походження до напрямку, на який він вказує. Якщо лазерний промінь ударить по стіні, він зупиняється. Якщо лазерний промінь потрапить у дзеркало, він відскакує на 90 градусів у напрямку, на яке вказує дзеркало. Дзеркала є двосторонніми, тобто обидві сторони є "відбивними" і можуть відскакувати промінь двома способами. Якщо лазерний промінь потрапляє на лазер ( ^v><), він трактується як стіна (лазерний промінь руйнує промінь, тому він ніколи не потрапить у ціль).

Тестові справи

Вхід:
    ##########
    # / \ #
    # #
    # \ x #
    #> / #
    ########## 
Вихід:
    правда

Вхід:
    ##########
    # vx #
    # / #
    # / #
    # \ #
    ##########
Вихід:    
    помилковий

Вхід:
    ##############
    # #
    #> # #
    # #
    # # x #
    # #
    ##############
Вихід:
    помилковий

Вхід:
    ##########
    # / \ / \ / \ #
    # \\ // \\\ #
    # // \ / \ / \\ #
    # \ / \ / \ / x ^ #
    ##########
Вихід:
    правда

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


84
IMMA CHARGIN 'MAH LAZER!
flafur Waage

37
Це приголомшливо .
GManNickG


49
@GameFreak: Це старіє по- справжньому .
Артелій,

24
Це "^" насправді акула з лазерною лазеркою на голові?
Натан Фегер

Відповіді:


78

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 на 3 дає правильну поведінку для \дзеркала, а XOR'ing на 1 дає правильну поведінку для /дзеркала.

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

Далі, поточна позиція $rоновлюється відповідно до поточного напрямку.

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

Ми призначаємо символ у поточному положенні, щоб $_зручно використовувати оператори відповідності.

/[\/\\ ]/ && redo

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

Обмеження: може не працювати при проблемах з більш ніж 99 стовпцями. Це обмеження можна усунути за рахунок ще трьох символів,


Гаразд, дістався до 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

Перл, 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;);}

Пояснення

Це чудовисько, ймовірно, буде важко дотримуватися, якщо ти не розумієш С. Просто попередження.

#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's.

    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
Тьфу! Я працював над рішенням С, коли в моєму квартирному комплексі звучала пожежна сигналізація. Тепер мене побили. Хороше рішення, хоча.
rlbond

Способи використання тимчасової змінної для етапів свопінгу та заміни / заперечення дозволить заощадити пару символів.
Артелій

@Artelius, Так, я зрозумів це, і ще кілька речей. Дякую.
страгер

1
TCC насправді не любить нетипізовані декларації та помилки з g.c:3: declaration expected:(
Марком Rushakoff,

2
Видалення putsдекларації допомогло, але недостатньо, щоб підвести її під 170. Хоча 209 дуже добре, тому я думаю, що я залишу це. Дякую за допомогу, хлопці. Я дійсно ціную це. =] (Що б детронувати цих відьом Перла!)
страгер

36

Я б сказав, що люди чекали цього протягом LOOOOONG. (Що ви маєте на увазі, виклик закінчений і вже нікого не хвилює?)

Ось ... я тут представляю рішення в

Befunge-93!

Він важить у колосальних 973 символів (або 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, трохи нижче, ніж фактично пройдений код.

Після цього розташування променя копіюється назад у місця курсору, і виконується наступна ітерація:

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

Якщо на нього достатньо попиту, я спробую вказати, де саме в коді все це виконано.


14
+1 - Тільки тому, що це може бути неправильно трактовано як EXE, відкритий у блокноті.
Кайл Розендо

1
Гм ... святий ****. Я поспішив з Befunge, і це дійсно, дуже вражає.
Алмо

Код гольфу на заплутаних мовах ... як арахісове масло та кайєнна!
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

Гольфскрипт - 83 знаки (мішання моїх та страгерських)

Новий рядок якраз тут для обгортання

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

Гольфскрипт - 107 символів

Новий рядок якраз там для наочності

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

Як це працює.

Перший рядок визначає початкове розташування та напрямок.
Друга лінія крокує через поворот, коли лазер потрапляє в дзеркало.


18

353 символів у Рубі:

314 277 символів зараз!

Гаразд, 256 символів у Рубі, і зараз я закінчив. Гарний круглий номер, на якому можна зупинитися. :)

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 (оцінюється як істинний), щоб цикл 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);індекс поточної діаграми напрямку в 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 надрукувати помилково, якщо ми зупинилися на чомусь іншому, крім цілі.



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 для об'єктів:

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 #. х)
Емісвельт

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

Давайте поговоримо через нього рядок за рядком.

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

Далі ми обчислюємо "w", ширину вхідного рядка та "c", вихідну позицію, шляхом індексації до нашого масиву.

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

Індекс-епсилон 'е' та те, що-новий-напрямок-якщо-ми-ми-ударимо-сліш 's' обчислюються таблицею. Наприклад, якщо поточний напрямок 'd' дорівнює 0 (вгору), то перший елемент таблиці каже "-w, 2", що означає, що ми зменшуємо індекс на w, а якщо ми потрапимо на косу рису, новий напрямок дорівнює 2 (справа).

Тепер ми повторюємо наступну функцію 'n' за допомогою (1) наступного індексу ("c + e" - струм плюс epsilon) і (2) нового напрямку, який ми обчислюємо, дивлячись вперед, щоб побачити, що знаходиться в масиві в що наступна комірка. Якщо значок "lookahead" - це коса риса, новий напрямок - "s". Якщо це зворотний нахил, новий напрямок - 3-х (наш вибір кодування 0123 робить цю роботу). Якщо це пробіл, ми просто продовжуємо рухатися в тому ж напрямку "d". І якщо це будь-який інший символ "c", то гра закінчується, друкуючи "true", якщо знаком було "x", а інакше помилковим.

Для того, щоб почати роботу, ми називаємо рекурсивну функцію 'n' з початковою позицією «c» та початковим напрямком (який виконує початкове кодування напрямку в 0123).

Я думаю, що я, напевно, можу все-таки поголити ще декілька персонажів, але я дуже задоволений цим (і 255 - це приємне число).


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

Баш сценарій, щоб показати кольорові повідомлення про помилки:

#!/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 = Список <рядок>; з використанням P = System.Drawing.Point; з використанням L = Список <string>; L r = new L () {"v", "<", ">", "^"}; P p = новий P (); r.ForEach (a => {int c = 0; v.ForEach (s => {c ++ ; якщо (s.IndexOf (a)! = - 1) {pX = s.IndexOf (a); pY = c;}});}); int l = v [0] .Lggth; v - це список <string>, що містить таблицю, і він видає точку, що представляє положення лазера + 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]. довжина;
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!


= О! +1! З повагою мене побили. =]
страгер

2
Тут більшість хороших рішень роблять припущення "всі лінії однакової довжини". Все справедливо в гольфі та війні.
варення

Якщо потрібно, щоб лінії не були однакової довжини, я додав би тестовий випадок для цього. але я чітко сказав, що це було навмисно :)
LiraNuna


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)

Якщо вам потрібна велика істина true / false, змініть останній рядок на

print`D<5`.lower()

Скільки символів займає зміни Trueв trueі Falseдо false? ;-)
моб

Не вдалося видалити 1 символ, змінивши "print D<5" на "print D <5"? Або щось мені не вистачає?
Ponkadoodle

@wallacoloo, звичайно, може. Це потрібно лише для малих істинних / хибних
Джон Ла Руй,

7

JavaScript - 265 символів

Оновлення IV - Коефіцієнти - це буде останній раунд оновлень, якому вдалося зберегти ще пару символів, перейшовши на цикл виконання часу і переписавши рівняння руху.

Оновлення III - Завдяки стратегічній пропозиції щодо видалення 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>

як це приймає вклад? Я хочу перевірити і перевірити це. Крім того, ви можете зберегти багато символів, якщо ви збережете посилання на 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.
користувач1686

6

Будинок дзеркал

Не справжній вступ до виклику, але я написав гру на основі цієї концепції (не надто довго назад).

Він написаний у Scala, з відкритим кодом та доступний тут :

Це робить трохи більше; стосується кольорів та різних типів дзеркал та пристроїв, але версія 0,00001 зробила саме те, що задає цей виклик. Я втратив цю версію, хоча вона ніколи не була оптимізована для підрахунку символів.


Чи вдасться вам завантажити компільовану версію, яка працює під 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");}

Нестиснений (ish):

#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;
s(d,e,Z)
{
  for(;;)
    switch(m[x+=d][y+=e]){
    C'^': 
      R 1==e;
    C'>': 
      R-1==d;
    C'v': 
      R-1==e;
    C'<': 
      R 1==d;
    C'#':
    C'x':
      R 0;
    C 92:
      e=-e;
      d=-d;
    C'/':
      c=d;
      d=-e;
      e=-c;
    }
}
main(){
  while((c=getchar())>0)
    c==10?i=0,j++:
      (c==120?x=i,y=j:i,m[i++][j]=c);
  puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");
}

Немає перевірки вводу, і неправильний вхід може відправити його в нескінченний цикл. Працює належним чином із введенням не більше 99 на 99. Потрібен компілятор, який зв’яже стандартну бібліотеку, не включаючи жодного заголовка. І я думаю, що я закінчую, що більш сильний розтягнення мене змусив бити , навіть з його допомогою.

Я швидше сподіваюся, що хтось продемонструє більш тонкий спосіб виконати завдання. У цьому немає нічого поганого, але це не глибока магія.


У =0глобальних мережах немає потреби, оскільки вони ініціалізовані до 0 за замовчуванням. Замініть символьні константи на їх еквівалент у десяткових. Використовуйте >0замість того, !=EOFщоб перевірити наявність EOF (і \0). Ви, ймовірно, можете #defineвідірвати частину коду, caseяк я зробив з if. Немає необхідності в екстра \nв putsякості putsповинен надрукувати рядок в будь-якому випадку. for(;;)коротше, ніж while(1). Сподіваюся, це допомагає. =]
страгер

@strager: Дякую Я завжди
підходжу

2
"There is no input validation"- Не повинно бути жодного. Щоб зробити це полегшеним для гравців у гольф, вхідні дані вважаються завжди "чистими", якщо не вказано інше.
LiraNuna

@ dmckee, Не хвилюйтесь, професіонали Code 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 =

4

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


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, хоча це стає нудно. Давайте зробимо рішення, яке відображає "true" або "false" під час компіляції xD
Йоханнес Шауб - litb

додано пояснення, оскільки я думаю, що я буду тримати це у цьому :)
Йоханнес Шауб - ліб

2

Groovy @ 279 characers

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це масив масивів символів у кожному рядку, і ви можете прочитати просте regexp та trзаяви.


Порада: ви можете скоротити свій дзеркальний код вгору. $_=$s;tr/^v<>/<>^v/і $_=$s;tr/v^<>/<>^v/відповідно. Крім того , вам не потрібно mін m//.
варення

Вибачте, зробіть це друге$_=$s;tr/v^></<>^v/;
варення,

У вас ще є декілька, if m/.../які могли б if/.../врятувати двох символів естради.
панно

Ви можете використовувати y///замість, tr///щоб зберегти два символи.
Platinum Azure

0

F # - 454 (або наступні)

Трохи запізнився в гру, але не втримаюсь, щоб розмістити свою 2d спробу.

Оновлення змінено незначно. Тепер правильно зупиняється при попаданні передавача. Защипнув ідею Браяна для IndexOfAny (ганьба, що рядок настільки багатослівний). Мені фактично не вдалося розібратися, як змусити ReadToEnd повернутися з консолі, тому я довіряю цьому шматочку ...

Я задоволений цією відповіддю, оскільки він досить короткий, він все ще досить читабельний.

let s=System.Console.In.ReadToEnd()       //(Not sure how to get this to work!)
let w=s.IndexOf('\n')+1                   //width
let h=(s.Length+1)/w                      //height
//wodge into a 2d array
let a=Microsoft.FSharp.Collections.Array2D.init h (w-1)(fun y x -> s.[y*w+x])
let p=s.IndexOfAny[|'^';'<';'>';'v'|]     //get start pos
let (dx,dy)=                              //get initial direction
 match "^<>v".IndexOf(s.[p]) with
 |0->(0,-1)
 |1->(-1,0)
 |2->(1,0)
 |_->(0,1)
let mutable(x,y)=(p%w,p/w)                //translate into x,y coords
let rec f(dx,dy)=
 x<-x+dx;y<-y+dy                          //mutate coords on each call
 match a.[y,x] with
 |' '->f(dx,dy)                           //keep going same direction
 |'/'->f(-dy,-dx)                         //switch dx/dy and change sign
 |'\\'->f(dy,dx)                          //switch dx/dy and keep sign
 |'x'->"true"
 |_->"false"
System.Console.Write(f(dx,dy))

Вони є прикрасою. Перевірте інші мої проблеми, це просто форматування.
LiraNuna

@LiraNuna, добре, як виявляється, ця ітерація все одно їх з'їдає :)
Benjol

Було б добре порівняти з 1-д реалізацією. Просто додайте / віднімайте 1 вліво і вправо і додайте / віднімайте w вгору і вниз. Я б очікував, що ви врятуєте досить багато символів
Джон Ла Руй,

@gnibbler, Брайан вже зробив це, я не впевнений, що міг би його побити, але я можу спробувати.
Бенжол
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.