Помощь - Поиск - Пользователи - Календарь
Полная версия: Ракета и Цель.
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ада и другие языки
Andrewshkovskii
Есть желание написать программу, реализующую действия ракеты и её цели. Задаются координаты цели для ракеты, стрелками на клавиатуре можно менять положение цели в 2-х мерном пространстве, ракета должна отслеживать цель. Все это должно производиться "параллельно"..

Код исходный большой(т.е там файлов листинга просто много). Я знаю, что один вольво отвечает на мои безумные посты, на него и надежда вся.smile.gif
В общем, в коде я оставил комментарии, где происходит "неконтролируемое", они в 2х файлах main и realiz.cpp. Код в архиве...
Andrewshkovskii
с одной проблемой в case разобрался. совсем забыл про brake...А вот почему поток изменения координаты мешени не вызывается - не пойму..:/
volvo
А можно объяснить, что ты задумывал сделать вот тут:
	switch (choise)
{
case 'n':
std::cout << "Exiting..";
exit(20);
case 'y':
Target target(sx, sy);
p.tarp = &target;
rocket.LockTarget(target.GetXCoord(), target.GetYCoord());
myEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
SetEvent(myEvent);
MyEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
SetEvent(MyEvent2);
while (target.TStatus)
{
if (kbhit() != 0)
{
key = getch();
TH2=CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE) ChangeCoordTarget, &target, 0, &ID); }
TH=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE) MoveToTarget, &p, 0, &ID);
}
}

, желательно - построчно... Особенное внимание, если можно, обратить на ПОСТОЯННОЕ создание потока MoveToTarget. Сколько их у тебя создается? Что делает каждый из них?
Andrewshkovskii
switch (choise)
{
case 'n':// если ответ - n - выходим из программы.
std::cout << "Exiting..";
exit(20);
case 'y':
Target target(sx, sy);// создается объект класса Target
p.tarp = &target;//полю tarp ,структуры содержащей указатели на классы Rocket и Target, //присваевается адрес объекта target
rocket.LockTarget(target.GetXCoord(), target.GetYCoord());//позиционируем координаты цели
myEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//инициализация события для функции изменения //координаты цели
SetEvent(myEvent);//сброс , что бы не тормозить потоки.
MyEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);//для изменения координаты ракеты
SetEvent(MyEvent2);
while (target.TStatus)//цикл пока жива цель
{
if (kbhit() != 0)//если была нажата клавища
{
key = getch();//считать её в переменную key
TH2=CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE) ChangeCoordTarget, &target, 0, &ID);//поток обрабатывает нажатие клавиши и перемешает цель
}
TH=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE) MoveToTarget, &p, 0, &ID);//ракета постоянно контролирует положение цели и стремиться к ней.
}
}

Такая вот задумка.
Andrewshkovskii
Одну проблему нашел, где вычисляется соотношение координаты ракеты и координаты цели. сейчас пытаюсь исправить. Голова плохо варит, спал 4 часа..на рыбалку ездилsmile.gif)
volvo
Задумка неплохая, НО... Объясни мне, зачем у тебя потоки создаются, делают одно действие, и удаляются?

Я бы сделал так: Запускаешь сразу после того как ввел координаты цели, оба потока. Функции обоих потоков зацикливаешь, чтобы они не завершались, до тех пор, пока... Ну, например, пока цель не будет сбита (в этот момент какой-нибудь глобальный флажок перебросится в TRUE, и оба потока завершатся). А потом в основном потоке приложения уже читаешь клавишу, записываешь ее коды (да, да... Именно коды, все кнопки управления курсором возвращают 2 байта, первый = 0, второй - то, что ты обрабатываешь), на время изменения переменной, хранящей код нажатой клавиши, устанавливаешь событие, блокируя потоку ChangeCoordTarget доступ к этой переменной, пускай в это время он ждет снятия события (второй поток в это время может продолжать работать, его ничего не должно держать), а потом, как только переменная key "разблокирована", поток ChangeCoordTarget опять продолжает работать: читает содержимое key, и, если оно не = 0, изменяет позицию цели... Поскольку и один и второй поток у тебя работает с данными через указатели, то следующий же цикл чтения потоком MoveToTarget уже будет работать с обновленными координатами цели.

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

Понимаешь идею, или не совсем? У тебя что-то похожее, но не надо постоянно создавать/уничтожать потоки, и не надо блокировать работу одного потока на ВЕСЬ цикл работы другого, достаточно запретить им обращаться к одной и той же переменной в разных режимах (когда один - пишет, другой - читает)
Andrewshkovskii
Честно говоря не совсем. НЕ совсем я понимаю в синхронизации/блокировке потоков. Просто очень мало с этим работаю. Допустим у меня будет одна булевая переменная , назовем её ISALIVE; Цикл идет while(ISALIVE). это ясно.
Потом, как мне БЛОКИРОВАТЬ поток?с пом-щью wait-функций?
Не понял про коды smile.gif проверять надо по hex-кодам?
volvo
Смотри... Я ничего не менял в файлах Rocket.cpp и Target.cpp... Вот все изменения, которые я сделал:

1) файл func.h добавлены описания:
extern char key;
extern bool IsAlive;

extern HANDLE changeKeyEvent;
extern HANDLE startEvent;
, все эти переменные определены в main.cpp

2) файл func.cpp полностью:
#include "func.h"

bool ChangeCoordTarget(Target &tr)
{
WaitForSingleObject(startEvent, INFINITE);
SetEvent(startEvent);
while(IsAlive) {
WaitForSingleObject(changeKeyEvent, INFINITE);
if(key != 0) {
// ResetEvent(changeCoordEvent);
cout << (int)key << endl;
switch (key) {
case 72: // up
tr.y++;
std::cout << "\nY now : " << tr.y << '\n';
break;
case 80: //down
tr.y--;
std::cout << "\nY now : " << tr.y << '\n';
break;
case 75: //left
tr.x--;
std::cout << "\nX now : " << tr.x << '\n';
break;
case 77: // right
tr.x++;
std::cout << "\nX now : " << tr.x << '\n';
break;
}
// SetEvent(changeCoordEvent);
key = 0;
}
Sleep(10);
}
return true;
}


bool MoveToTarget(pointers& p)
{
WaitForSingleObject(startEvent, INFINITE);
SetEvent(startEvent);
while(IsAlive) {
p.rocp->GetRealTimeTCoord(p.tarp);
if (p.rocp->x != p.tarp->x)
if (p.tarp->x < 0) p.rocp->x--;
else p.rocp->x++;

if (p.rocp->y != p.tarp->y)
if (p.tarp->y < 0) p.rocp->y--;
else p.rocp->y++;

cout << "Missile: " << p.rocp->x << " ," << p.rocp->y << endl;
Sleep (1000); // <--- Изменить, а то не совсем честно получается - ракета медленно летит
p.rocp->Hit(p.tarp->TStatus);
if(!p.tarp->TStatus) {
IsAlive = false;
}
}
return true;
}


3) основной цикл файла main.cpp (после согласия выпустить ракету):
		rocket.LockTarget(target.GetXCoord(), target.GetYCoord());
startEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

changeKeyEvent = CreateEvent(NULL, TRUE, TRUE, NULL);

TH2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ChangeCoordTarget,
&target, 0, &ID); // поток обработки изменения координаты цели
TH = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE) MoveToTarget,
&p, 0, &ID); // ракета постоянно контролирует положение цели и стремится к ней.

SetEvent(startEvent);
int ch;
while(kbhit()) getch();

while(IsAlive) {
if(kbhit()) {
if((ch = getch()) == 224) { // Откуда взялось 224 - не понял, всегда был 0
ResetEvent(changeKeyEvent);
key = getch();
SetEvent(changeKeyEvent);
}
else {
if(ch == 27) {
IsAlive = false;
}
}
}
Sleep(50);
}
} // switch(choise)
Если нужны комментарии - скажи, я суть позже добавлю.
Andrewshkovskii
ну интересуют все закомментированные инструкции, в частности:
// switch(choise) - просто забыл убрать?smile.gif
//Set и Reset Event - делал для теста,так я понимаю?
volvo
Цитата
просто забыл убрать?
Нет-нет... Это как раз для того, чтобы показать тебе, где заканчивается мой фрагмент, и продолжается дальше твой, чтоб ты не подумал, что лишняя закрывающая скобка...

Насчет Set/ResetEvent внутри ChangeCoordTarget() - не то, чтобы для теста, оно таки должно там быть. Пока у тебя пауза в одном потоке в 100 раз больше, чем в другом - практически нереально, чтобы в тот самый момент, когда один поток переменную меняет, другой ее считывал. Как только длительности Sleep-ов приблизятся друг к другу, вероятность такого события будет все выше и выше, и тебе с уменьшением паузы при полете ракеты придется раскомментировать Set/ResetEvent, а в начале цикла while(IsAlive) добавить ожидание этого события, по тому же принципу, как это сделано в ChangeCoordTarget()...

А теперь у меня вопрос на засыпку тебе, раз ты не спросил сам smile.gif

	WaitForSingleObject(startEvent, INFINITE);
SetEvent(startEvent);
и в начале потока ChangeCoordTarget, и в начале потока MoveToTarget(). Почему? Зачем? Что будет, если не делать SetEvent?
Andrewshkovskii
я сделал такое лицо сейчас - dry.gif
У меня лишь предположение, что потоки не начнут свою работу?..

Добавлено через 3 мин.
Черт. я перестал вообще понимать что происходит. Тупо, често, вот сейчас тупо вставил твой код, попробывал "поиграться" и...приложение завершается , так сказать, не успев начаться. Я так понимаю надо добавить ивенты для смены координат...?или я ничего не понимаю.. mad.gif
volvo
Цитата(Andrewshkovskii @ 18.12.2008 23:08) *
вот сейчас тупо вставил твой код, попробывал "поиграться" и...приложение завершается , так сказать, не успев начаться. Я так понимаю надо добавить ивенты для смены координат...?или я ничего не понимаю.. mad.gif

Я надеюсь, ты не забыл, что надо инициализировать IsAlive?
bool IsAlive = true;
Иначе оно инициализируется false, и "Усе пропало, шеф!!!" (С) "Бриллиантовая рука"

Добавлено через 1 мин.
Цитата
У меня лишь предположение, что потоки не начнут свою работу?..
Неправильно smile.gif Думай дальше...
Andrewshkovskii
Volvo, скажу одно - тебе можно только позавидовать, у тебя столько практики в логике и кодинге..а ладно.щас подумаю получше.


Добавлено через 18 мин.
Ну, видимо, если убрать SetEvent ,допустим, в ChangeCoordTarget, то поток с "полетом ракеты" не узнает о том, что координата поменялась ...
Кстате, почему-то, если добавить строчку в

...
case 77: // right
tr.x++;
break;
}
// SetEvent(changeCoordEvent);
key = 0;
std::cout << "Target now on " << tr.x << " : " << tr.y;//!!!тут
}

То выводиться ascii код нажатой клавиши.
ЕЩё одно..почему-то ракета не летит за целью, а просто перебирает координаты(x++,y--).
Условие я такое поставил :
if (p.rocp->x != p.rocp->TX)//если координата ракеты != коориданты цели
if (p.rocp->x < p.rocp->TX)//если координата Х ракеты меньше коориданты Х цели
if (p.rocp->TX < 0)//проверить, отрицательная или положительная координата х у ракеты
p.rocp->x++;//тогда сместить к цели
else
p.rocp->x++;//иначе , если кооридината ракеты выше положительная, то так же сместиться к ней
else if (p.rocp->x > p.rocp->TX)//если ракета находиться выше цели то..
if (p.rocp->TX < 0)//опять проверяем и т.д.тоже самое и координатой Y
p.rocp->x--;
else
p.rocp->x--;

if (p.rocp->y != p.rocp->TY)
if (p.rocp->y < p.rocp->TY)
if (p.rocp->TY < 0)
p.rocp->y++;
else
p.rocp->y++;
else if (p.rocp->y > p.rocp->TY)
if (p.rocp->TY < 0)
p.rocp->y--;
else
p.rocp->y--;

volvo
Цитата
Ну, видимо, если убрать SetEvent ,допустим, в ChangeCoordTarget, то поток с "полетом ракеты" не узнает о том, что координата поменялась ...
Если убрать SetEvent в ChangeCoordTarget, то поток с полетом ракеты никогда не выйдет из ожидания WaitForSingleObject, потому что событие - с автосбросом, как только один WaitFor... его дождался - оно сбрасывается, и если его опять не установить, то больше ни один WaitFor... его не дождется... Обращай на это внимание. Так что SetEvent-ы в обоих потоках нужны для того, чтобы запустить друг друга (твой изначальный ответ был неверен: один поток все-же запустится, а вот какой именно - это решает системный планировщик заданий).

Цитата
почему-то ракета не летит за целью, а просто перебирает координаты(x++,y--).
С твоей функцией у меня ракета снесла 4 цели из 4-х...

Цитата
выводиться ascii код нажатой клавиши.
Не нужен? Убери
            // ResetEvent(changeCoordEvent);
cout << (int)key << endl; // <--- Вот эту строчку
Andrewshkovskii
про аски-код - не заметил, извиняюсь.
Странно, но у меня почему-то происходит изменение координат ракеты так, как я описал, а не "правильно.
полный непонимат.
Будто ракета не получает кооридинаты цели.
может я что-не так написал?,,Да нет, ничего такого вроде бы:

bool MoveToTarget(pointers& p)
{
WaitForSingleObject(startEvent, INFINITE);
SetEvent(startEvent);
while (IsAlive)
{
p.rocp->GetRealTimeTCoord(p.tarp);
if (p.rocp->x != p.rocp->TX)
if (p.rocp->x < p.rocp->TX)
if (p.rocp->TX < 0)
p.rocp->x++;
else
p.rocp->x++;
else if (p.rocp->x > p.rocp->TX)
if (p.rocp->TX < 0)
p.rocp->x--;
else
p.rocp->x--;

if (p.rocp->y != p.rocp->TY)
if (p.rocp->y < p.rocp->TY)
if (p.rocp->TY < 0)
p.rocp->y++;
else
p.rocp->y++;
else if (p.rocp->y > p.rocp->TY)
if (p.rocp->TY < 0)
p.rocp->y--;
else
p.rocp->y--;
// p.rocp->GetRealTimeTCoord(p.tarp);
// if (p.rocp->x != p.tarp->x)
// if (p.tarp->x < 0) p.rocp->x--;
// else p.rocp->x++;
//
// if (p.rocp->y != p.tarp->y)
// if (p.tarp->y < 0) p.rocp->y--;
// else p.rocp->y++;

cout << "\nMissile at : " << p.rocp->x << " ," << p.rocp->y << endl;
Sleep(500); // <--- Изменить, а то не совсем честно получается - ракета медленно летит
p.rocp->Hit(p.tarp->TStatus);
if (!p.tarp->TStatus)
{
IsAlive = false;
}
}
return true;
}
volvo
Кстати, чтобы ракета наводилась на цель, достаточно:
        if(p.rocp->x > p.rocp->TX) p.rocp->x -= 1;
else if(p.rocp->x < p.rocp->TX) p.rocp->x += 1;

if(p.rocp->y > p.rocp->TY) p.rocp->y -= 1;
else if(p.rocp->y < p.rocp->TY) p.rocp->y += 1;


И еще одно: если ты хочешь, чтобы при выводе из разных потоков информация отображалась именно так, как ты ее выводишь, а не "перемешивалась" - то сначала печатай все в одну строку (sprintf-ом, например, включая и перевод строки), а потом сразу всю строку посылай в cout. Тогда это будет отображаться точно в том виде, в котором ты подразумевал, в консоли... Иначе, если сделать

// в одном потоке
cout << "hello" << " world" << endl;

// в другом потоке
cout << "wow" << endl;

, то вывод может "перемешаться", и получится, скажем, "hellowow\n world\n", или "hello worldwow\n\n", ведь ты посылаешь данные в поток вывода порциями...
Andrewshkovskii
Про вывод из потоков понятно.НО проблема-то не в выводе,а в том, что ракеты "психует":) по Х летит вверх, по Y вниз,цель игнорирует. У Вас все нормально?..
volvo
Цитата
проблема-то не в выводе,а в том, что ракеты "психует":) по Х летит вверх, по Y вниз,цель игнорирует.
Мой вариант функции слежения тоже работает стабильно, цели уничтожаются... Значит, ты что-то не сделал...

Вот весь архив: Нажмите для просмотра прикрепленного файла

Проверь...
Andrewshkovskii
Missile at : 8 ,-8
missile report: target at 1527825539 : -1916576418
...всё ясно. Забыл присвоить структуре указателей указатель на цель.
Volvo...Огромное спасибо! Я не знаю, что бы я делал без твоих советов!:)
Кстате, кот у тебя на аватарке классныйsmile.gif
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.