00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifndef ASEBA_ASSERT
00033 #define ASEBA_ASSERT
00034 #endif
00035
00036 #include <../../vm/vm.h>
00037 #include "../../vm/natives.h"
00038 #include <../../common/consts.h>
00039 #include <../../transport/buffer/vm-buffer.h>
00040 #include <dashel/dashel.h>
00041 #include <enki/PhysicalEngine.h>
00042 #include <enki/robots/e-puck/EPuck.h>
00043 #include <iostream>
00044 #include <QtGui>
00045 #include <QtDebug>
00046 #include "challenge.h"
00047 #include <challenge.moc>
00048 #include <string.h>
00049
00050 #define SIMPLIFIED_EPUCK
00051
00052 static void initTexturesResources()
00053 {
00054 Q_INIT_RESOURCE(challenge_textures);
00055 }
00056
00058 template<typename Derived, typename Base>
00059 inline Derived polymorphic_downcast(Base base)
00060 {
00061 Derived derived = dynamic_cast<Derived>(base);
00062 assert(derived);
00063 return derived;
00064 }
00065
00066
00067
00068 namespace Enki
00069 {
00070 class AsebaFeedableEPuck;
00071 }
00072
00073
00074 typedef QMap<AsebaVMState*, Enki::AsebaFeedableEPuck*> VmEPuckMap;
00075 static VmEPuckMap asebaEPuckMap;
00076
00077 static AsebaNativeFunctionPointer nativeFunctions[] =
00078 {
00079 ASEBA_NATIVES_STD_FUNCTIONS,
00080 };
00081
00082 static const AsebaNativeFunctionDescription* nativeFunctionsDescriptions[] =
00083 {
00084 ASEBA_NATIVES_STD_DESCRIPTIONS,
00085 0
00086 };
00087
00088
00089 static QString localName;
00090
00091 extern "C" AsebaVMDescription vmDescription_en;
00092 extern "C" AsebaVMDescription vmDescription_fr;
00093
00094 namespace Enki
00095 {
00096 #define EPUCK_FEEDER_INITIAL_ENERGY 10
00097 #define EPUCK_FEEDER_THRESHOLD_HIDE 2
00098 #define EPUCK_FEEDER_THRESHOLD_SHOW 4
00099 #define EPUCK_FEEDER_RADIUS 5
00100 #define EPUCK_FEEDER_RADIUS_DEAD 6
00101 #define EPUCK_FEEDER_RANGE 10
00102
00103 #define EPUCK_FEEDER_COLOR_ACTIVE Color::blue
00104 #define EPUCK_FEEDER_COLOR_INACTIVE Color::red
00105 #define EPUCK_FEEDER_COLOR_DEAD Color::gray
00106
00107 #define EPUCK_FEEDER_D_ENERGY 4
00108 #define EPUCK_FEEDER_RECHARGE_RATE 0.5
00109 #define EPUCK_FEEDER_MAX_ENERGY 100
00110
00111 #define EPUCK_FEEDER_LIFE_SPAN 60
00112 #define EPUCK_FEEDER_DEATH_SPAN 10
00113
00114 #define EPUCK_INITIAL_ENERGY 10
00115 #define EPUCK_ENERGY_CONSUMPTION_RATE 1
00116
00117 #define DEATH_ANIMATION_STEPS 30
00118 #define PORT_BASE ASEBA_DEFAULT_PORT
00119
00120 extern GLint GenFeederBase();
00121 extern GLint GenFeederCharge0();
00122 extern GLint GenFeederCharge1();
00123 extern GLint GenFeederCharge2();
00124 extern GLint GenFeederCharge3();
00125 extern GLint GenFeederRing();
00126
00127 class FeedableEPuck: public EPuck
00128 {
00129 public:
00130 double energy;
00131 double score;
00132 QString name;
00133 int diedAnimation;
00134
00135 public:
00136 FeedableEPuck() : EPuck(CAPABILITY_BASIC_SENSORS | CAPABILITY_CAMERA), energy(EPUCK_INITIAL_ENERGY), score(0), diedAnimation(-1) { }
00137
00138 virtual void controlStep(double dt)
00139 {
00140 EPuck::controlStep(dt);
00141
00142 energy -= dt * EPUCK_ENERGY_CONSUMPTION_RATE;
00143 score += dt;
00144 if (energy < 0)
00145 {
00146 score /= 2;
00147 energy = EPUCK_INITIAL_ENERGY;
00148 diedAnimation = DEATH_ANIMATION_STEPS;
00149 }
00150 else if (diedAnimation >= 0)
00151 diedAnimation--;
00152 }
00153 };
00154
00155 class AsebaFeedableEPuck : public FeedableEPuck, public Dashel::Hub
00156 {
00157 public:
00158 Dashel::Stream* stream;
00159 AsebaVMState vm;
00160 std::valarray<unsigned short> bytecode;
00161 std::valarray<signed short> stack;
00162 struct Variables
00163 {
00164 sint16 speedL;
00165 sint16 speedR;
00166 sint16 colorR;
00167 sint16 colorG;
00168 sint16 colorB;
00169 #ifdef SIMPLIFIED_EPUCK
00170 sint16 dist_A[8];
00171 sint16 dist_B[8];
00172 #else
00173 sint16 prox[8];
00174 #endif
00175 #ifdef SIMPLIFIED_EPUCK
00176 sint16 camR_A[3];
00177 sint16 camR_B[3];
00178 sint16 camG_A[3];
00179 sint16 camG_B[3];
00180 sint16 camB_A[3];
00181 sint16 camB_B[3];
00182 #else
00183 sint16 camR[60];
00184 sint16 camG[60];
00185 sint16 camB[60];
00186 #endif
00187 sint16 energy;
00188 sint16 user[1024];
00189 } variables;
00190 int port;
00191
00192 uint16 lastMessageSource;
00193 std::valarray<uint8> lastMessageData;
00194
00195 public:
00196 AsebaFeedableEPuck(int id) :
00197 stream(0)
00198 {
00199 asebaEPuckMap[&vm] = this;
00200
00201 vm.nodeId = 1;
00202
00203 bytecode.resize(512);
00204 vm.bytecode = &bytecode[0];
00205 vm.bytecodeSize = bytecode.size();
00206
00207 stack.resize(64);
00208 vm.stack = &stack[0];
00209 vm.stackSize = stack.size();
00210
00211 vm.variables = reinterpret_cast<sint16 *>(&variables);
00212 vm.variablesSize = sizeof(variables) / sizeof(sint16);
00213
00214 port = PORT_BASE+id;
00215 try
00216 {
00217 Dashel::Hub::connect(QString("tcpin:port=%1").arg(port).toStdString());
00218 }
00219 catch (Dashel::DashelException e)
00220 {
00221 QMessageBox::critical(0, QApplication::tr("Aseba Challenge"), QApplication::tr("Cannot create listening port %0: %1").arg(port).arg(e.what()));
00222 abort();
00223 }
00224
00225 AsebaVMInit(&vm);
00226
00227 variables.colorG = 100;
00228 }
00229
00230 virtual ~AsebaFeedableEPuck()
00231 {
00232 asebaEPuckMap.remove(&vm);
00233 }
00234
00235 public:
00236 void connectionCreated(Dashel::Stream *stream)
00237 {
00238 std::string targetName = stream->getTargetName();
00239 if (targetName.substr(0, targetName.find_first_of(':')) == "tcp")
00240 {
00241 qDebug() << this << " : New client connected.";
00242 if (this->stream)
00243 {
00244 closeStream(this->stream);
00245 qDebug() << this << " : Disconnected old client.";
00246 }
00247 this->stream = stream;
00248 }
00249 }
00250
00251 void incomingData(Dashel::Stream *stream)
00252 {
00253 uint16 len;
00254 stream->read(&len, 2);
00255 stream->read(&lastMessageSource, 2);
00256 lastMessageData.resize(len+2);
00257 stream->read(&lastMessageData[0], lastMessageData.size());
00258
00259 if (*((uint16*)&lastMessageData[0]) >= 0xA000)
00260 AsebaProcessIncomingEvents(&vm);
00261 else
00262 qDebug() << this << " : Non debug event dropped.";
00263 }
00264
00265 void connectionClosed(Dashel::Stream *stream, bool abnormal)
00266 {
00267 if (stream == this->stream)
00268 {
00269 this->stream = 0;
00270
00271 vm.breakpointsCount = 0;
00272 }
00273 if (abnormal)
00274 qDebug() << this << " : Client has disconnected unexpectedly.";
00275 else
00276 qDebug() << this << " : Client has disconnected properly.";
00277 }
00278
00279 double toDoubleClamp(sint16 val, double mul, double min, double max)
00280 {
00281 double v = static_cast<double>(val) * mul;
00282 if (v > max)
00283 v = max;
00284 else if (v < min)
00285 v = min;
00286 return v;
00287 }
00288
00289 void controlStep(double dt)
00290 {
00291
00292 leftSpeed = toDoubleClamp(variables.speedL, 1, -13, 13);
00293 rightSpeed = toDoubleClamp(variables.speedR, 1, -13, 13);
00294 Color c;
00295 c.setR(toDoubleClamp(variables.colorR, 0.01, 0, 1));
00296 c.setG(toDoubleClamp(variables.colorG, 0.01, 0, 1));
00297 c.setB(toDoubleClamp(variables.colorB, 0.01, 0, 1));
00298 setColor(c);
00299
00300
00301 FeedableEPuck::controlStep(dt);
00302
00303
00304 #ifdef SIMPLIFIED_EPUCK
00305 variables.dist_A[0] = static_cast<sint16>(infraredSensor0.getDist());
00306 variables.dist_A[1] = static_cast<sint16>(infraredSensor1.getDist());
00307 variables.dist_A[2] = static_cast<sint16>(infraredSensor2.getDist());
00308 variables.dist_A[3] = static_cast<sint16>(infraredSensor3.getDist());
00309 variables.dist_A[4] = static_cast<sint16>(infraredSensor4.getDist());
00310 variables.dist_A[5] = static_cast<sint16>(infraredSensor5.getDist());
00311 variables.dist_A[6] = static_cast<sint16>(infraredSensor6.getDist());
00312 variables.dist_A[7] = static_cast<sint16>(infraredSensor7.getDist());
00313 for (size_t i = 0; i < 8; ++i)
00314 variables.dist_B[i] = variables.dist_A[i];
00315 #else
00316 variables.prox[0] = static_cast<sint16>(infraredSensor0.finalValue);
00317 variables.prox[1] = static_cast<sint16>(infraredSensor1.finalValue);
00318 variables.prox[2] = static_cast<sint16>(infraredSensor2.finalValue);
00319 variables.prox[3] = static_cast<sint16>(infraredSensor3.finalValue);
00320 variables.prox[4] = static_cast<sint16>(infraredSensor4.finalValue);
00321 variables.prox[5] = static_cast<sint16>(infraredSensor5.finalValue);
00322 variables.prox[6] = static_cast<sint16>(infraredSensor6.finalValue);
00323 variables.prox[7] = static_cast<sint16>(infraredSensor7.finalValue);
00324 #endif
00325
00326 #ifdef SIMPLIFIED_EPUCK
00327 for (size_t i = 0; i < 3; i++)
00328 {
00329 double sumR = 0;
00330 double sumG = 0;
00331 double sumB = 0;
00332 for (size_t j = 0; j < 20; j++)
00333 {
00334 size_t index = 59 - (i * 20 + j);
00335 sumR += camera.image[index].r();
00336 sumG += camera.image[index].g();
00337 sumB += camera.image[index].b();
00338 }
00339 variables.camR_A[i] = variables.camR_B[i] = static_cast<sint16>(sumR * 100. / 20.);
00340 variables.camG_A[i] = variables.camG_B[i] = static_cast<sint16>(sumG * 100. / 20.);
00341 variables.camB_A[i] = variables.camB_B[i] = static_cast<sint16>(sumB * 100. / 20.);
00342 }
00343 #else
00344 for (size_t i = 0; i < 60; i++)
00345 {
00346 variables.camR[i] = static_cast<sint16>(camera.image[i].r() * 100.);
00347 variables.camG[i] = static_cast<sint16>(camera.image[i].g() * 100.);
00348 variables.camB[i] = static_cast<sint16>(camera.image[i].b() * 100.);
00349 }
00350 #endif
00351
00352 variables.energy = static_cast<sint16>(energy);
00353
00354
00355 Hub::step();
00356
00357
00358 AsebaVMRun(&vm, 65535);
00359
00360
00361 if (AsebaMaskIsClear(vm.flags, ASEBA_VM_STEP_BY_STEP_MASK) || AsebaMaskIsClear(vm.flags, ASEBA_VM_EVENT_ACTIVE_MASK))
00362 AsebaVMSetupEvent(&vm, ASEBA_EVENT_LOCAL_EVENTS_START);
00363 }
00364 };
00365
00366 class EPuckFeeding : public LocalInteraction
00367 {
00368 public:
00369 double energy;
00370 double age;
00371 bool alive;
00372
00373 public :
00374 EPuckFeeding(Robot *owner, double age) : energy(EPUCK_FEEDER_INITIAL_ENERGY), age(age)
00375 {
00376 r = EPUCK_FEEDER_RANGE;
00377 this->owner = owner;
00378 alive = true;
00379 }
00380
00381 void objectStep(double dt, World *w, PhysicalObject *po)
00382 {
00383 if (alive)
00384 {
00385 FeedableEPuck *epuck = dynamic_cast<FeedableEPuck *>(po);
00386 if (epuck && energy > 0)
00387 {
00388 double dEnergy = dt * EPUCK_FEEDER_D_ENERGY;
00389 epuck->energy += dEnergy;
00390 energy -= dEnergy;
00391 if (energy < EPUCK_FEEDER_THRESHOLD_HIDE)
00392 owner->setColor(EPUCK_FEEDER_COLOR_INACTIVE);
00393 }
00394 }
00395 }
00396
00397 void finalize(double dt, World *w)
00398 {
00399 age += dt;
00400 if (alive)
00401 {
00402 if ((energy < EPUCK_FEEDER_THRESHOLD_SHOW) && (energy+dt >= EPUCK_FEEDER_THRESHOLD_SHOW))
00403 owner->setColor(EPUCK_FEEDER_COLOR_ACTIVE);
00404 energy += EPUCK_FEEDER_RECHARGE_RATE * dt;
00405 if (energy > EPUCK_FEEDER_MAX_ENERGY)
00406 energy = EPUCK_FEEDER_MAX_ENERGY;
00407
00408 if (age > EPUCK_FEEDER_LIFE_SPAN)
00409 {
00410 alive = false;
00411 age = 0;
00412 owner->setColor(EPUCK_FEEDER_COLOR_DEAD);
00413 owner->setCylindric(EPUCK_FEEDER_RADIUS_DEAD, owner->getHeight(), owner->getMass());
00414 }
00415 }
00416 else
00417 {
00418 if (age > EPUCK_FEEDER_DEATH_SPAN)
00419 {
00420 alive = true;
00421 age = 0;
00422 owner->setColor(EPUCK_FEEDER_COLOR_ACTIVE);
00423 owner->setCylindric(EPUCK_FEEDER_RADIUS, owner->getHeight(), owner->getMass());
00424 }
00425 }
00426 }
00427 };
00428
00429 class EPuckFeeder : public Robot
00430 {
00431 public:
00432 EPuckFeeding feeding;
00433
00434 public:
00435 EPuckFeeder(double age) : feeding(this, age)
00436 {
00437 setCylindric(EPUCK_FEEDER_RADIUS, 5, -1);
00438 addLocalInteraction(&feeding);
00439 setColor(EPUCK_FEEDER_COLOR_ACTIVE);
00440 }
00441 };
00442
00443 class FeederModel : public ViewerWidget::CustomRobotModel
00444 {
00445 public:
00446 FeederModel(ViewerWidget* viewer)
00447 {
00448 textures.resize(2);
00449 textures[0] = viewer->bindTexture(QPixmap(QString(":/textures/feeder.png")), GL_TEXTURE_2D);
00450 textures[1] = viewer->bindTexture(QPixmap(QString(":/textures/feederr.png")), GL_TEXTURE_2D, GL_LUMINANCE8);
00451 lists.resize(6);
00452 lists[0] = GenFeederBase();
00453 lists[1] = GenFeederCharge0();
00454 lists[2] = GenFeederCharge1();
00455 lists[3] = GenFeederCharge2();
00456 lists[4] = GenFeederCharge3();
00457 lists[5] = GenFeederRing();
00458 }
00459
00460 void cleanup(ViewerWidget* viewer)
00461 {
00462 for (int i = 0; i < textures.size(); i++)
00463 viewer->deleteTexture(textures[i]);
00464 for (int i = 0; i < lists.size(); i++)
00465 glDeleteLists(lists[i], 1);
00466 }
00467
00468 virtual void draw(PhysicalObject* object) const
00469 {
00470 EPuckFeeder* feeder = polymorphic_downcast<EPuckFeeder*>(object);
00471 double age = feeder->feeding.age;
00472 bool alive = feeder->feeding.alive;
00473
00474 glEnable(GL_TEXTURE_2D);
00475 glBindTexture(GL_TEXTURE_2D, textures[0]);
00476
00477 glPushMatrix();
00478 double disp;
00479 if (age < M_PI/2)
00480 {
00481
00482 if (alive)
00483 disp = -1 + sin(age);
00484 else
00485 disp = -sin(age);
00486 }
00487 else
00488 {
00489 if (alive)
00490 disp = 0;
00491 else
00492 disp = -1;
00493 }
00494
00495 glTranslated(0, 0, 4.3*disp-0.2);
00496
00497
00498 glColor3d(1, 1, 1);
00499 glCallList(lists[0]);
00500
00501
00502 glColor3d(0.6+object->getColor().components[0]-0.3*object->getColor().components[1]-0.3*object->getColor().components[2], 0.6+object->getColor().components[1]-0.3*object->getColor().components[0]-0.3*object->getColor().components[2], 0.6+object->getColor().components[2]-0.3*object->getColor().components[0]-0.3*object->getColor().components[1]);
00503 glCallList(lists[5]);
00504
00505
00506 glColor3d(0.3, 0.3, 1);
00507 int foodAmount = (int)((feeder->feeding.energy * 5) / (EPUCK_FEEDER_MAX_ENERGY + 0.001));
00508 assert(foodAmount <= 4);
00509 for (int i = 0; i < foodAmount; i++)
00510 glCallList(lists[1+i]);
00511
00512 glPopMatrix();
00513
00514
00515 glColor3d(1, 1, 1);
00516 glBindTexture(GL_TEXTURE_2D, textures[1]);
00517 glDisable(GL_LIGHTING);
00518 glEnable(GL_BLEND);
00519 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
00520
00521
00522 glPushMatrix();
00523
00524 glDepthMask( GL_FALSE );
00525 glEnable(GL_POLYGON_OFFSET_FILL);
00526
00527 glBegin(GL_QUADS);
00528 glTexCoord2f(1.f, 0.f);
00529 glVertex2f(-7.5f, -7.5f);
00530 glTexCoord2f(1.f, 1.f);
00531 glVertex2f(7.5f, -7.5f);
00532 glTexCoord2f(0.f, 1.f);
00533 glVertex2f(7.5f, 7.5f);
00534 glTexCoord2f(0.f, 0.f);
00535 glVertex2f(-7.5f, 7.5f);
00536 glEnd();
00537 glDisable(GL_POLYGON_OFFSET_FILL);
00538 glDepthMask( GL_TRUE );
00539 glPopMatrix();
00540
00541 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00542 glDisable(GL_BLEND);
00543 glEnable(GL_LIGHTING);
00544
00545 glDisable(GL_TEXTURE_2D);
00546 }
00547
00548 virtual void drawSpecial(PhysicalObject* object, int param) const
00549 {
00550
00551
00552
00553
00554
00555 }
00556 };
00557
00558
00559
00560 ChallengeViewer::ChallengeViewer(World* world, int ePuckCount) : ViewerWidget(world), ePuckCount(ePuckCount)
00561 {
00562 savingVideo = false;
00563 initTexturesResources();
00564
00565 helpViewer = new QTextBrowser();
00566 helpViewer->setReadOnly(true);
00567 helpViewer->resize(600, 500);
00568
00569 if (localName.left(2) == "fr")
00570 helpViewer->setSource(QString("qrc:/doc/challenge.fr.html"));
00571 else
00572 helpViewer->setSource(QString("qrc:/doc/challenge.en.html"));
00573 helpViewer->moveCursor(QTextCursor::Start);
00574 helpViewer->setWindowTitle(tr("Aseba Challenge Help"));
00575
00576 #ifndef Q_WS_MAC
00577
00578 QVBoxLayout *vLayout = new QVBoxLayout;
00579 QHBoxLayout *hLayout = new QHBoxLayout;
00580
00581 hLayout->addStretch();
00582
00583 menuFrame = new QFrame();
00584 menuFrame->setFrameStyle(QFrame::Box | QFrame::Plain);
00585
00586 QHBoxLayout *frameLayout = new QHBoxLayout;
00587 addRobotButton = new QPushButton(tr("Add a new robot"));
00588 frameLayout->addWidget(addRobotButton);
00589 delRobotButton = new QPushButton(tr("Remove all robots"));
00590 frameLayout->addWidget(delRobotButton);
00591 autoCamera = new QCheckBox(tr("Auto camera"));
00592 frameLayout->addWidget(autoCamera);
00593 hideButtons = new QCheckBox(tr("Auto hide"));
00594 frameLayout->addWidget(hideButtons);
00595 helpButton = new QPushButton(tr("Help"));
00596 frameLayout->addWidget(helpButton);
00597 menuFrame->setLayout(frameLayout);
00598
00599 hLayout->addWidget(menuFrame);
00600 hLayout->addStretch();
00601 vLayout->addLayout(hLayout);
00602 vLayout->addStretch();
00603 setLayout(vLayout);
00604
00605 connect(addRobotButton, SIGNAL(clicked()), SLOT(addNewRobot()));
00606 connect(delRobotButton, SIGNAL(clicked()), SLOT(removeRobot()));
00607 connect(helpButton, SIGNAL(clicked()), helpViewer, SLOT(show()));
00608
00609 #else // Q_WS_MAC
00610
00611 QMenuBar *menuBar = new QMenuBar(0);
00612 QMenu *menu = menuBar->addMenu(tr("Simulator control"));
00613 menu->addAction(tr("Add a new robot"), this, SLOT(addNewRobot()));
00614 menu->addAction(tr("Remove all robots"), this, SLOT(removeRobot()));
00615 menu->addSeparator();
00616 autoCamera = new QAction(tr("Auto camera"), 0);
00617 autoCamera->setCheckable(true);
00618 menu->addAction(autoCamera);
00619 menu->addSeparator();
00620 menu->addAction(tr("Help"), helpViewer, SLOT(show()));
00621
00622 #endif // Q_WS_MAC
00623
00624 connect(this, SIGNAL(windowClosed()), helpViewer, SLOT(close()));
00625
00626 int res = QFontDatabase::addApplicationFont(":/fonts/SF Old Republic SC.ttf");
00627 Q_ASSERT(res != -1);
00628
00629 titleFont = QFont("SF Old Republic SC", 20);
00630 entryFont = QFont("SF Old Republic SC", 23);
00631 labelFont = QFont("SF Old Republic SC", 16);
00632
00633 setMouseTracking(true);
00634 setAttribute(Qt::WA_OpaquePaintEvent);
00635 setAttribute(Qt::WA_NoSystemBackground);
00636 setAutoFillBackground(false);
00637 resize(780, 560);
00638 }
00639
00640 void ChallengeViewer::addNewRobot()
00641 {
00642 bool ok;
00643 QString eventName = QInputDialog::getText(this, tr("Add a new robot"), tr("Robot name:"), QLineEdit::Normal, "", &ok);
00644 if (ok && !eventName.isEmpty())
00645 {
00646
00647 Enki::AsebaFeedableEPuck* epuck = new Enki::AsebaFeedableEPuck(ePuckCount++);
00648 epuck->pos.x = Enki::random.getRange(120)+10;
00649 epuck->pos.y = Enki::random.getRange(120)+10;
00650 epuck->name = eventName;
00651 world->addObject(epuck);
00652 }
00653 }
00654
00655 void ChallengeViewer::removeRobot()
00656 {
00657 std::set<AsebaFeedableEPuck *> toFree;
00658
00659 for (World::ObjectsIterator it = world->objects.begin(); it != world->objects.end(); ++it)
00660 {
00661 AsebaFeedableEPuck *epuck = dynamic_cast<AsebaFeedableEPuck*>(*it);
00662 if (epuck)
00663 toFree.insert(epuck);
00664 }
00665
00666 for (std::set<AsebaFeedableEPuck *>::iterator it = toFree.begin(); it != toFree.end(); ++it)
00667 {
00668 world->removeObject(*it);
00669 delete *it;
00670 }
00671 ePuckCount = 0;
00672 }
00673
00674 void ChallengeViewer::timerEvent(QTimerEvent * event)
00675 {
00676 if (autoCamera->isChecked())
00677 {
00678 altitude = 70;
00679 yaw += 0.002;
00680 pos = QPointF(-world->w/2 + 120*sin(yaw+M_PI/2), -world->h/2 + 120*cos(yaw+M_PI/2));
00681 if (yaw > 2*M_PI)
00682 yaw -= 2*M_PI;
00683 pitch = M_PI/7 ;
00684 }
00685 ViewerWidget::timerEvent(event);
00686 }
00687
00688 void ChallengeViewer::mouseMoveEvent ( QMouseEvent * event )
00689 {
00690 #ifndef Q_WS_MAC
00691
00692 bool isInButtonArea = event->y() < addRobotButton->y() + addRobotButton->height() + 10;
00693 if (hideButtons->isChecked())
00694 {
00695 if (isInButtonArea && !addRobotButton->isVisible())
00696 {
00697 menuFrame->show();
00698 }
00699 if (!isInButtonArea && addRobotButton->isVisible())
00700 {
00701 menuFrame->hide();
00702 }
00703 }
00704
00705 #endif // Q_WS_MAC
00706
00707 ViewerWidget::mouseMoveEvent(event);
00708 }
00709
00710 void ChallengeViewer::keyPressEvent ( QKeyEvent * event )
00711 {
00712 if (event->key() == Qt::Key_V)
00713 savingVideo = true;
00714 else
00715 ViewerWidget::keyPressEvent(event);
00716 }
00717
00718 void ChallengeViewer::keyReleaseEvent ( QKeyEvent * event )
00719 {
00720 if (event->key() == Qt::Key_V)
00721 savingVideo = false;
00722 else
00723 ViewerWidget::keyReleaseEvent (event);
00724 }
00725
00726 void ChallengeViewer::closeEvent ( QCloseEvent * event )
00727 {
00728 if (event->isAccepted())
00729 emit windowClosed();
00730 }
00731
00732 void ChallengeViewer::drawQuad2D(double x, double y, double w, double ar)
00733 {
00734 double thisAr = (double)width() / (double)height();
00735 double h = (w * thisAr) / ar;
00736 glBegin(GL_QUADS);
00737 glTexCoord2d(0, 1);
00738 glVertex2d(x, y);
00739 glTexCoord2d(1, 1);
00740 glVertex2d(x+w, y);
00741 glTexCoord2d(1, 0);
00742 glVertex2d(x+w, y+h);
00743 glTexCoord2d(0, 0);
00744 glVertex2d(x, y+h);
00745 glEnd();
00746 }
00747
00748 void ChallengeViewer::initializeGL()
00749 {
00750 ViewerWidget::initializeGL();
00751 }
00752
00753 void ChallengeViewer::renderObjectsTypesHook()
00754 {
00755
00756 managedObjects[&typeid(EPuckFeeder)] = new FeederModel(this);
00757 managedObjectsAliases[&typeid(AsebaFeedableEPuck)] = &typeid(EPuck);
00758 }
00759
00760 void ChallengeViewer::displayObjectHook(PhysicalObject *object)
00761 {
00762 FeedableEPuck *epuck = dynamic_cast<FeedableEPuck*>(object);
00763 if ((epuck) && (epuck->diedAnimation >= 0))
00764 {
00765 ViewerUserData *userData = dynamic_cast<ViewerUserData *>(epuck->userData);
00766 assert(userData);
00767
00768 double dist = (double)(DEATH_ANIMATION_STEPS - epuck->diedAnimation);
00769 double coeff = (double)(epuck->diedAnimation) / DEATH_ANIMATION_STEPS;
00770 glColor3d(0.2*coeff, 0.2*coeff, 0.2*coeff);
00771 glTranslated(0, 0, 2. * dist);
00772 userData->drawSpecial(object);
00773 }
00774
00775
00776
00777 }
00778
00779 void ChallengeViewer::sceneCompletedHook()
00780 {
00781
00782 qglColor(Qt::black);
00783 QMultiMap<int, QStringList> scores;
00784 for (World::ObjectsIterator it = world->objects.begin(); it != world->objects.end(); ++it)
00785 {
00786 AsebaFeedableEPuck *epuck = dynamic_cast<AsebaFeedableEPuck*>(*it);
00787 if (epuck)
00788 {
00789 QStringList entry;
00790 entry << epuck->name << QString::number(epuck->port) << QString::number((int)epuck->energy) << QString::number((int)epuck->score);
00791 scores.insert((int)epuck->score, entry);
00792 renderText(epuck->pos.x, epuck->pos.y, 10, epuck->name, labelFont);
00793 }
00794 }
00795
00796
00797 QImage scoreBoard(512, 256, QImage::Format_ARGB32);
00798 scoreBoard.setDotsPerMeterX(2350);
00799 scoreBoard.setDotsPerMeterY(2350);
00800 QPainter painter(&scoreBoard);
00801
00802 painter.fillRect(scoreBoard.rect(), QColor(224,255,224,196));
00803
00804
00805 painter.setBrush(Qt::NoBrush);
00806 QPen pen(Qt::black);
00807 pen.setWidth(2);
00808 painter.setPen(pen);
00809 painter.drawRect(scoreBoard.rect());
00810 pen.setWidth(1);
00811 painter.setPen(pen);
00812 painter.drawLine(22, 34, 504, 34);
00813 painter.drawLine(312, 12, 312, 247);
00814 painter.drawLine(312, 240, 504, 240);
00815
00816
00817 painter.setFont(titleFont);
00818 painter.drawText(35, 28, "name");
00819 painter.drawText(200, 28, "port");
00820 painter.drawText(324, 28, "energy");
00821 painter.drawText(430, 28, "points");
00822
00823
00824 QMapIterator<int, QStringList> it(scores);
00825
00826 it.toBack();
00827 int pos = 61;
00828 while (it.hasPrevious())
00829 {
00830 it.previous();
00831 painter.drawText(200, pos, it.value().at(1));
00832 pos += 24;
00833 }
00834
00835 it.toBack();
00836 painter.setFont(entryFont);
00837 pos = 61;
00838 while (it.hasPrevious())
00839 {
00840 it.previous();
00841 painter.drawText(35, pos, it.value().at(0));
00842 painter.drawText(335, pos, it.value().at(2));
00843 painter.drawText(445, pos, it.value().at(3));
00844 pos += 24;
00845 }
00846
00847 glDisable(GL_LIGHTING);
00848 glEnable(GL_TEXTURE_2D);
00849 GLuint tex = bindTexture(scoreBoard, GL_TEXTURE_2D);
00850 glEnable(GL_BLEND);
00851 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00852
00853 glCullFace(GL_FRONT);
00854 glColor4d(1, 1, 1, 0.75);
00855 for (int i = 0; i < 4; i++)
00856 {
00857 glPushMatrix();
00858 glTranslated(world->w/2, world->h/2, 50);
00859 glRotated(90*i, 0, 0, 1);
00860 glBegin(GL_QUADS);
00861 glTexCoord2d(0, 0);
00862 glVertex3d(-20, -20, 0);
00863 glTexCoord2d(1, 0);
00864 glVertex3d(20, -20, 0);
00865 glTexCoord2d(1, 1);
00866 glVertex3d(20, -20, 20);
00867 glTexCoord2d(0, 1);
00868 glVertex3d(-20, -20, 20);
00869 glEnd();
00870 glPopMatrix();
00871 }
00872
00873 glCullFace(GL_BACK);
00874 glColor3d(1, 1, 1);
00875 for (int i = 0; i < 4; i++)
00876 {
00877 glPushMatrix();
00878 glTranslated(world->w/2, world->h/2, 50);
00879 glRotated(90*i, 0, 0, 1);
00880 glBegin(GL_QUADS);
00881 glTexCoord2d(0, 0);
00882 glVertex3d(-20, -20, 0);
00883 glTexCoord2d(1, 0);
00884 glVertex3d(20, -20, 0);
00885 glTexCoord2d(1, 1);
00886 glVertex3d(20, -20, 20);
00887 glTexCoord2d(0, 1);
00888 glVertex3d(-20, -20, 20);
00889 glEnd();
00890 glPopMatrix();
00891 }
00892
00893 deleteTexture(tex);
00894
00895 glDisable(GL_TEXTURE_2D);
00896 glColor4d(7./8.,7./8.,1,0.75);
00897 glPushMatrix();
00898 glTranslated(world->w/2, world->h/2, 50);
00899 glBegin(GL_QUADS);
00900 glVertex3d(-20,-20,20);
00901 glVertex3d(20,-20,20);
00902 glVertex3d(20,20,20);
00903 glVertex3d(-20,20,20);
00904
00905 glVertex3d(-20,20,0);
00906 glVertex3d(20,20,0);
00907 glVertex3d(20,-20,0);
00908 glVertex3d(-20,-20,0);
00909 glEnd();
00910 glPopMatrix();
00911
00912
00913 static int imageCounter = 0;
00914 if (savingVideo)
00915 grabFrameBuffer().save(QString("frame%0.bmp").arg(imageCounter++), "BMP");
00916 }
00917 }
00918
00919
00920
00921 extern "C" void AsebaPutVmToSleep(AsebaVMState *vm)
00922 {
00923 }
00924
00925 extern "C" void AsebaSendBuffer(AsebaVMState *vm, const uint8* data, uint16 length)
00926 {
00927 Dashel::Stream* stream = asebaEPuckMap[vm]->stream;
00928 assert(stream);
00929
00930 uint16 len = length - 2;
00931 stream->write(&len, 2);
00932 stream->write(&vm->nodeId, 2);
00933 stream->write(data, length);
00934 stream->flush();
00935 }
00936
00937 extern "C" uint16 AsebaGetBuffer(AsebaVMState *vm, uint8* data, uint16 maxLength, uint16* source)
00938 {
00939 if (asebaEPuckMap[vm]->lastMessageData.size())
00940 {
00941 *source = asebaEPuckMap[vm]->lastMessageSource;
00942 memcpy(data, &asebaEPuckMap[vm]->lastMessageData[0], asebaEPuckMap[vm]->lastMessageData.size());
00943 }
00944 return asebaEPuckMap[vm]->lastMessageData.size();
00945 }
00946
00947 extern "C" const AsebaVMDescription* AsebaGetVMDescription(AsebaVMState *vm)
00948 {
00949 if (localName == "fr")
00950 return &vmDescription_fr;
00951 else
00952 return &vmDescription_en;
00953 }
00954
00955 static const AsebaLocalEventDescription localEvents[] = { { "timer", "periodic timer at 50 Hz" }, { NULL, NULL }};
00956
00957 extern "C" const AsebaLocalEventDescription * AsebaGetLocalEventsDescriptions(AsebaVMState *vm)
00958 {
00959 return localEvents;
00960 }
00961
00962 extern "C" const AsebaNativeFunctionDescription * const * AsebaGetNativeFunctionsDescriptions(AsebaVMState *vm)
00963 {
00964 return nativeFunctionsDescriptions;
00965 }
00966
00967 extern "C" void AsebaNativeFunction(AsebaVMState *vm, uint16 id)
00968 {
00969 nativeFunctions[id](vm);
00970 }
00971
00972 extern "C" void AsebaWriteBytecode(AsebaVMState *vm)
00973 {
00974 }
00975
00976 extern "C" void AsebaResetIntoBootloader(AsebaVMState *vm)
00977 {
00978 }
00979
00980 extern "C" void AsebaAssert(AsebaVMState *vm, AsebaAssertReason reason)
00981 {
00982 std::cerr << "\nFatal error: ";
00983 switch (vm->nodeId)
00984 {
00985 case 1: std::cerr << "left motor module"; break;
00986 case 2: std::cerr << "right motor module"; break;
00987 case 3: std::cerr << "proximity sensors module"; break;
00988 case 4: std::cerr << "distance sensors module"; break;
00989 default: std::cerr << "unknown module"; break;
00990 }
00991 std::cerr << " has produced exception: ";
00992 switch (reason)
00993 {
00994 case ASEBA_ASSERT_UNKNOWN: std::cerr << "undefined"; break;
00995 case ASEBA_ASSERT_UNKNOWN_UNARY_OPERATOR: std::cerr << "unknown unary operator"; break;
00996 case ASEBA_ASSERT_UNKNOWN_BINARY_OPERATOR: std::cerr << "unknown binary operator"; break;
00997 case ASEBA_ASSERT_UNKNOWN_BYTECODE: std::cerr << "unknown bytecode"; break;
00998 case ASEBA_ASSERT_STACK_OVERFLOW: std::cerr << "stack overflow"; break;
00999 case ASEBA_ASSERT_STACK_UNDERFLOW: std::cerr << "stack underflow"; break;
01000 case ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS: std::cerr << "out of variables bounds"; break;
01001 case ASEBA_ASSERT_OUT_OF_BYTECODE_BOUNDS: std::cerr << "out of bytecode bounds"; break;
01002 case ASEBA_ASSERT_STEP_OUT_OF_RUN: std::cerr << "step out of run"; break;
01003 case ASEBA_ASSERT_BREAKPOINT_OUT_OF_BYTECODE_BOUNDS: std::cerr << "breakpoint out of bytecode bounds"; break;
01004 case ASEBA_ASSERT_EMIT_BUFFER_TOO_LONG: std::cerr << "tried to emit a buffer too long"; break;
01005 default: std::cerr << "unknown exception"; break;
01006 }
01007 std::cerr << ".\npc = " << vm->pc << ", sp = " << vm->sp;
01008 std::cerr << "\nResetting VM" << std::endl;
01009 assert(false);
01010 AsebaVMInit(vm);
01011 }
01012
01013
01014 LanguageSelectionDialog::LanguageSelectionDialog()
01015 {
01016 QVBoxLayout* layout = new QVBoxLayout(this);
01017
01018 QLabel* text = new QLabel(tr("Please choose your language"));
01019 layout->addWidget(text);
01020
01021 languageSelectionBox = new QComboBox(this);
01022 languageSelectionBox->addItem(QString::fromUtf8("English"), "en");
01023 languageSelectionBox->addItem(QString::fromUtf8("Français"), "fr");
01024
01025 for (int i = 0; i < languageSelectionBox->count(); ++i)
01026 {
01027 if (QLocale::system().name().startsWith(languageSelectionBox->itemData(i).toString()))
01028 {
01029 languageSelectionBox->setCurrentIndex(i);
01030 break;
01031 }
01032 }
01033 layout->addWidget(languageSelectionBox);
01034
01035 QPushButton* okButton = new QPushButton(QIcon(":/images/ok.png"), tr("Ok"));
01036 connect(okButton, SIGNAL(clicked(bool)), SLOT(accept()));
01037 layout->addWidget(okButton);
01038
01039 setWindowTitle(tr("Language selection"));
01040 }
01041
01042
01043 int main(int argc, char *argv[])
01044 {
01045 QApplication app(argc, argv);
01046
01047
01048 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
01049
01050 QTranslator qtTranslator;
01051 qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
01052 app.installTranslator(&qtTranslator);
01053
01054 qDebug() << QLocale::system().name();
01055 QTranslator translator;
01056 qDebug() << translator.load(QString(":/asebachallenge_") + QLocale::system().name());
01057 app.installTranslator(&translator);
01058
01059
01060 {
01061 LanguageSelectionDialog languageSelectionDialog;
01062 languageSelectionDialog.show();
01063 languageSelectionDialog.exec();
01064
01065 localName = languageSelectionDialog.languageSelectionBox->itemData(languageSelectionDialog.languageSelectionBox->currentIndex()).toString();
01066 qtTranslator.load(QString("qt_") + localName, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
01067 translator.load(QString(":/asebachallenge_") + localName);
01068 }
01069
01070
01071 Enki::World world(140, 140, Enki::Color(0.4, 0.4, 0.4));
01072
01073
01074 Enki::EPuckFeeder* feeders[4];
01075
01076 feeders[0] = new Enki::EPuckFeeder(0);
01077 feeders[0]->pos.x = 40;
01078 feeders[0]->pos.y = 40;
01079 world.addObject(feeders[0]);
01080
01081 feeders[1] = new Enki::EPuckFeeder(15);
01082 feeders[1]->pos.x = 100;
01083 feeders[1]->pos.y = 40;
01084 world.addObject(feeders[1]);
01085
01086 feeders[2] = new Enki::EPuckFeeder(45);
01087 feeders[2]->pos.x = 40;
01088 feeders[2]->pos.y = 100;
01089 world.addObject(feeders[2]);
01090
01091 feeders[3] = new Enki::EPuckFeeder(30);
01092 feeders[3]->pos.x = 100;
01093 feeders[3]->pos.y = 100;
01094 world.addObject(feeders[3]);
01095
01096
01097 int ePuckCount = 0;
01098 for (int i = 1; i < argc; i++)
01099 {
01100 Enki::AsebaFeedableEPuck* epuck = new Enki::AsebaFeedableEPuck(i-1);
01101 epuck->pos.x = Enki::random.getRange(120)+10;
01102 epuck->pos.y = Enki::random.getRange(120)+10;
01103 epuck->name = argv[i];
01104 world.addObject(epuck);
01105 ePuckCount++;
01106 }
01107
01108
01109 Enki::ChallengeViewer viewer(&world, ePuckCount);
01110
01111
01112 viewer.setWindowTitle("ASEBA Challenge - Stephane Magnenat (code) - Basilio Noris (gfx)");
01113 viewer.show();
01114
01115 return app.exec();
01116 }