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