illyge - Программирование, извращение [entries|archive|friends|userinfo]
illyge

[ userinfo | ljr userinfo ]
[ archive | journal archive ]

Программирование, извращение [Jun. 26th, 2009|01:34 am]
Previous Entry Add to Memories Tell A Friend Next Entry
Вот задача:
Поиск пути, ходим по клеткам, с одной клетки восемь направлений: вверх-вниз, вправо-влево и промежуточные.
Рекурсивный обход. Начинать надо с наиболее вероятного направления, постепенно перебирать от него к противоположному.
Направление кодируется двумя значениями: dC - горизонтальное и dR - вертикальное.
Т.е. например dC = -1, dR = 0 означает строго влево. А, к примеру, dC = 1, dR = -1 означает по диагонали вверх-вправо.
Если наиболее вероятное направление, к примеру, dC = 0, dR = -1 (т.е. строго вверх), то перебирать надо так:

dC = 0, dR = -1
dC = -1, dR = -1
dC = 1, dR = -1
dC = -1, dR = 0
dC = 1, dR = 0
dC = -1, dR = 1,
dC = 1, dR = 1
dC = 0, dR = 1

Что я сделал:



void MovementGrid::findPath(int col, int row, BoardObject *pObject) {
// тут всякий код
......
// тут заканчивается всякий код

static int circle[] = {0, 10, 20, 21, 22, 12, 2, 1};
// массив circle[] - это закодированные направления. Функция кодирования направлений показана ниже - toCircle(). Обратные ей функции toDC() и toDR()


int circleValue = toCircle(dC, dR);

int circleCoord = 0;
for (circleCoord=0; circleCoord<8; circleCoord++) {
if (circleValue == circle[circleCoord])
break;
}

for (int i = 0; i<5; i++) {
int c = (circleCoord+i)%8;
findPath (col + toDC(circle[c]), row + toDR(circle[c]), pObject);

if (i!=0&&i!=4) {
c = (circleCoord - i + 8)%8;
findPath (col + toDC(circle[c]), row + toDR(circle[c]), pObject);
}
}
}


int toCircle (int dC, int dR) {
return (dC+1)*10 + dR+1;
}

int toDC (int circle) {
return circle/10 - 1;
}

int toDR (int circle) {
return circle%10 - 1;
}




Выглядит, на мой вкус, монструозненько. Интересно, это решается как-нибудь более красиво?
LinkLeave a comment

Comments:
[User Picture]
From:[info]a_karpov
Date:June 25th, 2009 - 11:07 pm
(Link)
я что-то не понял - а в чем, собственно, задача? есть доска, есть клетки, можно ходить... а куда, зачем?
[User Picture]
From:[info]illyge
Date:June 25th, 2009 - 11:12 pm
(Link)
Ну, вообще задача поиска пути. Типа, юнит в игре должен найти себе путь, обходя препятствия. Я ее решаю обычным рекурсивным обходом.
Вот возникла такая подзадача этой задачи: реализовать перебор направлений на отдельном шаге рекурсии, причем не от балды, а отталкиваясь от наиболее вероятного направления, которое, разумеется, заранее не известно.
[User Picture]
From:[info]a_karpov
Date:June 25th, 2009 - 11:17 pm
(Link)
вот я и не пойму - поиск пути куда? и какие бывают препятствия? то есть я честно не вижу достаточной информации пока для того, чтобы говорить о решении задачи =)

к примеру, если бы клетки имели координаты и вес, и исходные данные были бы координаты двух клеток, можно было бы говорить, к примеру, о задаче поиска пути из одной в другую с минимальной стоимостью. А пока я не занудничаю, а правда не понимаю, что за задача решается =)
[User Picture]
From:[info]illyge
Date:June 25th, 2009 - 11:23 pm
(Link)
Короче, смотри :)
Смысл в том, что задача собственно поиска пути в этом посте не решается. Здесь решается задача перебора направлений, и только :)
Ну представь себе, что у тебя есть клетка. Вокруг нее восемь других клеток. Тебе нужно их проверить по очереди. Проверить по некоему условию, которое тебя не волнует. Каждая из этих клеток кодируется двумя числами, каждое из которых может принимать значение -1, 0, 1. Вот их нужно перебрать.
Если бы нужен был просто тупой перебор, я бы делал так:

for (int i=-1; i<2; i++) {
for (int j=-1; j<2; j++) {
check (i, j);
}
}

Но! Мне нужен не простой перебор. Мне нужен перебор, начинающийся от определенных значений i и j и заканчивающийся значениями -i, -j.
Искать путь не надо, это я так, в качестве лирического отступления упомянул :).
[User Picture]
From:[info]a_karpov
Date:June 26th, 2009 - 12:04 am
(Link)
Хм? Типа,

Дано: восемь значений.
Задача: невозбранно проверить их!

Как-то непохоже на задачу, достойную поста в жж.

Ты говоришь "Мне нужен перебор, начинающийся от определенных значений i и j и заканчивающийся значениями -i, -j"

под i,j имеются в виду координаты клеток, так? Типа, i = количество горизонтальных, а j = вертикальных шагов от Здесь? Ну значит если тебе нужен не простой перебор, а от сих и до сих, так и пиши не for i = -1; i< 2, а пиши for i = от_сих; i< до_сих

хотя для задачи размеров в 8 простой перебор наверное таки лучше =)

или я реально туплю и не понимаю чего-то, что тут тебя заинтересовало?
[User Picture]
From:[info]illyge
Date:June 26th, 2009 - 12:11 am
(Link)
пиши не for i = -1; i< 2, а пиши for i = от_сих; i< до_сих

От сих до сих - это от каких до каких? :)
Нет, проблема в том, что интервалы по-любому будут от -1 до +1 включительно. Но порядок перебора не линейный "от сих до сих", а по окружности. Представь себе такое обтекание окружности с двух сторон. А там получаются очереди, которые тупо циклом не переберешь. Ну я же в посте привел пример перебора, начинающегося от 0, -1 и заканчивающегося 0, 1:

dC = 0, dR = -1
dC = -1, dR = -1
dC = 1, dR = -1
dC = -1, dR = 0
dC = 1, dR = 0
dC = -1, dR = 1,
dC = 1, dR = 1
dC = 0, dR = 1

А, допустим, если начинать с -1, -1, то получится вот такое:

-1, -1
-1, 0
0, -1
-1, 1
1, -1
0, 1
1, 0
1, 1

Какие ты тут "от сих до сих" будешь подставлять? :)
[User Picture]
From:[info]a_karpov
Date:June 26th, 2009 - 12:20 am
(Link)
хм, я что-то не понимаю вопроса. Если ты говоришь о порядке, то это значит, что у тебя есть понятие, ну, порядка, в математическом смысле. То есть, все пары элементов твоего множества могут быть скормлены оператору "<" и дадут ответ "да" или "нет". Значит можно задать диапазон "от сих до сих", математически осмысленный. То есть, напиши тупо оператор "<" для своего класса или что там в си, стракты? И задавай =)

о, и ещё - что означают твои имена dC, dR? я бы назвал dH, dV, хоризонтал и вертикал )
[User Picture]
From:[info]illyge
Date:June 26th, 2009 - 12:35 am
(Link)
dC и dR генетически восходят к dCol и dRow :)

То есть, все пары элементов твоего множества могут быть скормлены оператору "<" и дадут ответ "да" или "нет". Значит можно задать диапазон "от сих до сих", математически осмысленный. То есть, напиши тупо оператор "<" для своего класса или что там в си, стракты? И задавай =)

Так нет, в том-то и дело! На окружности нет отношения больше-меньше. Вправо-влево есть, но это не совсем то. Давай на пальцах. Вот девять клеток:

A B C
D E F
G H I

Тебе из клетки E нужно заглянуь в каждую из соседних букв. Причем тебе дается начальная буква. Допустим, это буква F. Сначала ты проверяешь F, затем соседние - C и I, затем их соседей B и H, потом A и G, потом D.
Как ты на множестве ABCFIHGD введешь операцию сравнения, если ты не знаешь, начнешь ты обход с точки F или, к примеру, с точки A?

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

static int circle[] = {0, 10, 20, 21, 22, 12, 2, 1};

каждое число в этом массиве получено следующим образом: 10*(dC+1) + dR + 1

С другой стороны, я втайне надеюсь, что кто-то знает, как, имея пару чисел dR и dC, получить пару чисел, представляющих соседа, не прибегая к предварительному построению массива. Ну, типа, одной формулой.
[User Picture]
From:[info]a_karpov
Date:June 26th, 2009 - 01:12 am
(Link)
ну чисто _формально_ - да, массив не нужен... задай пару функций, описывающих биекцию из твоих восьми клеток в Z8 и обратно, и вызывай "следующую" клетку операцией "плюсадин" =)
[User Picture]
From:[info]illyge
Date:June 26th, 2009 - 01:18 am
(Link)
Уж все-таки лучше один массив, чем восемь ифов :)
[User Picture]
From:[info]a_karpov
Date:June 26th, 2009 - 01:26 am
(Link)
ну, да, и памяти съест столько же )
[User Picture]
From:[info]illyge
Date:June 26th, 2009 - 01:20 am
(Link)
Но вообще, эта задача обхода соседних клеток меня давно раздражает. Казалось бы, что может быть проще, а вот ведь, формализуется туговато, с кучей кода.
[User Picture]
From:[info]kouzdra
Date:June 26th, 2009 - 09:00 am
(Link)
imho она несложно формализуется - трабл в другом - с 8 клетками проще захардкодить значительную часть логики, чем писать ее "по уму" - в результате чего и получается то, что получается. Но короче всего делается как-то так:
static int dirs [] [2] =  { 0,1,   1,1,  1,0,    1,-1,  0,-1,  -1,-1,  -1,0,  -1,1 };
static int order [] = {0, 1,-1,  2,-2, 3,-3, 4};
void scan (int dir) {
  for (int i = 0; i != 8; ++ i)
    {
      int const * d = dirs [(order [i] + dir + 8) % 8];
      printf ("<%d,%d>  ",  d [0], d [1]);
    }
   printf ("\n");
}
[User Picture]
From:[info]illyge
Date:June 26th, 2009 - 09:03 am
(Link)
О, симпатично, кстати.
[User Picture]
From:[info]illyge
Date:June 25th, 2009 - 11:26 pm
(Link)
Вернее вот так:

for (int i=-1; i<2; i++) {
for (int j=-1; j<2; j++) {
if (i!=0||j!=0) check (i, j);
}
}

Но это не принципиально :)
[User Picture]
From:[info]kouzdra
Date:June 25th, 2009 - 11:32 pm
(Link)
Принципиально лучше я ничего не вижу, кроме того, чтобы разложить направления в два параллельных массива (или массив структур) и убрать все эти toDR и проч. И лично я бы вместо цикла просто выписал бы массив из 8 значений индексов и закрутил бы по нему цикл с циклическим сдвигом индекса
[User Picture]
From:[info]illyge
Date:June 26th, 2009 - 12:13 am
(Link)
А, ну да, в принципе, надо было делать структурами. Я как-то эту мысль сразу отмел, как загромождающую код, но в итоге получилось гораздо страшнее, а мысль обиделась и не вернулась :)
Собственно, если бы не этот смешной массив направлений, который у меня получился, я бы и пост не создавал