Помощь - Поиск - Пользователи - Календарь
Полная версия: Пройти лабиринт по правилу правой руки.
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи
DarkWishmaster
Есть лабиринт, надо его пройти по правилу правой руки.
вот к примеру
7 9
1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 0 1
1 0 1 0 0 1 0 1 1
1 0 0 0 1 0 0 0 1
1 0 1 0 1 0 1 0 1
1 0 0 0 0 0 1 0 1
1 2 1 1 1 1 1 3 1

1- блок
0 - пусто
2- вход
3- выход
Я знаю как должен вести себя робот, но проблема в том что не могу написать его поведение на Паскале, может вы поможете?
Тут надо помнить в какую сторону смотри робот что-бы можно было знать относительно него какая сторона есть - право а какая -лево
для этого я исп. процедуры:
например если робот смотрит вверх то он идет по x=-1, y=0

    procedure RotD;  //поворачивает направо
        begin
           if (x=0) and (y=1) then begin x:=1; y:=0; exit; end;
            if (x=0) and (y=-1) then begin x:=-1; y:=0; exit; end;
             if (x=-1) and (y=0) then begin x:=0; y:=1; exit; end;
              if (x=1) and (y=0) then begin x:=0; y:=-1; exit; end;
         end;
        procedure RotS; // поворачивает налево
        begin
           if (x=0) and (y=1) then begin x:=-1; y:=0; exit; end;
            if (x=0) and (y=-1) then begin x:=1; y:=0; exit; end;
             if (x=-1) and (y=0) then begin x:=0; y:=-1; exit; end;
              if (x=1) and (y=0) then begin x:=0; y:=1; exit; end;
         end; 


  procedure Move;
    begin
     i:=i+x;
     j:=j+y;
end;
 


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

Ну потом что-бы показать результат для самого короткого пути то все клетки увелечиваем на еденицу если робот по ней прошел, и дальше проходим по меньшим единицам пока доходим до цели.
Freedom
Цитата(DarkWishmaster @ 27.03.2011 18:43) *


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


а что если тупик, то повернуться не один раз налево а два раза?
DarkWishmaster
Цитата(Freedom @ 27.03.2011 18:57) *

а что если тупик, то повернуться не один раз налево а два раза?

Я имел ввиду что если тупик впереди и нельзя повернуть направо то надо налево, извините, исправил.
А если нигде повернуть нельзя то поворачиваем на 180 и придерживаемся правой стены.
DarkWishmaster
Вообщем вот что получилось:

  Program Robot; Uses Crt;
 var a:array[1..100,1..100] of integer;
    i,j,n,m,x,y,flag:integer;  F:text; Q:boolean;
procedure Enter;
       begin
         for i:=1 to n do
          for j:=1 to m do
            if a[i,j]=2 then exit;
       end; {Enter}
    procedure Initiare;
          begin
          assign(F, 'C:\In.TXT');  reset(F);
          read(F, n); readln(F, m);
          for i:=1 to n do   begin
          for j:=1 to m do    begin
          read(F,a[i,j]);
          end;
        readln(F);
      end;
     close(F);
   end; {Initiare}
  procedure Print;
    var i,j:byte;
      begin
        for i:=1 to n do begin
          for j:=1 to m do begin
            write(a[i,j]); end;
             writeln;
              end; {Print} end;
        function Block(i,j:integer):boolean;  // смотрим если можно идти вперёд
           begin
             if (a[i,j]=1) or (i>n) or (j>m) then Block:=True
              else Block:=False;
            end;  (*BlocK*)
         procedure RotD;  // поворот направо
        begin
           if (x=0) and (y=1) then begin x:=1; y:=0; exit; end;
            if (x=0) and (y=-1) then begin x:=-1; y:=0; exit; end;
             if (x=-1) and (y=0) then begin x:=0; y:=1; exit; end;
              if (x=1) and (y=0) then begin x:=0; y:=-1; exit; end;
         end;
        procedure RotS; // поворот налево
        begin
           if (x=0) and (y=1) then begin x:=-1; y:=0; exit; end;
            if (x=0) and (y=-1) then begin x:=1; y:=0; exit; end;
             if (x=-1) and (y=0) then begin x:=0; y:=-1; exit; end;
              if (x=1) and (y=0) then begin x:=0; y:=1; exit; end;
         end;
     procedure Move(var i,j:integer); // шаг вперед
       begin
        i:=i+x;
         j:=j+y;
       end; {move}
     function Exit(i,j:integer):boolean; // проверка на выход
       begin
        if a[i+x,j+y]=3 then Exit:=True else Exit:=False;
      end;
 Begin ClrScr;
   Initiare;
   Enter;
   x:=-1; y:=0;
   flag:=1;
    repeat
 if Block(i+x,j+y)=True then  //если впереди блок тогда 
       begin
       RotD;  // поворачиваем направо
        if Block(i+x,j+y) // если и на право блок то
           then while Block(i+x,j+y)=True do   // поворачиваем налево до тех пор пока найдем свободное пространство
                  RotS
                else Move(i,j); // если после поворота направо нету блока то шаг вперед
       end else Move(i,j); //если впереди нету блока то шаг вперед
    until Exit(i,j)=True;  // выполнять пока не найдем выход
readln;
end.



Для примера массива что я написал выше, он идёт наверх, поворачивает, видет что там тупик, и идёт в обратном направлении пока дойдёт до входа, и опять всё заново sad.gif((

З.Ы , как тут спойлер написать? что-бы можно было свернуть весь код.

Там откуда я взял задачу, она по техники Greedy, и есть Подсказка: Выбирать из Множество [Вниз, Вверх, Направо, Налево] так что-бы робот двигался вдоль одной стены.
TarasBer
Ну и жесть вы понаписали.
Я так и не понял, а где вы храните, в какую сторону смотрит робот?


const 
  dx : array [0 .. 3] of integer = (1, 0, -1, 0);
  dy : array [0 .. 3] of integer = (0, 1, 0, -1);

var 
  robot: record
    x, y, direction: integer;// текущее направление тоже надо помнить
  end;

begin
  ...
  for d := robot.direction - 1 to robot.direction + 2 do begin // перебор по направлениям, начиная с того, что направо от текущего
    robot.direction := (d + 4) mod 4;  // просто переводим счётчик цикла к интервалу 0..3
    if free[x+dx[robot.direction], y+dy[robot.direction]] then break; // если туда можно пойти, значит мы нашли нужное направление, выходим из цикла
  end;
end.



> if Block(i+x,j+y)=True then

Да чего уж там, давай сразу
if true=(true=((Block((i+x+0)*1, (j+y+1-1)*1)=true)=true))

Не надо писать =true, ясно? Это тавтология потому что.
DarkWishmaster
Цитата(TarasBer @ 29.03.2011 9:20) *

Ну и жесть вы понаписали.
Я так и не понял, а где вы храните, в какую сторону смотрит робот?


const 
  dx : array [0 .. 3] of integer = (1, 0, -1, 0);
  dy : array [0 .. 3] of integer = (0, 1, 0, -1);

var 
  robot: record
    x, y, direction: integer;// текущее направление тоже надо помнить
  end;

begin
  ...
  for d := robot.direction - 1 to robot.direction + 2 do begin // перебор по направлениям, начиная с того, что направо от текущего
    robot.direction := (d + 4) mod 4;  // просто переводим счётчик цикла к интервалу 0..3
    if free[x+dx[robot.direction], y+dy[robot.direction]] then break; // если туда можно пойти, значит мы нашли нужное направление, выходим из цикла
  end;
end.



> if Block(i+x,j+y)=True then

Да чего уж там, давай сразу
if true=(true=((Block((i+x+0)*1, (j+y+1-1)*1)=true)=true))

Не надо писать =true, ясно? Это тавтология потому что.


У меня там переменые x и y меняются в зависимости от того куда смотрит робот
например
если x=-1 y=0 тогда когда мы вызываем процедуру Move(i,j); то он идёт наверх на 1 единицу (i-1, j+0)
т.е он смотри на север.

З.Ы если мы делаем так: IF Ok then то оператор сразу понимает что Ok(boolean) должен быть True?
а для False как? If not ok then ?
TarasBer
> У меня там переменые x и y меняются в зависимости от того куда смотрит робот

Я понял. У тебя x, y - это вектор сдвига, а i, j - координаты.
Плохие названия, неговорящие.

Обычно x, y - координаты, а dx, dy - вектор сдвига.
Но в данном случае как раз проще хранить в качестве направления именно число от 0 до 3.

> З.Ы если мы делаем так: IF Ok then то оператор сразу понимает что Ok(boolean) должен быть True?
а для False как? If not ok then ?

Ну да.
Это должно быть очевидно, если понимать, что заголовок оператора if - это любое выражение типа boolean. Это может быть как переменная этого типа ( if b then ... ), так и оператор, возвращающий значение этого типа ( if x>y then ... ).

Добавлено через 1 мин.
> if (a[i,j]=1) or (i>n) or (j>m) then Block:=True
else Block:=False;

Тоже типичный бульщит (bool shit)

А сразу почему не написать

Block := (a[i,j]=1) or (i>n) or (j>m);
DarkWishmaster
Tarasber, спасибо, исправлюсь, пока в привычку не вошло.
DarkWishmaster

Program Robot_Greedy; Uses Crt;
const
  dx : array [0 .. 3] of integer = (1, 0, -1, 0);
  dy : array [0 .. 3] of integer = (0, 1, 0, -1);
type matrice=array[1..100,1..100] of integer;
var
  a,b:matrice;
    i,j,n,m,x,y,d,flag:integer;  F:text;
    Robot: record
      x, y, direction: integer;
          end;
procedure Enter;   {Find Entrance}
       begin
         for i:=1 to n do
          for j:=1 to m do
            if a[i,j]=2 then exit;
       end; {Enter}
    procedure Initiare;
          begin
          assign(F, 'C:\In.TXT');  reset(F);
          read(F, n); readln(F, m);
          for i:=1 to n do   begin
          for j:=1 to m do    begin
          read(F,a[i,j]);
          end;
        readln(F);
      end;
     close(F);
     for i:=1 to n do
      for j:=1 to n do
       if a[i,j]=0  then b[i,j]:=0 else b[i,j]:=-1;   end; {Initiare}
  procedure Print(var a:matrice);
    var i,j:byte;
      begin
        for i:=1 to n do begin
          for j:=1 to m do begin
            write(a[i,j],' '); end;
             writeln;
              end; {Print} end;
      function Free(i,j:integer):boolean;
          begin
           Free:=(a[i,j]=0) and (i<n) and (j<m);
          end;      {Free}
       procedure GO;
         begin
          i:=i+dx[robot.direction];
          j:=j+dy[robot.direction];
          b[i,j]:=b[i,j]+1;
         end;
          procedure Back;
         begin
          i:=i-dx[robot.direction];
          j:=j-dy[robot.direction];
          b[i,j]:=b[i,j]+1;
         end;
       procedure Right90;
         begin
          if robot.direction=3 then robot.direction:=-1;
           robot.direction:=robot.direction+1;
         end;
         procedure Left90;
         begin
          if robot.direction=0 then robot.direction:=4;
           robot.direction:=robot.direction-1;
         end;
Begin ClrScr; {Main Program}
Initiare;
Print(a);
readln;
Enter;
x:=i; y:=j;
robot.direction:=2;
  while Free(i,j) do
     Go;
    while flag=0 do  begin
     Right90;
      Go;
       if  a[i,j]=3 then flag:=1 else
         if not(Free(i,j))then begin
           Back;
            Left90;
             Go;
              if not(free(i,j))then begin
                Back;
                 Left90;
               end
           end
        end;

 Print(b);
readln;
  end. 


  //File In.TXT
7 9
1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 0 0 1
1 0 1 0 0 1 0 1 1
1 0 0 0 1 0 0 0 1
1 0 1 0 1 0 1 0 1
1 0 0 0 0 0 1 0 1
1 2 1 1 1 1 1 3 1
// Matrix B
-1 1  0 -1  1  0  0 1 0 
 0 5  6  4  4  2  3 6 2 
 0 4  4  4  1 -1  2 4 0 
 0 3  2  1 -1  0  1 4 2 
 0 2 -1  0 -1  0 -1 2 1 
 0 2  0  0  0  0 -1 3 1 
 0 0 -1 -1 -1 -1 -1 2 0 





Вот что получилось, робот находит выход, только он идёт по очень длиному пути.
Пробовал использовать матрицу B для генерации ответа ( надо пройти по минимальным числам ), но это не получилось...
-TarasBer-
Так если ты хочешь кратчайший путь, то это тебе не правая рука нужна, а "обход в ширину" (в википедии поищи).
-TarasBer-
> i:=i+dx[robot.direction];
j:=j+dy[robot.direction];


Вот нахрена я заводил поля robot.x и robot.y , если ты всё равно координаты хранишь в i, j?
DarkWishmaster
Цитата(-TarasBer- @ 30.03.2011 18:44) *

> i:=i+dx[robot.direction];
j:=j+dy[robot.direction];
Вот нахрена я заводил поля robot.x и robot.y , если ты всё равно координаты хранишь в i, j?


Мне просто так удобнее, не путаюсь )
-TarasBer-
Через год ты проклянёшь себя при попытке понять, почему i,j - это координаты робота и для чего заведены поля x и y.
Lapp
Нн-дя...
DarkWishmaster, ты извини, но ТАК программировать НЕЛЬЗЯ..
Я впервые вижу такое.. Возвращаемый результат процедуры - параметры цикла!! Где ты это почерпнул?? Я имею в виду сейчас, как ты находишь вход (процедура Enter). Давай разберем. По возрастанию важности ошибок:
1. Зачем делать процедурой кусок кода, который будет использован один раз?
2. Зачем проходить по масиву повторно? Ты мог сделать это при вводе.
3. Нельзя использовать значения параметров цикла вне цикла. Их значения не гарантируются.
4. Параметры цикла должны быть ЛОКАЛЬНЫМИ, то есть описаны внутри процедуры. Только ТР может пропустить такое, всякий нормальный современный компилятор выдаст ошибку.
5. При выполнении п.4 значения i и j будут недоступны в вызывающем модуле.
Я даже и не знаю, что тут еще добавить и какие рекомедации дать. Постарайся сделать выводы сам.
Так, пойду смотреть дальше..

Слушай, DarkWishmaster, ну я же просил тебя уже, кажется: пожалуйста, форматируй код! Ну это же не стихи, где ты можешь писать лесенкой, как тебе вздумается, под Маяковского. Процедура Print - энды пляшут танец с саблями что ли?

Ладно, с меня пока хватит. Я совершенно не понимаю, как можно ТАК УСЛОЖНИТЬ простейшую задачу. Вот тебе код - надеюсь, ты его разберешь. Заметь, что собственно решение занимает в нем 8 строк (в конце).
type
  tDir= array [0..3] of integer;

const
u: tDir = (-1,0,1,0);
v: tDir = (0,1,0,-1);

var
  maze: array of array of byte;
  d,d0,i,j,m,n,x,y,fx,fy: integer;
  s: string;
  f: text;

begin
  // reading the file
  Assign(f,'maze.dat');
  Reset(f);
  n:=0;
  while not EoF(f) do begin
    ReadLn(f,s);
    Inc(n)
  end;
  m:= Length(s);
  SetLength(maze,n+2,m+2);
  for i:=0 to n+1 do for j:=0 to m+1 do maze[i,j]:=1;
  Reset(f);
  for i:=1 to n do begin
    ReadLn(f,s);
    for j:=1 to m do begin
      case s[j] of
        '2': begin
          x:= i;
          y:= j;
        end;
        '3': begin
          fx:= i;
          fy:= j;
        end
      end;
      if s[j]<>'1' then maze[i,j]:= 0
    end
  end;
  Close(f);

  // printing a picture
  for i:=0 to n+1 do begin
    for j:=0 to m+1 do Write(maze[i,j]);
    WriteLn
  end;

  // passing the maze
  d:= 0;
  repeat
    d:=(d+1) mod 4;
    while maze[x+u[d],y+v[d]]<>0 do d:= (d+3) mod 4;
    Inc(x,u[d]);
    Inc(y,v[d]);
    WriteLn(x:4,y:4)
  until (x=fx) and (y=fy);
end.

Пример входного файла:
   1111111
 1  11    
  1  1 11 
1 11 1 1  
   1   1 1
 1111111 1
 1     1 3
   1121111

Размеры прямоугольника - любые. Пробелы (или нули) обязательны (даже в конце строк). В конце файла не должно быть пустых строк!!
TarasBer
> 1. Зачем делать процедурой кусок кода, который будет использован один раз?

Ну это-то как раз правильно.
Lapp
Цитата(TarasBer @ 1.04.2011 9:50) *
> 1. Зачем делать процедурой кусок кода, который будет использован один раз?

Ну это-то как раз правильно.
Ситуации, когда это оправдано, безусловно, бывают, и нередко. Но всегда полезно спросить (себя) - зачем? Так вот, в данном случае я не нахожу ответа на этот вопрос no1.gif. Достаточно было выделить этот кусок комментариями. Это не ошибка в прямом смысле слова. Тут это сыграло даже положительную роль. Как всегда, диалектика: не сделал бы он этого, никто бы сказал ему про остальные ошибки, и они бы остались надолго.. А сейчас, надеюсь, выводы будут сделаны smile.gif.
DarkWishmaster
Спасибо, Lapp и TarasBer, буду исправляться по тихоньку.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.