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

Ну, один ответ, "когда необходимо поддерживать 1.1 или 2.0", так как WPF является частью.NET 3.0. Существуют известные ограничения ОС для WPF, и существует очевидная проблема навыков: если у Вас есть команда разработчиков, которые знают winforms, то может быть легче оказаться устойчивым кодом с winforms. Однако, если Вы пишете много кода UI, вероятно, стоит начать брать WPF в какой-то момент.

WPF также совместно использует много вместе с Silverlight, таким образом, это обладает передаваемыми преимуществами.

152
задан 9 revs, 3 users 72% 28 September 2009 в 06:05
поделиться

28 ответов

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

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

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

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

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

/[\/\\ ]/ && redo

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

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

78
ответ дан 23 November 2019 в 22:07
поделиться

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
ответ дан 23 November 2019 в 22:07
поделиться

Perl 219
Моя версия perl имеет длину 392 342 символа (мне приходилось обрабатывать случай попадания луча в лазер):
Обновление , спасибо Хоббсу за напоминание о tr // , теперь это 250 символов:
Обновление , удаление m в m // изменение двух контуров и дало небольшую экономию; теперь требуется только одно место.
( 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 операторы.

0
ответ дан 23 November 2019 в 22:07
поделиться

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 - верхнее / левое («убывающее»), а нечетные - нижнее / правое («возрастающее»). В соответствии с этим понятием устанавливаются d («направление») и r («ориентация»). Мы индексируем массив указателей z с ориентацией и добавляем направление к полученному целому числу. Направление меняется, только если мы ударим косую черту, в то время как он остается таким же, когда мы наносим обратный слеш. Конечно, когда мы ударяемся о зеркало, мы всегда меняем ориентацию ( r =! R ).

3
ответ дан 23 November 2019 в 22:07
поделиться

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

  puts Board.new.validate(input)

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

3
ответ дан 23 November 2019 в 22:07
поделиться

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
ответ дан 23 November 2019 в 22:07
поделиться

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

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

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

Сжатый:

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

Несжатый (иш):

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

Нет проверки ввода, и плохой ввод может отправить его в бесконечный цикл. Работает правильно с вводом размером не более 99 на 99. Требуется компилятор, который будет связывать стандартную библиотеку без включения каких-либо заголовков. И я думаю, что я закончил, незнакомец значительно опередил меня , даже с его помощью.

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

6
ответ дан 23 November 2019 в 22:07
поделиться

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
4
ответ дан 23 November 2019 в 22:07
поделиться

House of Mirrors

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

Она написана на Scala, open- источник и доступен здесь :

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

6
ответ дан 23 November 2019 в 22:07
поделиться

JavaScript - 265 символов

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

Обновление III - Благодаря предложению strager относительно удаления Math.abs () и помещения переменных в глобальное пространство имен, это в сочетании с некоторой перестановкой присваиваний переменных получило код до 282 символов.

Обновление II - Еще несколько обновлений кода, чтобы удалить использование! = -1, а также улучшенное использование переменных для более длительных операций.

Обновление - Прошел и внес некоторые изменения, создав ссылку на функцию indexOf (спасибо LiraNuna!) И убрав круглые скобки, которые не нужны.

Я впервые играю в гольф код, так что я ' m не уверен, насколько это может быть лучше, любая обратная связь приветствуется.

Полностью свернутая версия:

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>
7
ответ дан 23 November 2019 в 22:07
поделиться

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

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

9
ответ дан 23 November 2019 в 22:07
поделиться

Ruby, 176 символов

x=!0;y=0;e="^v<>#x";b=readlines;b.map{|l|(x||=l=~/[v^<>]/)||y+=1};c=e.index(b[y][x])
loop{c<2&&y+=c*2-1;c>1&&x+=2*c-5;e.index(n=b[y][x])&&(p n==?x;exit);c^='  \/'.index(n)||0}

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

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

c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1

Изменить : Я смог немного сократить приведенное выше:

c<2&&y+=c*2-1;c>1&&x+=2*c-5

Текущее направление лазера c сохраняется следующим образом:

0 => up
1 => down
2 => left
3 => right

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

11
ответ дан 23 November 2019 в 22:07
поделиться

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 для вверх, влево, вправо, вниз.

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

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

Чтобы начать, мы вызываем рекурсивную функцию 'n'

16
ответ дан 23 November 2019 в 22:07
поделиться

Этот был прямым портом решения Брайана на 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));
16
ответ дан 23 November 2019 в 22:07
поделиться

Python

294 277 253 240 232 символа, включая символы новой строки:

(первый символ в строках 4 и 5 - табуляция , а не пробелы)

l='>v<^';x={'/':'^<v>','\\':'v>^<',' ':l};b=[1];r=p=0
while b[-1]:
 b+=[raw_input()];r+=1
 for g in l:
    c=b[r].find(g)
    if-1<c:p=c+1j*r;d=g
while' '<d:z=l.find(d);p+=1j**z;c=b[int(p.imag)][int(p.real)];d=x.get(c,' '*4)[z]
print'#'<c

Я забыл, что в Python даже есть необязательные точки с запятой.

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

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

l = '> v <^'; список лазерных символов. Порядок выбирается так, чтобы индекс символа направления лазера соответствовал степени sqrt (-1)

x = {'/': '^ ', '\\': 'v> ^ < ',' ': l}; таблица преобразования, определяющая, как изменяется направление, когда луч выходит из разных плиток. Плитка - это ключ, а новые направления - это значения.

b = [1]; удерживает доску. Первый элемент равен 1 (оценивается как истина), так что цикл while будет выполняться хотя бы один раз.

r = p = 0 r - текущий номер строки ввода, p - текущее положение лазерного луча.

while b [-1]: остановить загрузку данных платы, когда raw_input возвращает пустую строку

b + = [raw_input ()]; r + = 1 добавить следующую строку ввода на плату и увеличить счетчик строк

для g в l: угадать направление каждого лазера по очереди

c = b [r] .find (g) установить столбец с местоположением лазера или -1, если он не находится на линии (или указывает в другом направлении)

if-1 if мы нашли лазер, затем установите текущее положение p и направление d . d - один из символов в l

После загрузки доски в b , текущее положение p и направление 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 '#' print false, если мы остановились на чем-либо, кроме цели.

]
17
ответ дан 23 November 2019 в 22:07
поделиться

353 символа в Ruby:

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

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

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

223 203 201 символ в Ruby

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

С пробелами:

d = x = y = -1
b = readlines.each { |l|
  d < 0 && (d = "^>v<".index l[x] if x = l.index(/[>^v<]/); y += 1)
}

loop {
  c = b[y += [-1, 0, 1, 0][d]][x += [0, 1, 0, -1][d]]

  c == 47 ? d = [1, 0, 3, 2][d] :
  c == 92 ? d = 3 - d :
  c == 35 ? (p !1; exit) :
  c < ?x ? 0 : (p !!1; exit)
}

Слегка отредактирован:

board = readlines

direction = x = y = -1
board.each do |line|
  if direction < 0
    x = line.index(/[>^v<]/)
    if x
      direction = "^>v<".index line[x]
    end
    y += 1
  end
end

loop do
  x += [0, 1, 0, -1][direction]
  y += [-1, 0, 1, 0][direction]

  ch = board[y][x].chr
  case ch
  when "/"
    direction = [1, 0, 3, 2][direction]
  when "\\"
    direction = 3 - direction
  when "x"
    puts "true"
    exit
  when "#"
    puts "false"
    exit
  end
end
18
ответ дан 23 November 2019 в 22:07
поделиться

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
29
ответ дан 23 November 2019 в 22:07
поделиться

C89 (209 символов)

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

Объяснение

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

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

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

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

Объявление некоторых переменных. * q - текущее местоположение источника света. * G - игровое поле в виде одномерного массива. * p - это текущее место чтения при заполнении G . * w - ширина платы.

main(m){

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

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

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

        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 = pG вместо 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 )

    );
}
39
ответ дан 23 November 2019 в 22:07
поделиться

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

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} на два символа меньше, чем для (;;) {...} и на три символа меньше, чем , а (1) {...} .

и поместите то, что мы только что перезаписали, в сохраненный символ.

redo;

Повторяйте до завершения. {...; redo} на два символа меньше, чем для (;;) {...} и на три символа меньше, чем , а (1) {...} .

и поместите то, что мы только что перезаписали, в сохраненный символ.

redo;

Повторяйте до завершения. {...; redo} на два символа меньше, чем для (;;) {...} и на три символа меньше, чем , а (1) {...} .

75
ответ дан 23 November 2019 в 22:07
поделиться

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!

9
ответ дан 23 November 2019 в 22:07
поделиться

Взвешивание 18203 символов - это решение Python, которое может:

  • справиться с зеркалами вне 'room'
  • вычисляет траекторию, когда нет 'комнаты' на основе 2D-ограничений (в спецификации много говорится о том, что должно быть в 'комнате', но не о том, должна ли эта комната существовать)
  • report назад к ошибкам

Его все еще нужно немного привести в порядок, и я не знаю, диктует ли 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#
#\\^/\\ /  #
##########"

Используемые unittests в разработке:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest

from lasers import *

class TestTileRecognition(unittest.TestCase):
    def test_solid_wall(self):
        self.assertEqual(SOLID_WALL, identify_tile("#"))

    def test_target(self):
        self.assertEqual(TARGET, identify_tile("x"))

    def test_mirror_ne_sw(self):
        self.assertEqual(MIRROR_NE_SW, identify_tile("/"))

    def test_mirror_nw_se(self):
        self.assertEqual(MIRROR_NW_SE, identify_tile("\\"))

    def test_laser_down(self):
        self.assertEqual(LASER_DOWN, identify_tile("v"))

    def test_laser_up(self):
        self.assertEqual(LASER_UP, identify_tile("^"))

    def test_laser_right(self):
        self.assertEqual(LASER_RIGHT, identify_tile(">"))

    def test_laser_left(self):
        self.assertEqual(LASER_LEFT, identify_tile("<"))

    def test_other(self):
        self.assertEqual(None, identify_tile(" "))

class TestReflection(unittest.TestCase):
    def setUp(self):
        self.DIRECTION = LEFT
        self.NOT_DIRECTIO
11
ответ дан 23 November 2019 в 22:07
поделиться

PostScript, 359 bytes

First attempt, lots of room for improvement...

/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 =
5
ответ дан 23 November 2019 в 22:07
поделиться

Golfscript (83 символа)

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

:\'><v^'.{\?}%{)}?:P@=?{:O[1-1\10?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do
9
ответ дан 23 November 2019 в 22:07
поделиться

Python - 152

Считывает ввод из файла с именем "L"

A=open("L").read()
W=A.find('\n')+1
D=P=-1
while P<0:D+=1;P=A.find(">^<v"[D])
while D<4:P+=[1,-W,-1,W][D];D=[D,D^3,D^1,4,5][' \/x'.find(A[P])]
print D<5

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

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

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

print`D<5`.lower()
9
ответ дан 23 November 2019 в 22:07
поделиться

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$

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

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

29
ответ дан 23 November 2019 в 22:07
поделиться

Ruby - 146 символов

A=$<.read
W=A.index('
')+1
until
q=A.index(">^<v"[d=d ?d+1:0])
end
while d<4
d=[d,d^3,d^1,4,5][(' \/x'.index(A[q+=[1,-W,-1,W][d]])or 4)]
end
p 5>d
6
ответ дан 23 November 2019 в 22:07
поделиться

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

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

Обновление немного изменено. Теперь останавливается правильно, если передатчик ударил. Ущипнул идею Брайана для индексаны (позор этой линии настолько многолетнее). На самом деле мне не удалось отработать, как получить Readteend вернуться с консоли, поэтому я принимаю это немного на доверие ...

Я доволен этим ответом, как будто это довольно коротко, это все еще довольно читается.

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))
0
ответ дан 23 November 2019 в 22:07
поделиться

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

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

Befunge-93!

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

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

003pv   >~v>  #v_"a"43g-!#v_23g03p33v>v
>39#<*v   ::   >:52*-!v   >"rorrE",vg2*
######1   >^vp31+1g31$_03g13gp vv,,<15,
    a#3     >0v       vp30+1g30<>,,#3^@
######p $     0vg34"a"<   >       >vp
^<v>  > ^   p3<>-#v_:05g-!|>:15g-!| $
 >     v^     <   <   <   >^v-g52:< $ 
  v _  >52*"eslaf",,vv|-g53:_      v   
  : ^-"#">#:< #@,,,,<<>:43p0 v0 p34< 
  >">"-!vgv<  ^0p33g31p32-1g3<       
 ^     <#g1|-g34_v#-g34_v#-g34"><v^"<<<<
    v!<^<33>13g1v>03g1-v>03g1+03p$v  $$
>^  _#-v 1>g1-1v>+13pv >03p       v  pp
^_:"^"^#|^g30 <3#   $<           $<>^33
 ^!-"<":<>"v"v^># p#$<>            $^44
^      >#^#_ :" "-#v_ ^   >         ^gg
v  g34$<   ^!<v"/":< >$3p$^>05g43p$ ^55
 >,@   |!-"\"  :_$43g:">"-!|>      ^$32
 *v"x":<      >-^    ^4g52<>:"^" -#v_^
 5>-!#v_"ror"vv$p34g51:<>#|  !-"<":<#|
 ^2,,, ,,"er"<>v      #^^#<>05g43p$$^>^
      >52*"eurt",,,,,@>15g4 3p$$$$  ^#
>:"v"\:"<"\: "^"   -!#^_-!#^_-!      ^
               >                       ^

Объяснение

Если вы не знакомы с синтаксисом и работой Befunge, проверьте здесь .

Befunge - это стековый язык, но есть команды, позволяющие записывать символы в код Befunge. Я пользуюсь этим в двух местах.Сначала я копирую весь ввод на доску Befunge, но располагаю на пару строк ниже написанного кода. (Конечно, при запуске кода это никогда не видно.)

Другое место находится рядом с левым верхним:

######
    a#
######

В этом случае область, которую я выделил выше, - это место, где я храню пару координаты. Первый столбец в средней строке - это место, где я храню координату x для текущей «позиции курсора»; во втором столбце я храню координату y; следующие два столбца предназначены для хранения координат x и y источника лазерного луча, если он найден; и последний столбец (с символом «a» в нем) в конечном итоге перезаписывается, чтобы содержать текущее направление луча, которое, очевидно, изменяется по мере отслеживания пути луча.

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

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

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

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

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

36
ответ дан 23 November 2019 в 22:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: