357 lines
17 KiB
C++
357 lines
17 KiB
C++
#include <cmath>
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#include <sstream>
|
||
#include <QApplication>
|
||
#include <QLabel>
|
||
#include <QString>
|
||
#include <QTime>
|
||
#include "model.h"
|
||
|
||
static const qreal pi = 3.14159265358979323846264338327950288419717;
|
||
|
||
/*
|
||
* Знак числа
|
||
*/
|
||
template <typename T> int sgn(T val) {
|
||
return (T(0) < val) - (val < T(0));
|
||
}
|
||
|
||
Model::Model(uint modelId, uint CM, uint CN, uint PM, uint PN, int radius, uint speed, bool period, uint distr,
|
||
uint enmity, uint newEnmity, QObject *parent): id(modelId), cowardMedian(CM), cowardNormal(CN),
|
||
protectorMedian(PM), protectorNormal(PN), agentRadius(radius), gamerSpeed(speed), period(period), initDistr(distr),
|
||
enmity(enmity), newEnmity(newEnmity), avContentment(0), QObject(parent)
|
||
{
|
||
uint agr; // номер агрессора (для передачи в конструктор игрока)
|
||
uint prot; // номер защитника (для передачи в конструктор игрока)
|
||
|
||
// Конструирование игроков, заполнение списка игроков
|
||
uint gamerCount = cowardMedian + cowardNormal + protectorMedian + protectorNormal;
|
||
QTime now = QTime::currentTime();
|
||
qsrand(now.msec());
|
||
|
||
for(uint i = 0; i < gamerCount; i++){
|
||
do {
|
||
agr = qrand() % gamerCount;
|
||
}
|
||
while (agr == i); // игрок не может быть собственным агрессором
|
||
do {
|
||
prot = qrand() % gamerCount;
|
||
}
|
||
while(prot == i || prot == agr); // игрок и агрессор не могут быть защитниками
|
||
|
||
Agent *gamer = new Agent(i, agr, prot, 0, 0, agentRadius, gamerSpeed);
|
||
if (i < cowardMedian){}
|
||
else if (i < cowardMedian + cowardNormal){
|
||
gamer->setStratedy(1);
|
||
}
|
||
else if (i < cowardMedian + cowardNormal + protectorMedian){
|
||
gamer->setMode(1);
|
||
}
|
||
else {
|
||
gamer->setMode(1);
|
||
gamer->setStratedy(1);
|
||
}
|
||
uint d;
|
||
if (initDistr == 0){
|
||
d = qrand() % 220 + 70;
|
||
}
|
||
else {
|
||
d = 200;
|
||
}
|
||
agentList.push_back(gamer);
|
||
agentList[i]->setCoordX(::sin((i * 2 * pi) / gamerCount) * d);
|
||
agentList[i]->setCoordY(::cos((i * 2 * pi) / gamerCount) * d);
|
||
}
|
||
setEnmity(newEnmity);
|
||
}
|
||
|
||
/*
|
||
* Функция, возвращающая модуль от числа
|
||
*/
|
||
qreal Model::mod(qreal a){
|
||
qreal mod = (a > 0) ? a : -a;
|
||
return mod;
|
||
}
|
||
|
||
/* Функция, возвращающая список игроков, с которыми столкнётся игрок index
|
||
* при сдвиге на вектор (x, y); dist - максимальное расстояние, на котором игроки считаются столкнувшимися
|
||
*/
|
||
QList<Agent *> Model::DangerGamer(uint index, qreal x, qreal y, uint dist){
|
||
//std:: cout << "INFO: Manager::DangerGamer start" << std::endl;
|
||
QList <Agent *> DangerGamers;
|
||
for(int i = 0; i < agentList.size(); i++){
|
||
if(i == index){
|
||
continue;
|
||
}
|
||
else{
|
||
QLineF toAgent = QLineF(agentList[index]->getCoordX() + x, agentList[index]->getCoordY() + y, agentList[i]->getCoordX(), agentList[i]->getCoordY());
|
||
if(toAgent.length() <= dist){
|
||
DangerGamers.push_back(agentList[i]);
|
||
}
|
||
else{}
|
||
}
|
||
}
|
||
return DangerGamers;
|
||
}
|
||
|
||
QList<QList<Agent *> > Model::DangerGamer(QList<QList<Agent *> > danger, int index, qreal x, qreal y, uint dist){
|
||
//std:: cout << "INFO: Manager::DangerGamer start" << std::endl;
|
||
for(int i = index + 1; i < agentList.size(); i++){
|
||
QLineF toAgent = QLineF(agentList[index]->getCoordX() + x, agentList[index]->getCoordY() + y, agentList[i]->getCoordX(), agentList[i]->getCoordY());
|
||
if(toAgent.length() <= dist){
|
||
danger[index].push_back(agentList[i]);
|
||
danger[i].push_back(agentList[index]);
|
||
}
|
||
else {}
|
||
}
|
||
return danger;
|
||
}
|
||
|
||
/*
|
||
* Проверка, выходит ли вектор checked в координатах игрока с номером gamerId за пределы сцены по координате x
|
||
* возвращает true, если выходит, false - иначе.
|
||
*/
|
||
bool Model::leaveWindowX(uint agentId, QLineF checked){
|
||
if(mod(agentList[agentId]->getCoordX() + checked.dx()) > 300 - agentList[agentId]->getRadius()){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* Проверка, выходит ли вектор checked в координатах игрока с номером gamerId за пределы сцены по координате y
|
||
* возвращает true, если выходит, false - иначе.
|
||
*/
|
||
bool Model::leaveWindowY(uint agentId, QLineF checked){
|
||
if(mod(agentList[agentId]->getCoordY() + checked.dy()) > 300 - agentList[agentId]->getRadius()){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* Преобразование координат из-за периодичности сцены
|
||
* (при попытке выйти за пределы окна игрок должен "выйти" с противоположной стороны сцены)
|
||
* параметры: номер игрока, вектор сдвига
|
||
* возвращает преобразованный вектор сдвига
|
||
*/
|
||
QLineF Model::periodicTransf(uint index, QLineF tend){
|
||
qreal x = tend.dx();
|
||
qreal y = tend.dy();
|
||
// Проверка на выход за пределы окна по координате x
|
||
if (mod(tend.dx() + agentList[index]->getCoordX()) > 300 - agentList[index]->getRadius()){
|
||
x = -1*(600 - 2*agentList[index]->getRadius())*sgn(tend.dx()) + tend.dx();
|
||
}
|
||
else{}
|
||
// Проверка на выход за пределы окна по координате y
|
||
if (mod(tend.dy() + agentList[index]->getCoordY()) > 300 - agentList[index]->getRadius()){
|
||
y = -1*(600 - 2*agentList[index]->getRadius())*sgn(tend.dy()) + tend.dy();
|
||
}
|
||
else{}
|
||
return QLineF(0, 0, x, y);
|
||
}
|
||
|
||
/*
|
||
* Вычисление новых координат для игрока под номером gamerId
|
||
*/
|
||
QLineF Model::requiredVector(uint agentId){
|
||
//std:: cout << "req start" << std::endl;
|
||
Agent *agressorAg = agentList[agentList[agentId]->getAgressor()];
|
||
Agent *protectorAg = agentList[agentList[agentId]->getProtector()];
|
||
// Вычисление координат агрессора и защитника
|
||
qreal protectorX = protectorAg->getCoordX();
|
||
qreal protectorY = protectorAg->getCoordY();
|
||
qreal agressorX = agressorAg->getCoordX();
|
||
qreal agressorY = agressorAg->getCoordY();
|
||
|
||
QLineF fromAtoPr(agressorX, agressorY, protectorX, protectorY);
|
||
QLineF shiftVector;
|
||
|
||
if (agentList[agentId]->getStrategy() == 0){ // Агент движется вдоль медианы
|
||
// Режим труса
|
||
if(agentList[agentId]->getMode() == 0){
|
||
qreal shiftX = agressorX + 2*fromAtoPr.dx() - agentList[agentId]->getCoordX();
|
||
qreal shiftY = agressorY + 2*fromAtoPr.dy() - agentList[agentId]->getCoordY();
|
||
shiftVector = QLineF(0, 0, shiftX, shiftY);
|
||
}
|
||
// Режим защитника
|
||
else if(agentList[agentId]->getMode() == 1){
|
||
qreal shiftX = agressorX + fromAtoPr.dx()/2 - agentList[agentId]->getCoordX();
|
||
qreal shiftY = agressorY + fromAtoPr.dy()/2 - agentList[agentId]->getCoordY();
|
||
shiftVector = QLineF(0, 0, shiftX, shiftY);
|
||
}
|
||
else {}
|
||
agentList[agentId]->setDistTarget(shiftVector.length());
|
||
// Проверка: не проскочить мимо цели
|
||
if (shiftVector.length() > agentList[agentId]->getSpeed()){
|
||
shiftVector.setLength(agentList[agentId]->getSpeed());
|
||
}
|
||
else{}
|
||
}
|
||
else if (agentList[agentId]->getStrategy() == 1){ // если агент движется вдоль нормали
|
||
|
||
// ToDo: поправить логику
|
||
if((protectorY - agressorY == 0 && agressorX - protectorX == 0)){
|
||
agentList[agentId]->setDistTarget(0);
|
||
return QLineF(0, 0, 0, 0);
|
||
}
|
||
// Расстояние от игрока до прямой, на которой расположены агрессор и защитник
|
||
qreal dist = (mod((protectorY - agressorY)*agressorX + (agressorX - protectorX)*agressorY)) /
|
||
sqrt(pow((qreal)protectorY - agressorY, 2) + pow((qreal)agressorX - protectorX, 2));
|
||
|
||
// Игрок стремится к данной прямой по нормали.
|
||
shiftVector = QLineF (0, 0, protectorY - agressorY, agressorX - protectorX);
|
||
|
||
// Если свободный член в уравнении прямой Ax + By + C = 0 больше нуля, нормаль нужно перевернуть + нормализация вектора.
|
||
if (agressorX*(protectorY - agressorY) + agressorY*(agressorX - protectorX) < 0){
|
||
shiftVector = QLineF(shiftVector.dx(), shiftVector.dy(), 0, 0);
|
||
|
||
}
|
||
else {}
|
||
// Установление места назначения в точности на пересечение нормали с прямой
|
||
shiftVector.setLength(dist);
|
||
|
||
QLineF fromShiftToA (shiftVector.dx() + agentList[agentId]->getCoordX(), shiftVector.dy() + agentList[agentId]->getCoordY(), agressorX, agressorY);
|
||
QLineF fromShiftToPr (shiftVector.dx() + agentList[agentId]->getCoordX(), shiftVector.dy() + agentList[agentId]->getCoordY(), protectorX, protectorY);
|
||
|
||
// Режим труса
|
||
if(agentList[agentId]->getMode() == 0){
|
||
// Проверка, не окажется ли место назначения между агрессором и протектором
|
||
// (а то и вовсе за агрессором)
|
||
if((fromShiftToA.length() < fromShiftToPr.length()) && (fromShiftToA.length() >= fromAtoPr.length())){
|
||
// Установление желаемых координат в точности за защитником
|
||
QLineF hide = fromAtoPr;
|
||
hide.setLength(enmity);
|
||
shiftVector = QLineF(0, 0, protectorX + hide.dx() - agentList[agentId]->getCoordX(), protectorY + hide.dy() - agentList[agentId]->getCoordY());
|
||
}
|
||
else{}
|
||
agentList[agentId]->setDistTarget(shiftVector.length());
|
||
// Проверка: не проскочить мимо цели
|
||
if (shiftVector.length() >= agentList[agentId]->getSpeed()){
|
||
shiftVector.setLength(agentList[agentId]->getSpeed());
|
||
}
|
||
else {}
|
||
}
|
||
// Режим защитника
|
||
else if (agentList[agentId]->getMode() == 1){
|
||
// Проверка, попадает ли точка назначения в промежуток между A и B
|
||
if((fromAtoPr.length() < fromShiftToPr.length()) && (fromAtoPr.length() >= fromShiftToPr.length())){
|
||
// Установление желаемых координат посередине между А и В
|
||
QLineF AtoPr = fromAtoPr;
|
||
AtoPr.setLength(fromAtoPr.length()/2);
|
||
shiftVector = QLineF(0, 0, agressorX + AtoPr.dx() - agentList[agentId]->getCoordX(),
|
||
agressorY + AtoPr.dy() - agentList[agentId]->getCoordY());
|
||
}
|
||
else {}
|
||
agentList[agentId]->setDistTarget(shiftVector.length());
|
||
// Проверка: не проскочить мимо цели
|
||
if (shiftVector.length() >= agentList[agentId]->getSpeed()){
|
||
shiftVector.setLength(agentList[agentId]->getSpeed());
|
||
}
|
||
else {}
|
||
}
|
||
else {}
|
||
}
|
||
else{}
|
||
//qApp->processEvents();
|
||
|
||
//ToDo: сократить код
|
||
|
||
// Для непериодичной сцены
|
||
if (period == false){
|
||
// Проверка на выход за пределы окна, "подрезание" вектора перемещения
|
||
if (agentList[agentId]->getCoordX() + shiftVector.dx() > 300 - agentList[agentId]->getRadius()){
|
||
shiftVector = QLineF(0, 0, 300 - agentList[agentId]->getRadius() - agentList[agentId]->getCoordX(), shiftVector.dy());
|
||
}
|
||
else if(agentList[agentId]->getCoordX() + shiftVector.dx() < -300 + agentList[agentId]->getRadius()){
|
||
shiftVector = QLineF(0, 0, -300 + agentList[agentId]->getRadius() - agentList[agentId]->getCoordX(), shiftVector.dy());
|
||
}
|
||
else{}
|
||
if(agentList[agentId]->getCoordY() + shiftVector.dy() > 300 - agentList[agentId]->getRadius()){
|
||
shiftVector = QLineF(0, 0, shiftVector.dx(), 300 - agentList[agentId]->getRadius() - agentList[agentId]->getCoordY());
|
||
}
|
||
else if(agentList[agentId]->getCoordY() + shiftVector.dy() < -300 + agentList[agentId]->getRadius()){
|
||
shiftVector = QLineF(0, 0, shiftVector.dx(), -300 + agentList[agentId]->getRadius() - agentList[agentId]->getCoordY());
|
||
}
|
||
else {}
|
||
}
|
||
// Для периодичной сцены
|
||
else{
|
||
// Пересчёт координат с учётом периодичности сцены
|
||
shiftVector = periodicTransf(agentId, shiftVector);
|
||
}
|
||
//qApp->processEvents();
|
||
|
||
if(enmity > 0){
|
||
// Проверка на столкновение
|
||
//if (agentId < agentList.size() - 1){
|
||
QList<Agent*> dangerAgents = DangerGamer(/*dangerAgents, */ agentId, shiftVector.dx(), shiftVector.dy(), enmity);
|
||
//}
|
||
//QList<Agent*> dangerAgents = DangerGamer(agentId, shiftVector.dx(), shiftVector.dy(), enmity);
|
||
if(dangerAgents.isEmpty()){
|
||
//std:: cout << "req start" << std::endl;
|
||
return shiftVector;
|
||
}
|
||
else{
|
||
// Попытка обойти все потенциальные угрозы стороной. Если не получается, игрок остаётся на месте
|
||
for (int i = 0; i < dangerAgents.size(); i++){
|
||
QLineF toAgent = QLineF(agentList[agentId]->getCoordX() + shiftVector.dx(), agentList[agentId]->getCoordY() + shiftVector.dy(),
|
||
dangerAgents[i]->getCoordX(), dangerAgents[i]->getCoordY());
|
||
shiftVector = toAgent.normalVector().unitVector();
|
||
if (period == false){ // для непериодичной сцены - проверка на покидание окна + на новое столкновение
|
||
QList<Agent*> futureDangerous = DangerGamer(agentId, shiftVector.dx(), shiftVector.dy(), enmity);
|
||
if(futureDangerous.isEmpty() && leaveWindowX(agentId, shiftVector) && leaveWindowY(agentId, shiftVector) == false){
|
||
return shiftVector;
|
||
}
|
||
else{}
|
||
futureDangerous.clear();
|
||
}
|
||
else{ // для периодичной сцены - преобразование координат из-за периодичности + проверка на новое столкновение
|
||
shiftVector = periodicTransf(agentId, shiftVector);
|
||
QList<Agent*> futureDangerous = DangerGamer(agentId, shiftVector.dx(), shiftVector.dy(), enmity);
|
||
if(futureDangerous.isEmpty()){
|
||
return shiftVector;
|
||
}
|
||
else{}
|
||
futureDangerous.clear();
|
||
}
|
||
//qApp->processEvents();
|
||
//std::cout << agentId << " Check for crush is done" << std::endl;
|
||
}
|
||
//std:: cout << "req start" << std::endl;
|
||
return QLineF(0, 0, 0, 0);
|
||
}
|
||
}
|
||
else {
|
||
//std::cout << "total shift X: " << shiftVector.dx() << " total shift Y: " << shiftVector.dy() << std::endl;
|
||
//std:: cout << "req start" << std::endl;
|
||
return shiftVector;
|
||
}
|
||
}
|
||
|
||
void Model::modelIteration() {
|
||
QList <QLineF> shift;
|
||
// Вычисление новых координат
|
||
// QList<QList<Agent *>> dangerAgents;
|
||
// for (int i = 0; i < agentList.size(); i++) {
|
||
// QList<Agent *> content;
|
||
// dangerAgents.push_back(content);
|
||
// }
|
||
for(int i = 0; i < agentList.size(); i++){
|
||
shift.append(requiredVector(i));
|
||
//std:: cout << "INFO: iteration: " << step << " agent: " << i << std::endl;
|
||
}
|
||
// Смена координат на новые
|
||
for(int i = 0; i < agentList.size(); i++){
|
||
agentList[i]->gameEvent(shift[i]);
|
||
}
|
||
//qApp->processEvents();
|
||
shift.clear();
|
||
}
|
||
|
||
void Model::clear() {
|
||
qDeleteAll(agentList.begin(), agentList.end());
|
||
agentList.clear();
|
||
}
|