Files
DEC/DEC_GUI/Agressor/model.cpp
2024-10-03 18:43:04 +07:00

357 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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();
}