среда, 22 февраля 2012 г.

FiguresWars - переход на Qt+OpenGL


Если кратко, то был добавлен интерфейс Qt и OpenGL было внедрено с помощью Qt. По смыслу игры ничего нового сделано не было. Единственное, что могу сказать Qt - удобная штука, правда надо немного помучится, чтобы его освоить.

воскресенье, 19 февраля 2012 г.

PicView - изучаем Qt

Прежде чем совмещать Qt и OpenGL, я решил изучить, хотя бы.. понемногу, того и того в отдельности. На ogl я делал "тестовую графику" для проекта по физике. На Qt  я решил сделать...да то, что получится. Очень важным элементом для меня была подгрузка картинок и их отображение, а так же понять как вообще работает Qt и так же было интересно пользоваться дизайнером окон Qt Creator.
Первое, что я сделал в новом проекте - это создал кнопку и сделал так, что при нажатии на неё, она скрывается. Абсолютно бессмысленная вещь. Однако принцип работы: как для элементов, созданных в дизайнере делать события я понял(правой кнопкой мыши на элемент и "Добавить слот"). Дальше я принялся загружать картинки(они отображались в виджитах типа label). Полез в гугл.нарыл кучу разных способов, но работал только один:
QString f;
...
QImage im;
im.load(f);
l->setPixmap(QPixmap::fromImage(im));
Где l - это указатель на наш label. образ метода был взят отсюда, спасибо автору статьи. В общем, это статья и определила тип приложения, которое я собирался сделать. А именно - небольшой просмоторщик картинок. Естественно. особый интерес представляла  загрузка файлов из папки проекта(ресурсные файлы, итд). Долго у меня не получилось, поэтом оказалась, что всё дело в странности реализации режима debug в Qt Creator,а именно: он создаёт папку, например: пусть в папке "MP" находится ваш проект с именем "Pj", при сборке debug, QtC создаёт папку, например "Pj-build-desktop-Qt_4_8_0_for_Desktop_-_MSVC2008__Qt_SDK_________" и в этой папке есть папка "debug", где находится файл Pj.exe(например). Я кидал ресурсы в папки MP/Pj и MP/.../debug. А при запуске в этом режиме QtC считает корневой папку MP/Pj-build-...
в неё и надо скидывать ресурсы. При это в release, он считай корневой ту папку, в которой находится исполняемый файл.
После того, как я понял этот странный факт, всё пошло быстро. к приложению была добавлена иконка(найденная, на просторах интернета, а точнее - на этом сайте).

Некоторые элементы интерфейса, были созданы программно, а именно опция "Открыть" в меню "Файл" и элемент label в статус-баре. С созданной опцией так же программно было связанно событие открытия файла.
Приложение работает следующим образом: с помощью опции "Открыть", в меню "Файл", выбираете картинку, а затем нажимаете "Set Image".
Вот то, что получилось, исходники прилагаются. Приложение требует библиотек QtGui4.dll и QtCore4.dll. Из-за того, что они немного увесисты(10 Мб вместе), прикладывать их не стал.

суббота, 18 февраля 2012 г.

Проект по физике - Волны на струне непрерывные и дискретные волны

Задача с виду простая - смоделировать колебание струны и волны на струне. рассматриваются струны двух видов - непрерывная и дискретная.

Непрерывная струна
Это обычная струна в нашем представлении. Некоторая тонкая нить, жестко закреплённая на концах. Колебания описываются волновым уравнением. В условиях задачи решение его получается методом Фурье. Краевые условия подобраны так: это две прямые, которые "скругляются параболой", а начальная скорость всех точек считает равным нулю.
С этим видимо колебаний почти не возникло проблем: получается решение в виде ряда, коэффициенты которого просто высчитываются (параллельный подсчёт коэффициентов Фурье я описывал раньше).

Дискретная струна
Это тонкая невесомая нить, на которой закреплены грузы равной массы. через равное расстояние.  Начальное положение высчитывается так же, однако, чтобы хоть как-то облегчить задачу, "потянуть" струну можно только за груз.
Колебание такой струны описывается системой из k уравнений следующего вида:
N - сила натяжения нити, m - масса груза, Δl - расстояние между грузами, а un - искомые функции.
Метод Эйлера решить эту систему не сильно помог.
Расспараллеленный на OpenMP, метод Рунге-Кутт, при нужной погрешности очень тормозил (тестирование проводилось при k=13, m =4кг, l =20м), однако, убрав параллельные области, я избавился от тормозов.

Сейчас обе модели работают, и дают верные(точнее, похожие на верные) результаты. Итог: использование параллельного программирование не всегда даёт прирост в скорости. Иногда на создание/уничтожение потоков(или пересылку сообщений) затрачивается больше времени, чем выигрывается на ускорение вычислений. Это тоже надо учитывать.

На очереди две соединённые непрерывные струны разной плотности. После проверки выложу примерный код(или сам код), реализующий описанные мой системы.

суббота, 11 февраля 2012 г.

Figures Wars - прогресс за два дня

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

Краткий обзор
Сменился графический движок (теперь он на OpenGL), добавились новые команды и название старых было изменено, добавился новый юнит и введены характеристики, добавилось возможность запомнить по одному юниту каждого типа. Теперь за ход выполняется не одна команда, а множество команд, которые могут быть выполнены и независимы. Например, рабочий может либо сходить, либо построить базу, но вместе эти действия совершить нельзя.

Юниты
Сокращения: HP - жизни, AT - атака, T - тип, M - графическое отображение
Рабочий. HP - 2, AT - 0, T - 0, M - конус, остриём вниз.
База. HP - 20, AT - 0, T - 1, M - шар.
Башня. HP - 10, AT - 1, T - 3,  M - параллелепипед. На каждом ходе башня атакует клетки во все 4 направления.

Команды
Курсивом помечаются аргументы функции. 
move x y d - юнит с координатами (x, y) перемещается в направление d
build x y t d - юнит с координатами (x, y) строить здание типа t в направление d
basecreate x y t - база с координатами (x, y) производит юнита типа t
mworker x y - запомнить рабочего с координатами (x, y)
mbase x y - запомнить базу с координатами (x, y)
mwmove d - запомненный рабочий перемещается в направлении d
mwbuild t d - запомненный рабочий строит здание t в направлении d
mbcreate t - запомненная база производит юнита типа t
| - разделитель, введён чтобы облегчить восприятие алгоритма. Отделяет команды созданные для одного шага.

Скрины

В ближайших планах на реализацию
 Реализовать запоминание потенциально бесконечного числа юнитов каждого типа. Ввести команды, реализующие однотипные действия (циклы). Добавить импорт моделек. Приделать интерфейс (этим я собственно сегодня полдня и занимался - пытался совместить OpenGL и Qt, не прочитав толком ни одно статьи про их совмещение. Пока не получилось. Будем пробывать дальше).

Благорадрность

Спасибо Антону (@MrFeod, блог) за внедрение OpenGL в проект.
  

четверг, 9 февраля 2012 г.

Figures Wars - начало

А вот и та игрушка. Она достаточно простая и, пока что, не интересная.  

Краткое описание
Есть несколько фракций (пока что от 1 до 3), которые враждую между собою. Все эти фракции представляют из себя фигуры. Каждая фигура выполняет свою роль. Круги - это базы, они создают юнитов, треугольники - это рабочие, они создаются здания. А больше пока и нет.
Задача игрока: написать последовательность действий, которая приведёт его к победе. 

 А теперь подробней
Игрокам предоставляется информация о карте: её размер, количество игроков, максимальное количество баз у одного игрока и местоположение начальных баз. Карта предоставляет из себя прямоугольное поле. Для каждой клетки задана относительная ориентация - направления, обознаются числами: 1 - вправо, 2 - вверх, 3 - влево, 4 - вниз.
Изначально у каждого игрока есть одна база и один рабочий(появляется в направлении 1, если там конец поля - в направлении 3 от базы). Далее все юниты выполняют последовательность команд, заданную игроком.

Команды
Курсивом будут указываться параметры команды.
md x y d  - перемещает юнита с координатами (x,y) в направление d(если клетка свободна)
bd x y t d - рабочий в клетке с координатами (x,y) строит здание типа t, на клетки в направлении d
bc x y t - база с координатами (x,y) строит юнит типа t. Юнит строится в свободной клетке, номер направления которой наименьший.

Юниты
Пока что есть два типа юнитов: 0 - рабочий, который может строить здания и 1 - база, которая производит другие юниты. Номера соответствуют типу юнита.


В ближайщих планах на реализацию
Добавить воинов и башни. Сделать массовые команды, которые приказывают всем юнитам одного типа. Сделать возможность исполнения нескольких команд от одного игрока на одном шаге.

Первый скрин

Простая игрушка

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

четверг, 2 февраля 2012 г.

Проект по физике: ряды Фурье, метод Эйлера и параллельное программирование

В третьем семестре мы разработали проект по физики - физический маятник. Я занимался вычислениями. Если в общих чертах, то там решалось дифференциальное уравнение методом Эйлера. И функция заданная эти уравнением раскладывалась в ряд Фурье.
Подсчёт коэффициентов Фурье занимал достаточно много времени (на компах в терминалках на это уходило до двух минут), поэтому решил попробовать ускорить это всё, используя OpenMP.
Сама задача не хитрая - распараллелить цикл подсчёта интегральных сумм(метод трапеций). Однако, в силу неявного задания функции очень неудобно считать её значения в точках, т.к. высчитываются они отдельно от предыдущих. Сказано - сделано.
Теперь просто - запускаем параллельный цикл с помощью #pragma parallel for и использованием reduction и получаем уже расспареллеленую версию интегрирования.
Что меня поразило, так это ускорение: если последовательный вариант считался 26.6 с, то параллельный 3.3 с.
Приведу схематичный пример кода (на С), который был и который стал(пояснения после)
double Ssin = 0, Scos = 0;
double fp = f(x1,fimax,vl,x1,&penenq,vl,w);
double fp1,fc,fc1,fs,fs1;

while(absol(x1-l)>=h){

 fp1 = f(x1, fp, vl, x1+h,&penenq,vl,w);
 fc = fp * cos(2*n*(x1)*pi/l)*h;
 fc1 = fp1 * cos(2*n*(x1+h)*pi/l)*h;
 fs = fp * sin(2*n*(x1)*pi/l)*h;
 fs1 = fp1 * sin(2*n*(x1+h)*pi/l)*h;
 Scos += (fc+fc1)/2;
 Ssin += (fs+fs1)/2;
 x1 += h1;
 fp = fp1;

};
Scos = 2*Scos/l;
Ssin = 2*Ssin/l;
Параллельный вариант:
double Ssin = 0, Scos = 0;
double fp1,fc,fc1,fs,fs1;
int it_numb = l/h;
double *fp = new double [it_numb];
fp[0] = f(x1,fimax,vl,x1,&penenq,vl,w);
for(int i = 1; i < it_numb; i++)
 fp[i] = f(x1+i*h);
int i;

#pragma omp parallel for reduction (+:Scos) reduction (+:Ssin) private (fc,fc1,fs,fs1,i) schedule(dynamic,4)

for(i = 1; i < it_numb; i++){
 fc = fp[i-1] * cos(2*n*(x1+i*h)*pi/l)*h;
 fc1 = fp[i] * cos(2*n*(x1+(i+1)*h)*pi/l)*h;
 fs = fp[i-1] * sin(2*n*(x1+i*h)*pi/l)*h;
 fs1 = fp[i] * sin(2*n*(x1+(i+1)*h)*pi/l)*h;
 Scos += (fc+fc1)/2;
 Ssin1 += (fs+fs1)/2;
}

Scos = 2*Scos/l;
Ssin = 2*Ssin/l; 
delete fp[];
x1 - текущая точка(в последовательном варианте) или начальная точка(в параллельном), l - промежуток интегрирования (если быть более точным [0,l]), h - шаг разбиения.
Я считаю, что сами изменения кода не очень большие, а вот результат очень хороший, так что при возможности - распараллеливание вычисления. 

Небольшое вступление.

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