Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Задачи _ Целенаправленное движение со случайной составляющей

Автор: Soxatyi 27.10.2006 0:33

Захотелось написать забавную задачку, но возникла проблемка при её реализации.
Задаются начальные координаты точки и её цвет, для нее генерится точка-цель. Точка устремляется к цели, причем на каждом шаге к координатам точки довешивается рандомное значение (назовем его заносом), лежащее в промежутке [-foo,foo]. Как только расстояние от точки до цели меньше половины заноса, задается новая цель и цвет и так далее.
Например, при foo = 0 точка просто движется к цели без всяких заносов.
Вот что у меня получилось:

uses crt,graph;
var gd,gm:integer;
x,y,dx,dy,v,w,color,foo:integer;
ch:char;
function Sign(x:integer):shortint;
begin
sign:=ord(x>0)-ord(x<0); {обычная сигнатура, люблю я её через ord'ы писать}
end;
begin gd:=Detect;
randomize;
initgraph(gd,gm,'');
foo:=0;
x:=0; {задание координат исходной точки}
y:=0;
v:=random(getmaxx+1); {задание координат точки-цели}
w:=random(getmaxy+1);
color:=random(15)+1;
moveto(x,y);
readln;
repeat;
setcolor(color);
lineto(x,y);
moveto(x,y);
delay(200);
dx:=random(2*foo+1)-foo; {генерация случайной составляющей движения}
dy:=random(2*foo+1)-foo; {рандом генерит значения в промежутке [-foo,foo]}
if (x+dx>getmaxx) or (x+dx<0) then dx:=-dx; {проверка выхода за границы экрана}
if (y+dy>getmaxy) or (y+dy<0) then dy:=-dy;
inc(x,dx+sign(v-x)); {устремление точки к цели за счет сигнатуры расстояния до цели + занос}
inc(y,dy+sign(w-y));
if (abs(v-x)<=(foo div 2)) and (abs(w-y)<=(foo div 2)) then {проверка достижения цели}
begin
color:=random(15)+1;
v:=random(getmaxx+1); {задание новой цели}
w:=random(getmaxy+1);
end;
if keypressed then begin {интерфейс}
ch:=readkey;
case ch of
'+':inc(foo);
'-':if foo>0 then dec(foo);
'0':foo:=0;
'c':begin
cleardevice;
moveto(x,y);
end;
end;
end;
until ch=#27;
closegraph;
end.

Во время работы проги кнопками '+' и '-' можно изменять величину заноса, '0' - устанавливать занос = 0, 'c' - чистить экран.
Собственно, не устраивает меня реализация устремления точки к цели. Сейчас она движется сначала по диагонали |x|=|y|, а как только одна из координат становится равной координате цели, то сигнатура начинает выдавать нуль и точка движется только по горизонтали или только по вертикали. Каким бы ни был занос, точка всегда пытается выйти на такой путь.
Естественно, хочется, чтобы точка пыталась двигаться не по этому галкообразному маршруту, а сразу по диагонали.
Поскольку объяснил я чуток кривовато, прилагаю картинку с тем, чего мне хочется. ;)
Прикрепленное изображение
Буду дико благодарен за помощь.

Автор: volvo 27.10.2006 0:43

Ну, насколько я понимаю, http://forum.pascal.net.ru/index.php?s=&showtopic=5491&view=findpost&p=42527 должна тебе помочь? У тебя "цель" движется? В моей программе - погоня за движущейся целью...

Автор: Soxatyi 27.10.2006 1:22

Цель абсолютно неподвижна и меняется только после её достижения. Я не представляю, как мне использовать твою программу про зайца, потому как там нету все портящего случайного заноса.
Если бы заноса не было, то решение было б очевидно. Я бы перевел x и y в real и в момент задания цели вычислил бы скорость:

buf:=max(abs(v-x),abs(w-y)); {в буферную переменную закидываю длину бОльшего катета} 
vx:=(v-x)/buf; {одна из скоростей стала бы равна единице}
vy:=(w-y)/buf;

После чего вместо двух inc использовал бы следующее:
x:=x+vx;
y:=y+vy;

Но в моем случае вычисленные так значения скорости стали бы неверны уже после первого заноса.

Автор: мисс_граффити 27.10.2006 1:26

есть смутная мысль...
если ты можешь реализовать y=x, то сможешь и y=2x, допустим?...

Автор: Soxatyi 27.10.2006 1:52

О, осенило! В моем случае достаточно лишь перерассчитывать эти vx и vy не только при задании новой цели, но и вообще на каждом витке цикла!

uses crt,graph;
var gd,gm:integer;
dx,dy,v,w,color,foo:integer;
x,y,buf:real;
ch:char;
function max(a,b:integer):integer;
begin
if a<=b then max:=b
else max:=a;
end;
function GetNew(z,limit:integer):integer; {вычисление отличной от текущей точки цели}
var buf:integer;
begin
repeat;
buf:=random(limit+1);
until buf<>z;
getnew:=buf;
end;
begin gd:=Detect;
randomize;
initgraph(gd,gm,'');
foo:=0;
x:=0;
y:=0;
v:=getnew(round(x),getmaxx);
w:=getnew(round(y),getmaxy);
color:=random(15)+1;
moveto(round(x),round(y));
readln;
repeat;
setcolor(color);
lineto(round(x),round(y));
moveto(round(x),round(y));
delay(200);
dx:=random(2*foo+1)-foo; {[-foo,foo]}
dy:=random(2*foo+1)-foo; {[-foo,foo]}
if (x+dx>getmaxx) or (x+dx<0) then dx:=-dx;
if (y+dy>getmaxy) or (y+dy<0) then dy:=-dy;
{кусок с проверкой достижения цели пришлось переставить выше
вычисления новых координат, ибо иначе могла вылезти ошибка "division by zero"}
if (abs(v-x)<=foo/2) and (abs(w-y)<=foo/2) then
begin
color:=random(15)+1;
x:=round(x);
y:=round(y);
v:=getnew(round(x),getmaxx);
w:=getnew(round(y),getmaxy);
end;
buf:=max(abs(v-round(x)),abs(w-round(y))); {сохраняем длину бОльшего катета}
x:=x+dx+(v-x)/buf;
y:=y+dy+(w-y)/buf;
if keypressed then begin
ch:=readkey;
case ch of
'+':inc(foo);
'-':if foo>0 then dec(foo);
'0':foo:=0;
'c':begin
cleardevice;
moveto(round(x),round(y));
end;
end;
end;
until ch=#27;
closegraph;
end.


Теперь все работает как надо.