#include #include #include #include #include #include #include #include #include "model.h" static const qreal pi = 3.14159265358979323846264338327950288419717; /* * Знак числа */ template 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 Model::DangerGamer(uint index, qreal x, qreal y, uint dist){ //std:: cout << "INFO: Manager::DangerGamer start" << std::endl; QList 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 > Model::DangerGamer(QList > 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 dangerAgents = DangerGamer(/*dangerAgents, */ agentId, shiftVector.dx(), shiftVector.dy(), enmity); //} //QList 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 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 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 shift; // Вычисление новых координат // QList> dangerAgents; // for (int i = 0; i < agentList.size(); i++) { // QList 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(); }