1 /***************************************************************************
2
3 file : pathfinder.cpp
4 created : Tue Oct 9 16:52:00 CET 2001
5 copyright : (C) 2001-2006 by Bernhard Wymann, some Code from Remi Coulom, K1999.cpp
6 email : berniw@bluewin.ch
7 version : $Id: pathfinder.cpp,v 1.1.2.3 2011/12/31 02:51:47 berniw Exp $
8
9 ***************************************************************************/
10
11 /***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20 #include "pathfinder.h"
21 #include "berniw.h"
22
23 const double Pathfinder::COLLDIST = 150.0;
24 PathSegOpt* Pathfinder::psopt = NULL;
25 bool Pathfinder::optpathinitialized = false;
26
27
Pathfinder(TrackDesc * itrack,tCarElt * car,tSituation * s)28 Pathfinder::Pathfinder(TrackDesc* itrack, tCarElt* car, tSituation *s)
29 {
30 int i;
31 track = itrack;
32 tTrack* t = track->getTorcsTrack();
33 o = new tOCar[s->_ncars];
34
35 // Set team mate, TODO: support multiple teammates.
36 teammate = NULL;
37 const char *teammatename = GfParmGetStr(car->_carHandle, BERNIW_SECT_PRIV, BERNIW_ATT_TEAMMATE, NULL);
38 // Teammate defined in XML setup file?
39 if (teammatename != NULL) {
40 // Teammate as well in the race?
41 for (i = 0; i < s->_ncars; i++) {
42 if (strcmp(s->cars[i]->_name, teammatename) == 0 && car != s->cars[i]) {
43 teammate = s->cars[i];
44 break;
45 }
46 }
47 }
48
49 overlaptimer = new tOverlapTimer[s->_ncars];
50
51 for (i = 0; i < s->_ncars; i++) {
52 overlaptimer[i].time = 0.0;
53 }
54
55 // The optimal path has to have one point per tracksegment.
56 nPathSeg = track->getnTrackSegments();
57
58 // Get memory for the optimal path, just once shared by all instances.
59 if (psopt == NULL) {
60 psopt = new PathSegOpt(nPathSeg);
61 }
62
63 // Get memory for dynamic trajectory.
64 psdyn = new PathSeg(PATHBUF, nPathSeg);
65 changed = lastPlan = lastPlanRange = 0;
66 inPit = pitStop = false;
67
68 // Check if there is a pit type we can use and if for this car is a pit available.
69 pit = false;
70 if (t->pits.type == TR_PIT_ON_TRACK_SIDE && car->_pit != NULL) {
71 pit = true;
72 }
73
74 s1 = e3 = 0;
75 if (isPitAvailable()) {
76 initPit(car);
77 // The values in the setup file must be for trackres == 1.0.
78 s1 = track->getPitEntryStartId();
79 s1 = (int) (GfParmGetNum(car->_carHandle, BERNIW_SECT_PRIV, BERNIW_ATT_PITENTRY, (char*)NULL, s1*TRACKRES)/TRACKRES);
80 e3 = track->getPitExitEndId();
81 e3 = (int) (GfParmGetNum(car->_carHandle, BERNIW_SECT_PRIV, BERNIW_ATT_PITEXIT, (char*)NULL, e3*TRACKRES)/TRACKRES);
82 pitspeedsqrlimit = t->pits.speedLimit - 0.5;
83 pitspeedsqrlimit *= pitspeedsqrlimit;
84 pspit = new PathSegPit(countSegments(s1, e3), nPathSeg, s1, e3, psopt);
85 }
86 }
87
88
~Pathfinder()89 Pathfinder::~Pathfinder()
90 {
91 delete psdyn;
92 if (psopt != NULL) {
93 delete psopt;
94 psopt = NULL;
95 optpathinitialized = false;
96 }
97 if (isPitAvailable()) {
98 delete pspit;
99 }
100 delete [] o;
101 delete [] overlaptimer;
102 }
103
104
105 // Compute where the pit is, etc.
initPit(tCarElt * car)106 void Pathfinder::initPit(tCarElt* car) {
107 tTrack* t = track->getTorcsTrack();
108
109 if (t->pits.driversPits != NULL && car != NULL) {
110 if (isPitAvailable()) {
111 tTrackSeg* pitSeg = car->_pit->pos.seg;
112 if (pitSeg->type == TR_STR) {
113 vec2d v1, v2, v3;
114 // v1 points in the direction of the segment.
115 v1.x = pitSeg->vertex[TR_EL].x - pitSeg->vertex[TR_SL].x;
116 v1.y = pitSeg->vertex[TR_EL].y - pitSeg->vertex[TR_SL].y;
117 v1.normalize();
118
119 // v2 points to the side of the segment.
120 double s = (t->pits.side == TR_LFT) ? -1.0 : 1.0 ;
121 v2.x = s*(pitSeg->vertex[TR_SR].x - pitSeg->vertex[TR_SL].x);
122 v2.y = s*(pitSeg->vertex[TR_SR].y - pitSeg->vertex[TR_SL].y);
123 v2.normalize();
124
125 // Loading starting point of segment.
126 pitLoc.x = (pitSeg->vertex[TR_SR].x + pitSeg->vertex[TR_SL].x) / 2.0;
127 pitLoc.y = (pitSeg->vertex[TR_SR].y + pitSeg->vertex[TR_SL].y) / 2.0;
128
129 // Going along the track.
130 pitLoc = pitLoc + (double)(car->_pit->pos.toStart)*v1;
131 pitSegId = track->getNearestId(&pitLoc);
132
133 // Going sideways, minus because of opposite sign of v2 and the value toMiddle.
134 double m = fabs(t->pits.driversPits->pos.toMiddle);
135 v3 = pitLoc + m*v2;
136
137 tTrackSeg* tmpSeg = t->pits.pitStart;
138 v2.x = (tmpSeg->vertex[TR_SR].x + tmpSeg->vertex[TR_SL].x) / 2.0;
139 v2.y = (tmpSeg->vertex[TR_SR].y + tmpSeg->vertex[TR_SL].y) / 2.0;
140
141 // FIXME: this could cause problems, integrate bt pit stops.
142 double dist = (v2-pitLoc).len() - 2.0/TRACKRES;
143 if (dist < t->pits.len) {
144 v2 = pitLoc - v1*(t->pits.len + 2.0/TRACKRES);
145 }
146 s3 = track->getNearestId(&v2);
147
148 tmpSeg = t->pits.pitEnd;
149 v2.x = (tmpSeg->vertex[TR_ER].x + tmpSeg->vertex[TR_EL].x) / 2.0;
150 v2.y = (tmpSeg->vertex[TR_ER].y + tmpSeg->vertex[TR_EL].y) / 2.0;
151 // FIXME: this could cause problems, integrate bt pit stops.
152 dist = (pitLoc-v2).len() - 2.0/TRACKRES;
153 if (dist < t->pits.len) {
154 v2 = pitLoc + v1*(t->pits.len + 2.0/TRACKRES);
155 }
156 e1 = track->getNearestId(&v2);
157
158
159 pitLoc = v3;
160 } else {
161 pit = false;
162 }
163 }
164 }
165 }
166
167
168 // Call this after you computed a static racing path with plan().
initPitStopPath(void)169 void Pathfinder::initPitStopPath(void)
170 {
171 tTrack* t = track->getTorcsTrack();
172 vec2d p, q;
173 const vec2d *pp, *pmypitseg = track->getSegmentPtr(pitSegId)->getMiddle();
174 double d, dp, sgn;
175 double delta = t->pits.width;
176 int i;
177 double ypit[PITPOINTS], yspit[PITPOINTS], spit[PITPOINTS];
178 int snpit[PITPOINTS];
179
180 // Set up point 0 on the track (s1).
181 ypit[0] = track->distToMiddle(s1, psopt->getOptLoc(s1));
182 snpit[0] = s1;
183
184 // Set up point 1 pit lane entry (s3).
185 track->dirVector2D(&pitLoc, pmypitseg, &p);
186 dp = p.len();
187 d = dp - delta;
188
189 sgn = (t->pits.side == TR_LFT) ? -1.0 : 1.0 ;
190 ypit[1] = d*sgn;
191 snpit[1] = s3;
192
193 // Set up point 2 before we turn into the pit.
194 // FIXME: this could cause problems, integrate bt pit stops.
195 i = (pitSegId - (int) (t->pits.len/TRACKRES) + nPathSeg) % nPathSeg;
196 ypit[2] = d*sgn;
197 snpit[2] = i;
198
199 // Set up point 3, the pit, we know this already.
200 ypit[3] = dp*sgn;
201 snpit[3] = pitSegId;
202
203 // Compute point 4, go from pit back to pit lane.
204 i = (pitSegId + (int) (t->pits.len/TRACKRES) + nPathSeg) % nPathSeg;
205 ypit[4] = d*sgn;
206 snpit[4] = i;
207
208 // compute point 5, drive to end of pit lane (e1).
209 ypit[5] = d*sgn;
210 snpit[5] = e1;
211
212 // Compute point 6, back on the track.
213 ypit[6] = track->distToMiddle(e3, psopt->getOptLoc(e3));
214 snpit[6] = e3;
215
216 // Compute spit array.
217 spit[0] = 0.0;
218 for (i = 1; i < PITPOINTS; i++) {
219 d = 0.0;
220 for (int j = snpit[i-1]; (j + 1) % nPathSeg != snpit[i]; j++) {
221 if (snpit[i] > snpit[i-1]) {
222 d = (double) (snpit[i] - snpit[i-1]);
223 } else {
224 d = (double) (nPathSeg - snpit[i-1] + snpit[i]);
225 }
226 }
227 spit[i] = spit[i-1] + d;
228 }
229
230 // Scale for track resolution.
231 for (i = 1; i < PITPOINTS; i++) {
232 spit[i] = TRACKRES*spit[i];
233 }
234
235 // Set up slopes.
236 yspit[0] = pathOptSlope(s1);
237 yspit[6] = pathOptSlope(e3);
238
239 for (i = 1; i < PITPOINTS-1; i++) {
240 yspit[i] = 0.0;
241 }
242
243 // Compute path to pit.
244 double l = 0.0;
245 for (i = s1; (i + nPathSeg) % nPathSeg != e3; i++) {
246 int j = (i + nPathSeg) % nPathSeg;
247 d = spline(PITPOINTS, l, spit, ypit, yspit);
248
249 pp = track->getSegmentPtr(j)->getMiddle();
250 p = *track->getSegmentPtr(j)->getToRight();;
251 q = *pp + p*d;
252 pspit->setPitLoc(&q, j);
253 l += TRACKRES;
254 }
255 }
256
257
258 // Plots pit path to file for gnuplot.
259 // gnuplot command: plot 'filename'
plotPitStopPath(char * filename)260 void Pathfinder::plotPitStopPath(char* filename)
261 {
262 FILE* fd = fopen(filename, "w");
263
264 // Plot pit path.
265 for (int i = 0; i < nPathSeg; i++) {
266 fprintf(fd, "%f\t%f\n", pspit->getPitLoc(i)->x, pspit->getPitLoc(i)->y);
267 }
268 fclose(fd);
269 }
270
271
272 // Plots the optimal path.
plotPath(char * filename)273 void Pathfinder::plotPath(char* filename)
274 {
275 FILE* fd = fopen(filename, "w");
276
277 // Plot path.
278 for (int i = 0; i < nPathSeg; i++) {
279 fprintf(fd, "%f\t%f\n", psopt->getOptLoc(i)->x, psopt->getOptLoc(i)->y);
280 }
281 fclose(fd);
282 }
283
284
285 // Plans a static trajectory ignoring current situation.
plan(MyCar * myc,int currentsegid)286 void Pathfinder::plan(MyCar* myc, int currentsegid)
287 {
288 double r, length, speedsqr;
289 int u, v, w;
290 vec2d dir;
291 int i,j;
292
293 // Compute optimal path if not already done.
294 if (optpathinitialized == false) {
295 // Basic initialisation with track middle.
296 for (i = 0; i < nPathSeg; i++) {
297 psopt->setOptLoc(track->getSegmentPtr(i)->getMiddle(), i);
298 }
299
300 // Compute optimal path.
301 for (int step = 128; (step /= 2) > 0;) {
302 for (int i = 100 * int(sqrt((double)step)); --i >= 0;) smooth(step);
303 interpolate(step);
304 }
305 optpathinitialized = true;
306
307 // Now init the other fields of the optimal path.
308 // Compute the tangent as slopes of the spline connecting the path.
309 double* sp_x = new double[nPathSeg+1];
310 double* sp_y = new double[nPathSeg+1];
311 double* sp_xs = new double[nPathSeg+1];
312 double* sp_ys = new double[nPathSeg+1];
313 double* sp_s = new double[nPathSeg+1];
314
315 for (i = 0; i < nPathSeg; i++) {
316 sp_x[i] = psopt->getOptLoc(i)->x;
317 sp_y[i] = psopt->getOptLoc(i)->y;
318 }
319 sp_x[nPathSeg] = sp_x[0];
320 sp_y[nPathSeg] = sp_y[0];
321
322 // Slopes for closed spline.
323 parametricslopesp(nPathSeg+1, sp_x, sp_y, sp_xs, sp_ys, sp_s);
324
325 for (i = 0; i < nPathSeg; i++) {
326 // We want not the tangent, we want the perpendicular vector to right, so swap sx, sy and
327 // fudge the signs (direction).
328 vec2d slope(sp_ys[i], -sp_xs[i]);
329 slope.normalize();
330 psopt->setOptToRight(&slope, i);
331 // Set the length.
332 psopt->setOptLength((*psopt->getOptLoc((i+1) % nPathSeg) - *psopt->getOptLoc(i)).len(), i);
333 }
334
335 for (i = 0; i < nPathSeg; i++) {
336 float f = ((*psopt->getOptLoc(i)) - (*track->getSegmentPtr(i)->getMiddle()))*(*track->getSegmentPtr(i)->getToRight());
337 psopt->setOptToMiddle(f, i);
338 }
339
340 delete [] sp_x;
341 delete [] sp_y;
342 delete [] sp_xs;
343 delete [] sp_ys;
344 delete [] sp_s;
345 }
346
347 // Init pit ond optimal path.
348 int bufstartseg = (currentsegid - BACK + nPathSeg) % nPathSeg;
349
350 psdyn->setBase(bufstartseg);
351 for (i = bufstartseg; i < bufstartseg + PATHBUF; i++) {
352 j = i % nPathSeg;
353 psdyn->setLoc(psopt->getOptLoc(j), j);
354 }
355
356 // Compute possible speeds, direction vector and length of trajectoies.
357 u = (nPathSeg + bufstartseg - 1) % nPathSeg; v = bufstartseg; w = (bufstartseg + 1) % nPathSeg;
358
359 for (j = bufstartseg; j < bufstartseg + PATHBUF; j++) {
360 i = j % nPathSeg;
361 r = radius(psopt->getOptLoc(u)->x, psopt->getOptLoc(u)->y,
362 psopt->getOptLoc(v)->x, psopt->getOptLoc(v)->y, psopt->getOptLoc(w)->x, psopt->getOptLoc(w)->y);
363 psdyn->setRadius(r, i);
364 r = fabs(r);
365
366 length = dist(psopt->getOptLoc(v), psopt->getOptLoc(w));
367
368 tdble mu = track->getSegmentPtr(i)->getKfriction()*myc->CFRICTION*track->getSegmentPtr(i)->getKalpha();
369 tdble b = track->getSegmentPtr(i)->getKbeta();
370 speedsqr = myc->SPEEDSQRFACTOR*r*g*mu/(1.0 - MIN(1.0, (mu*myc->ca*r/myc->mass)) + mu*r*b);
371
372 dir = (*psopt->getOptLoc(w)) - (*psopt->getOptLoc(u));
373 dir.normalize();
374
375 psdyn->set(speedsqr, length, &dir, i);
376
377 u = v; v = w; w = (w + 1 + nPathSeg) % nPathSeg;
378 }
379
380 // Add path to pit if a pit is available.
381 if (isPitAvailable()) {
382 initPitStopPath();
383 }
384 }
385
386
387 // Plans a trajectory according to the situation.
plan(int trackSegId,tCarElt * car,tSituation * situation,MyCar * myc,OtherCar * ocar)388 void Pathfinder::plan(int trackSegId, tCarElt* car, tSituation *situation, MyCar* myc, OtherCar* ocar)
389 {
390 double r, length, speedsqr;
391 int u, v, w;
392 vec2d dir;
393
394 int bufstartseg = (trackSegId - BACK + nPathSeg) % nPathSeg;
395 psdyn->setBase(bufstartseg);
396
397 int i, start;
398
399 if (myc->derror > myc->PATHERR*myc->PATHERRFACTOR) {
400 start = trackSegId;
401 } else {
402 start = lastPlan+lastPlanRange;
403 }
404
405 if (track->isBetween(e3, s1, trackSegId)) {
406 inPit = false;
407 }
408 // Relies on that pitstop dosen't get enabled between s1, e3.
409 if (track->isBetween(s1, e3, trackSegId) && (pitStop)) {
410 inPit = true;
411 }
412
413 // Load precomputed trajectory.
414 if (!pitStop && !inPit) {
415 for (i = start; i < trackSegId+AHEAD+SEGRANGE; i++) {
416 int j = (i+nPathSeg) % nPathSeg;
417 psdyn->setLoc(psopt->getOptLoc(j), j);
418 }
419 } else {
420 for (i = start; i < trackSegId+AHEAD+SEGRANGE; i++) {
421 int j = (i+nPathSeg) % nPathSeg;
422 psdyn->setLoc(pspit->getPitLoc(j), j);
423 }
424 }
425
426 // Update data about the opponents relative to me.
427 collcars = updateOCar(trackSegId, situation, myc, ocar, o);
428 updateOverlapTimer(trackSegId, situation, myc, ocar, o, overlaptimer);
429
430 if (!inPit && (!pitStop || track->isBetween(e3, (s1 - AHEAD + nPathSeg) % nPathSeg, trackSegId))) {
431 // Are we on the trajectory or do I need a correction.
432 if ((myc->derror > myc->PATHERR*myc->PATHERRFACTOR ||
433 (myc->getDeltaPitch() > myc->MAXALLOWEDPITCH && myc->getSpeed() > myc->FLYSPEED)))
434 {
435 changed += correctPath(trackSegId, car, myc);
436 }
437
438 // Overtaking.
439 if (changed == 0) {
440 changed += overtake(trackSegId, situation, myc, ocar);
441 }
442
443 // If we have nothing better to, let opponents overlap.
444 if (changed == 0) {
445 changed = letoverlap(trackSegId, situation, myc, ocar, overlaptimer);
446 }
447 }
448
449 // Recompute speed and direction of new trajectory.
450 if (changed > 0 || (psdyn->getSpeedsqr(trackSegId) < 5.0)) {
451 start = trackSegId;
452 }
453
454 u = start - 1; v = start; w = start+1;
455 int u2 = (start - 3 + nPathSeg) % nPathSeg;
456 int w2 = (start + 3 + nPathSeg) % nPathSeg;
457 u = (u + nPathSeg) % nPathSeg;
458 v = (v + nPathSeg) % nPathSeg;
459 w = (w + nPathSeg) % nPathSeg;
460
461 for (i = start; i < trackSegId+AHEAD+SEGRANGE; i++) {
462 int j = (i+nPathSeg) % nPathSeg;
463 /* taking 2 radiuses to reduce "noise" */
464
465 double r2 = radius(psdyn->getLoc(u)->x, psdyn->getLoc(u)->y,
466 psdyn->getLoc(v)->x, psdyn->getLoc(v)->y, psdyn->getLoc(w)->x, psdyn->getLoc(w)->y);
467
468 double r1 = radius(psdyn->getLoc(u2)->x, psdyn->getLoc(u2)->y,
469 psdyn->getLoc(v)->x, psdyn->getLoc(v)->y, psdyn->getLoc(w2)->x, psdyn->getLoc(w2)->y);
470
471 if (fabs(r1) > fabs(r2)) {
472 psdyn->setRadius(r1, j);
473 r = fabs(r1);
474 } else {
475 psdyn->setRadius(r2, j);
476 r = fabs(r2);
477 }
478
479 length = dist(psdyn->getLoc(v), psdyn->getLoc(w));
480
481 // Compute allowed speed squared.
482 double mu = track->getSegmentPtr(j)->getKfriction()*myc->CFRICTION*track->getSegmentPtr(j)->getKalpha();
483 double b = track->getSegmentPtr(j)->getKbeta();
484 speedsqr = myc->SPEEDSQRFACTOR*r*g*mu/(1.0 - MIN(1.0, (mu*myc->ca*r/myc->mass)) + mu*r*b);
485 if (pitStop && track->isBetween(s3, pitSegId, j)) {
486 double speedsqrpit = ((double) segmentsToPit(j) * TRACKRES) *2.0*g*track->getSegmentPtr(j)->getKfriction()*myc->CFRICTION*myc->cgcorr_b;
487 if (speedsqr > speedsqrpit) {
488 speedsqr = speedsqrpit;
489 }
490 }
491 if ((pitStop || inPit) && track->isBetween(s3, e1, j)) {
492 if (speedsqr > getPitSpeedSqrLimit()) {
493 speedsqr = getPitSpeedSqrLimit();
494 }
495 }
496
497 dir = (*psdyn->getLoc(w)) - (*psdyn->getLoc(u));
498 dir.normalize();
499
500 psdyn->set(speedsqr, length, &dir, j);
501
502 u = v; v = w; w = (w + 1 + nPathSeg) % nPathSeg;
503 w2 = (w2 + 1 + nPathSeg) % nPathSeg;
504 u2 = (u2 + 1 + nPathSeg) % nPathSeg;
505 }
506
507 changed = 0;
508
509 /* set speed limits on the path, in case there is an obstacle (other car) */
510 changed += collision(trackSegId, car, situation, myc, ocar);
511
512 lastPlan = trackSegId; lastPlanRange = AHEAD;
513 }
514
515
smooth(int s,int p,int e,double w)516 void Pathfinder::smooth(int s, int p, int e, double w)
517 {
518 TrackSegment2D* t = track->getSegmentPtr(p);
519 const vec2d *rgh = t->getToRight();
520 vec2d *rs = psdyn->getLoc(s), *rp = psdyn->getLoc(p), *re = psdyn->getLoc(e), n;
521
522 double rgx = (re->x - rs->x), rgy = (re->y - rs->y);
523 double m = ((rs->x - rp->x)*rgy + (rp->y - rs->y)*rgx ) / (rgy * rgh->x - rgx * rgh->y);
524 n = (*rp) + (*rgh)*m;
525 psdyn->setLoc(&n, p);
526 }
527
528
529 /* collision avoidence with braking */
collision(int trackSegId,tCarElt * mycar,tSituation * s,MyCar * myc,OtherCar * ocar)530 int Pathfinder::collision(int trackSegId, tCarElt* mycar, tSituation* s, MyCar* myc, OtherCar* ocar)
531 {
532 int end = (trackSegId + (int) (COLLDIST/TRACKRES) + nPathSeg) % nPathSeg;
533 int didsomething = 0;
534 int i, n = collcars;
535
536 for (i = 0; i < n; i++) {
537 // TODO: magic number
538 // TODO: Move out of pathfinder to improve performance.
539 if (o[i].overtakee == true || (o[i].time > myc->TIMETOCATCH-0.1 && o[i].collcar->getSpeed() < 10.0)) continue;
540 int currentsegid = o[i].collcar->getCurrentSegId();
541 if (track->isBetween(trackSegId, end, currentsegid) && (myc->getSpeed() > o[i].speed)) {
542 int spsegid = (currentsegid - (int) ((myc->CARLEN + 1)/TRACKRES) + nPathSeg) % nPathSeg;
543
544 // TODO: try to use relative speed.
545 if (o[i].mincorner < myc->CARWIDTH/2.0 + (myc->DIST*MIN(1.0, o[i].collcar->getSpeed()/28.0))) {
546 double cmpdist = o[i].dist - myc->CARLEN - myc->DIST;
547 if ((o[i].brakedist >= cmpdist) && (psdyn->getSpeedsqr(spsegid) > o[i].speedsqr)) {
548 int j;
549 int adv = MAX(1, (int) (3.0/TRACKRES+0.5));
550 for (j = spsegid - adv; j < spsegid + adv; j++) {
551 psdyn->setSpeedsqr(o[i].speedsqr, (j + nPathSeg) % nPathSeg);
552 }
553 didsomething = 1;
554 }
555 }
556
557 if (track->isBetween(trackSegId, end, o[i].catchsegid)) {
558 double myd = track->distToMiddle(o[i].catchsegid, psdyn->getLoc(o[i].catchsegid));
559 double sina = o[i].collcar->getDir()->fakeCrossProduct(myc->getDir());
560 double otherd = o[i].disttomiddle + sina*o[i].collcar->getSpeed()*o[i].time;
561
562 if (fabs(myd - otherd) < myc->CARWIDTH + myc->DIST*MIN(1.0, o[i].collcar->getSpeed()/28.0)) {
563 if ((o[i].catchdist > 0.0) && (o[i].brakedist >= (o[i].catchdist - (myc->CARLEN + myc->DIST)))) {
564 int catchsegid = ((o[i].catchsegid - (int) ((myc->CARLEN + 1)/TRACKRES) + nPathSeg) % nPathSeg);
565 if (psdyn->getSpeedsqr(catchsegid) > o[i].speedsqr) {
566 psdyn->setSpeedsqr(o[i].speedsqr, catchsegid);
567 didsomething = 1;
568 }
569 }
570 }
571 }
572
573 }
574 }
575 return didsomething;
576 }
577
578
579
curvature(double xp,double yp,double x,double y,double xn,double yn)580 inline double Pathfinder::curvature(double xp, double yp, double x, double y, double xn, double yn)
581 {
582 return 1.0/radius(xp, yp, x, y, xn, yn);
583 }
584
585
586 // Optimize point p ala k1999 (curvature), Remi Coulom, K1999.cpp.
adjustRadius(int s,int p,int e,double c,double security)587 inline void Pathfinder::adjustRadius(int s, int p, int e, double c, double security) {
588 const double sidedistext = 2.0;
589 const double sidedistint = 1.2;
590
591 TrackSegment2D* t = track->getSegmentPtr(p);
592 const vec2d *rgh = t->getToRight();
593 const vec2d *left = t->getLeftBorder();
594 const vec2d *right = t->getRightBorder();
595 const vec2d *rs = psopt->getOptLoc(s), *rp = psopt->getOptLoc(p), *re = psopt->getOptLoc(e);
596 double oldlane = track->distToMiddle(p, rp)/t->getWidth() + 0.5;
597
598 double rgx = (re->x - rs->x), rgy = (re->y - rs->y);
599 double m = ((rs->x - rp->x)*rgy + (rp->y - rs->y)*rgx ) / (rgy * rgh->x - rgx * rgh->y);
600
601 if (m < -t->getWidth()) {
602 m = -t->getWidth();
603 }
604 if (m > t->getWidth()) {
605 m = t->getWidth();
606 }
607
608 vec2d n = (*rp) +(*rgh)*m;
609 psopt->setOptLoc(&n, p);
610 double newlane = track->distToMiddle(p, rp)/t->getWidth() + 0.5;
611
612 // Get an estimate how much the curvature changes by moving the point 1/10000 of track width.
613 const double delta = 0.0001;
614 double dx = delta * (right->x - left->x);
615 double dy = delta * (right->y - left->y);
616 double deltacurvature = curvature(rs->x, rs->y, rp->x + dx, rp->y + dy, re->x, re->y);
617
618 if (deltacurvature > 0.000000001) {
619 newlane += (delta / deltacurvature) * c;
620 double ExtLane = (sidedistext + security) / t->getWidth();
621 double IntLane = (sidedistint + security) / t->getWidth();
622
623 if (ExtLane > 0.5) ExtLane = 0.5;
624 if (IntLane > 0.5) IntLane = 0.5;
625
626 if (c >= 0.0) {
627 if (newlane < IntLane) newlane = IntLane;
628 if (1 - newlane < ExtLane) {
629 if (1 - oldlane < ExtLane) newlane = MIN(oldlane, newlane);
630 else newlane = 1 - ExtLane;
631 }
632 } else {
633 if (newlane < ExtLane) {
634 if (oldlane < ExtLane) newlane = MAX(oldlane, newlane);
635 else newlane = ExtLane;
636 }
637 if (1 - newlane < IntLane) newlane = 1 - IntLane;
638 }
639
640 double d = (newlane - 0.5) * t->getWidth();
641 const vec2d* trackmiddle = t->getMiddle();
642
643 n = (*trackmiddle) + (*rgh)*d;
644 psopt->setOptLoc(&n, p);
645 }
646 }
647
648
649 // Interpolation step from Remi Coulom, K1999.cpp.
stepInterpolate(int iMin,int iMax,int Step)650 void Pathfinder::stepInterpolate(int iMin, int iMax, int Step)
651 {
652 int next = (iMax + Step) % nPathSeg;
653 if (next > nPathSeg - Step) next = 0;
654
655 int prev = (((nPathSeg + iMin - Step) % nPathSeg) / Step) * Step;
656 if (prev > nPathSeg - Step)
657 prev -= Step;
658
659 const vec2d *pp = psopt->getOptLoc(prev);
660 const vec2d *p = psopt->getOptLoc(iMin);
661 const vec2d *pn = psopt->getOptLoc(iMax % nPathSeg);
662 const vec2d *pnn = psopt->getOptLoc(next);
663
664 double ir0 = curvature(pp->x, pp->y, p->x, p->y, pn->x, pn->y);
665 double ir1 = curvature(p->x, p->y, pn->x, pn->y, pnn->x, pnn->y);
666
667 for (int k = iMax; --k > iMin;) {
668 double x = double(k - iMin) / double(iMax - iMin);
669 double TargetRInverse = x * ir1 + (1 - x) * ir0;
670 adjustRadius(iMin, k, iMax % nPathSeg, TargetRInverse, 0.0);
671 }
672 }
673
674
675 // Interpolating from Remi Coulom, K1999.cpp.
interpolate(int step)676 void Pathfinder::interpolate(int step)
677 {
678 if (step > 1) {
679 int i;
680 for (i = step; i <= nPathSeg - step; i += step) stepInterpolate(i - step, i, step);
681 stepInterpolate(i - step, nPathSeg, step);
682 }
683 }
684
685
686 // Smoothing from Remi Coulom, K1999.cpp.
smooth(int Step)687 void Pathfinder::smooth(int Step)
688 {
689 int prev = ((nPathSeg - Step) / Step) * Step;
690 int prevprev = prev - Step;
691 int next = Step;
692 int nextnext = next + Step;
693
694 const vec2d *pp, *p, *n, *nn, *cp;
695
696 for (int i = 0; i <= nPathSeg - Step; i += Step) {
697 pp = psopt->getOptLoc(prevprev);
698 p = psopt->getOptLoc(prev);
699 cp = psopt->getOptLoc(i);
700 n = psopt->getOptLoc(next);
701 nn = psopt->getOptLoc(nextnext);
702
703 double ir0 = curvature(pp->x, pp->y, p->x, p->y, cp->x, cp->y);
704 double ir1 = curvature(cp->x, cp->y, n->x, n->y, nn->x, nn->y);
705 double dx, dy;
706 dx = cp->x - p->x; dy = cp->y - p->y;
707 double lPrev = sqrt(dx*dx + dy*dy);
708 dx = cp->x - n->x; dy = cp->y - n->y;
709 double lNext = sqrt(dx*dx + dy*dy);
710
711 double TargetRInverse = (lNext * ir0 + lPrev * ir1) / (lNext + lPrev);
712
713 double Security = lPrev * lNext / (8.0 * 100.0);
714 adjustRadius(prev, i, next, TargetRInverse, Security);
715
716 prevprev = prev;
717 prev = i;
718 next = nextnext;
719 nextnext = next + Step;
720 if (nextnext > nPathSeg - Step) nextnext = 0;
721 }
722 }
723
724
725 // Compute a path back to the planned path.
correctPath(int id,tCarElt * car,MyCar * myc)726 int Pathfinder::correctPath(int id, tCarElt* car, MyCar* myc)
727 {
728 double s[2], y[2], ys[2];
729 bool out;
730
731 double d = track->distToMiddle(id, myc->getCurrentPos());
732 double factor = MIN(MIN(myc->CORRLEN/TRACKRES*myc->derror, nPathSeg/2.0), AHEAD);
733 int endid = (id + (int) (factor) + nPathSeg) % nPathSeg;
734
735 // Are we outside the track or on the track?
736 if (fabs(d) > (track->getSegmentPtr(id)->getWidth() - myc->CARWIDTH)/2.0) {
737 d = sign(d)*((track->getSegmentPtr(id)->getWidth() - myc->CARWIDTH)/2.0 - myc->MARGIN);
738 vec2d pathdir = *psdyn->getDir(id);
739 vec2d pathtoright(pathdir.y, -pathdir.x);
740 vec2d trackdir(-track->getSegmentPtr(id)->getToRight()->y, track->getSegmentPtr(id)->getToRight()->x);
741 double alpha = PI/2.0 - acos(trackdir*pathtoright);
742 ys[0] = tan(alpha);
743 out = true;
744 } else {
745 vec2d pathdir = *psdyn->getDir(id);
746 vec2d pathtoright(pathdir.y, -pathdir.x);
747 double alpha = PI/2.0 - acos((*myc->getDir())*pathtoright);
748 ys[0] = tan(alpha);
749 out = false;
750 }
751
752 // Set up points for spline.
753 y[0] = myc->getErrorSgn()*myc->derror;
754 y[1] = 0.0;
755 ys[1] = 0.0;
756
757 s[0] = 0.0;
758
759 int i, j;
760 s[1] = 0.0;
761 for (i = id; (j = (i + nPathSeg) % nPathSeg) != endid; i++) {
762 s[1] += psdyn->getLength(j);
763 }
764
765 // Modify path.
766 double l = 0.0;
767 vec2d q;
768 const vec2d *pp, *qq;
769
770 if (out == true) {
771 // We are off the track, so we take the optimal trajectory as base and reject the current one.
772 for (i = id; (j = (i + nPathSeg) % nPathSeg) != endid; i++) {
773 d = spline(2, l, s, y, ys);
774 float d1 = ((*psdyn->getLoc(j)) - (*track->getSegmentPtr(j)->getMiddle()))*(*track->getSegmentPtr(j)->getToRight());
775 float d2 = d1 + d;
776
777 if (fabs(d2) > (track->getSegmentPtr(j)->getWidth() - myc->CARWIDTH)/2.0) {
778 d = sign(d)*((track->getSegmentPtr(j)->getWidth() - myc->CARWIDTH)/2.0 - myc->MARGIN - fabs(d1));
779 }
780
781 pp = psopt->getOptLoc(j);
782 qq = psopt->getOptToRight(j);
783 q = (*pp) + (*qq)*d;
784 psdyn->setLoc(&q, j);
785 l += psdyn->getLength(j);
786 }
787
788 // Reload optimal trajectory where needed.
789 for (i = endid; (j = (i+nPathSeg) % nPathSeg) != (id+AHEAD) % nPathSeg; i++) {
790 psdyn->setLoc(psopt->getOptLoc(j), j);
791 }
792 } else {
793 // We are on the track, therefore we take the planned dynamic trajectory as base.
794 double newdisttomiddle[AHEAD];
795 for (i = id; (j = (i + nPathSeg) % nPathSeg) != endid; i++) {
796 d = spline(2, l, s, y, ys);
797 float d2 = ((*psdyn->getLoc(j)) - (*track->getSegmentPtr(j)->getMiddle()))*(*track->getSegmentPtr(j)->getToRight()) + d;
798 //printf("d2: %f, limit: %f\n", d2, (track->getSegmentPtr(j)->getWidth() - myc->CARWIDTH) / 2.0 - myc->MARGIN);
799 if (fabs(d2) > (track->getSegmentPtr(j)->getWidth() - myc->CARWIDTH) / 2.0 - myc->MARGIN) {
800 return 0;
801 }
802 newdisttomiddle[i - id] = d;
803 l += psdyn->getLength(j);
804 }
805
806 for (i = id; (j = (i + nPathSeg) % nPathSeg) != endid; i++) {
807 pp = psdyn->getLoc(j);
808 qq = psopt->getOptToRight(j);
809 q = *pp + (*qq)*newdisttomiddle[i - id];
810 psdyn->setLoc(&q, j);
811
812 }
813
814 // Reload of optimal trajectory here is not allowed, because we want connect to the dynamic trajectory.
815 }
816
817 // Align previous point for getting correct speedsqr in Pathfinder::plan(...).
818 int p = (id - 1 + nPathSeg) % nPathSeg;
819 int e = (id + 1 + nPathSeg) % nPathSeg;
820 smooth(id, p, e, 1.0);
821
822 return 1;
823 }
824
825
826 /* compute path for overtaking the "next colliding" car */
overtake(int trackSegId,tSituation * s,MyCar * myc,OtherCar * ocar)827 int Pathfinder::overtake(int trackSegId, tSituation *s, MyCar* myc, OtherCar* ocar)
828 {
829 if (collcars == 0) return 0;
830
831 int i, j, m = 0;
832 float freefrommiddle = -1000.0; // Invlaid, nothing free.
833
834 if (collcars > 1) {
835 int lanesize = (int) track->getSegmentPtr(trackSegId)->getWidth();
836 int* freelane = new int[lanesize];
837
838 for (i = 0; i < lanesize; i++) {
839 freelane[i] = 0;
840 }
841
842 for (i = 0; i < collcars; i++) {
843 if (o[i].speed > myc->getSpeed()) continue;
844 int lid = (int) (o[i].disttomiddle + lanesize/2.0 + 0.5);
845 if (lid > 2 && lid < lanesize-2) {
846 freelane[lid] += 1;
847 freelane[lid-1] += 1;
848 freelane[lid+1] += 1;
849 if (o[i].width/2.0 > 1.5) {
850 freelane[lid-2] += 1;
851 freelane[lid+2] += 1;
852 }
853 } else if (lid >= 0 && lid < lanesize){
854 freelane[lid] += 2;
855 }
856 }
857
858 // Search free lane.
859 for (i = 0; i < lanesize-4; i++) {
860 if (freelane[i] == 0 && freelane[i+1] == 0 &&
861 freelane[i+2] == 0 && freelane[i+3] == 0
862 ) {
863 float l = (i+2) - track->getSegmentPtr(trackSegId)->getWidth()/2.0;
864 if (fabs(l) < fabs(freefrommiddle)) {
865 freefrommiddle = l;
866 }
867 }
868 }
869
870 delete [] freelane;
871 }
872
873 const int start = (trackSegId - (int) ((2.0 + myc->CARLEN)/TRACKRES) + nPathSeg) % nPathSeg;
874 const int nearend = (trackSegId + (int) (2.0*myc->CARLEN/TRACKRES)) % nPathSeg;
875
876 OtherCar* nearestCar = NULL; /* car near in time, not in space ! (next reached car) */
877 double minTime = FLT_MAX;
878 double minorthdist = FLT_MAX; /* near in space */
879 double orthdist = FLT_MAX;
880 int minorthdistindex = 0;
881 int collcarindex = 0;
882
883 //int i, m = 0;
884 for (i = 0; i < collcars; i++) {
885 // TODO: MAGIC number
886 if (o[i].dist < COLLDIST*1.5) { // always?
887 double dst = o[i].minorthdist;
888 if (o[i].time > 0.0 && o[i].time < minTime) {
889 minTime = o[i].time;
890 collcarindex = i;
891 orthdist = dst;
892 }
893 if (dst < minorthdist && track->isBetween(start, nearend, o[i].collcar->getCurrentSegId())) {
894 minorthdist = dst;
895 minorthdistindex = i;
896 }
897 m++;
898 }
899 }
900
901 if (m == 0) return 0;
902
903 bool sidechangeallowed;
904
905 if (minorthdist <= myc->OVERTAKEMINDIST && o[collcarindex].dist >= o[minorthdistindex].dist) {
906 collcarindex = minorthdistindex;
907 nearestCar = o[minorthdistindex].collcar;
908
909 // Reject overtaking teammate if possible.
910 if (teammate != NULL && nearestCar->getCarPtr() == teammate) {
911 if (o[minorthdistindex].dist > (nearestCar->getCarPtr()->_dimension_x + myc->getCarPtr()->_dimension_x)/2.0 &&
912 (nearestCar->getCarPtr()->_dammage - myc->getCarPtr()->_dammage) < myc->TEAM_DAMAGE_CHANGE_LEAD)
913 {
914 return 0;
915 }
916 }
917
918 sidechangeallowed = false;
919 } else if (minTime < FLT_MAX){
920 nearestCar = o[collcarindex].collcar;
921 sidechangeallowed = true;
922 minorthdist = orthdist;
923 int i;
924 // TODO: MAGIG NUMBER.
925 for (i = 0; i <= (int) myc->MINOVERTAKERANGE; i += 10) {
926 if (track->getSegmentPtr((trackSegId+((int) (i/TRACKRES))) % nPathSeg)->getRadius() < myc->OVERTAKERADIUS) return 0;
927 }
928
929 // Reject overtaking teammate.
930 if (teammate != NULL &&
931 nearestCar->getCarPtr() == teammate &&
932 (nearestCar->getCarPtr()->_dammage - myc->getCarPtr()->_dammage) < myc->TEAM_DAMAGE_CHANGE_LEAD)
933 {
934 return 0;
935 }
936
937 } else return 0;
938
939 /* not enough space, so we try to overtake */
940 if (((o[collcarindex].mincorner < myc->CARWIDTH/2.0 + myc->DIST) && (minTime < myc->TIMETOCATCH)) || !sidechangeallowed) {
941 int overtakerange = (int) MIN(MAX((3.0*(3.0/myc->TIMETOCATCH*minTime)*myc->getSpeed()), myc->MINOVERTAKERANGE)/TRACKRES, AHEAD );
942 double d = o[collcarindex].disttomiddle;
943 double mydisttomiddle = track->distToMiddle(myc->getCurrentSegId(), myc->getCurrentPos());
944 double y[3], ys[3], s[3];
945
946
947 y[0] = track->distToMiddle(trackSegId, myc->getCurrentPos());
948 double alpha = PI/2.0 - acos((*myc->getDir())*(*track->getSegmentPtr(trackSegId)->getToRight()));
949
950 int trackSegId1;
951 if (minTime < myc->TIMETOCATCH && o[collcarindex].catchdist > 1.0) {
952 trackSegId1 = (trackSegId + MIN((int) (MAX((o[collcarindex].catchdist), myc->getSpeed()/4.0)/TRACKRES), overtakerange/3)) % nPathSeg;
953 } else {
954 trackSegId1 = (trackSegId + overtakerange/3) % nPathSeg;
955 }
956
957 double w = track->getSegmentPtr(nearestCar->getCurrentSegId())->getWidth() / 2;
958 double pathtomiddle = track->distToMiddle(trackSegId1, psdyn->getLoc(trackSegId1));
959 double paralleldist = o[collcarindex].cosalpha*dist(myc->getCurrentPos(), nearestCar->getCurrentPos());
960
961 if (!sidechangeallowed) {
962 if (paralleldist > 1.5*myc->CARLEN) {
963 int i;
964 for (i = 0; i <= (int) myc->MINOVERTAKERANGE; i += 10) {
965 if (track->getSegmentPtr((trackSegId+((int) (i/TRACKRES))) % nPathSeg)->getRadius() < myc->OVERTAKERADIUS) return 0;
966 }
967 vec2d dir = *o[collcarindex].collcar->getCurrentPos()- *myc->getCurrentPos();
968 double pathtocarsgn = sign(myc->getDir()->fakeCrossProduct(&dir));
969
970 y[1] = d + myc->OVERTAKEDIST*pathtocarsgn;
971 if (fabs(y[1]) > w - (1.0*myc->CARWIDTH)) {
972 y[1] = d - myc->OVERTAKEDIST*pathtocarsgn;
973 }
974
975
976 double beta = PI/2.0 - acos((*nearestCar->getDir())*(*track->getSegmentPtr(trackSegId)->getToRight()));
977 //double beta = PI/2.0 - acos((*nearestCar->getDir())*(*psopt->getOptToRight(trackSegId)));
978
979 if (y[1] - mydisttomiddle >= 0.0) {
980 if (alpha < beta + myc->OVERTAKEANGLE) alpha = alpha + myc->OVERTAKEANGLE;
981 } else {
982 if (alpha > beta - myc->OVERTAKEANGLE) alpha = alpha - myc->OVERTAKEANGLE;
983 }
984 } else {
985 double beta = PI/2.0 - acos((*nearestCar->getDir())*(*track->getSegmentPtr(trackSegId)->getToRight()));
986 //double beta = PI/2.0 - acos((*nearestCar->getDir())*(*psopt->getOptToRight(trackSegId)));
987
988 double delta = mydisttomiddle - d;
989 if (delta >= 0.0) {
990 if (alpha < beta + myc->OVERTAKEANGLE) alpha = beta + myc->OVERTAKEANGLE;
991 } else {
992 if (alpha > beta - myc->OVERTAKEANGLE) alpha = beta - myc->OVERTAKEANGLE;
993 }
994 double cartocarsgn = sign(delta);
995 y[1] = d + myc->OVERTAKEDIST*cartocarsgn;
996 if (fabs(y[1]) > w - (1.0*myc->CARWIDTH)) {
997 y[1] = cartocarsgn*(w - (myc->CARWIDTH/2.0 + myc->MARGIN));
998 }
999
1000 if (minorthdist > 1.0 /*myc->OVERTAKEMINDIST*/) o[collcarindex].overtakee = true;
1001 //o[collcarindex].overtakee = true;
1002 }
1003 } else {
1004 // Case when we can change the lane without crashing into the opponent.
1005 double pathtocarsgn = sign(pathtomiddle - d);
1006 if (collcars == 1) {
1007 y[1] = d + -sign(d)*(1.5+o[collcarindex].width/2.0 + myc->getCarPtr()->_dimension_y);
1008 } else if (freefrommiddle > -100.0) {
1009 y[1] = freefrommiddle;
1010 } else {
1011 y[1] = d + (myc->OVERTAKEDIST+o[collcarindex].width/2.0)*pathtocarsgn;
1012 }
1013 if (pathtocarsgn < 0.0 && y[1] > pathtomiddle) return 0;
1014 if (pathtocarsgn > 0.0 && y[1] <pathtomiddle) return 0;
1015
1016 if (fabs(y[1]) > w - (0.5*myc->CARWIDTH+2.0*myc->MARGIN)) {
1017 y[1] = d - myc->OVERTAKEDIST*pathtocarsgn;
1018 }
1019 }
1020
1021 double ww = w - (myc->CARWIDTH + myc->MARGIN);
1022 if ((y[1] > ww && alpha > 0.0) || (y[1] < -ww && alpha < 0.0)) {
1023 alpha = 0.0;
1024 }
1025
1026 y[1] = y[1] - pathtomiddle;
1027
1028 ys[0] = tan(alpha);
1029 ys[1] = 0.0;
1030
1031 /* set up point 2 */
1032 int trackSegId2 = (trackSegId + overtakerange) % nPathSeg;
1033 y[2] = track->distToMiddle(trackSegId2, psopt->getOptLoc(trackSegId2));
1034 ys[2] = pathOptSlope(trackSegId2);
1035
1036 // set up parameter s
1037 s[0] = s[1] = s[2] = 0.0;
1038 //s[0] = 0.0;
1039 for (i = trackSegId; (j = (i + nPathSeg) % nPathSeg) != trackSegId1; i++) {
1040 s[1] += dist(track->getSegmentPtr(j)->getMiddle(), track->getSegmentPtr((j+1)%nPathSeg)->getMiddle());//track->getSegmentPtr3D(j)->getLength();
1041 }
1042 for (i = trackSegId1; (j = (i + nPathSeg) % nPathSeg) != trackSegId2; i++) {
1043 s[2] += dist(track->getSegmentPtr(j)->getMiddle(), track->getSegmentPtr((j+1)%nPathSeg)->getMiddle());//track->getSegmentPtr3D(j)->getLength();
1044 }
1045 s[2] += s[1];
1046
1047 // check path for leaving to track
1048 double newdisttomiddle[AHEAD];
1049 int i, j;
1050 double l = 0.0;
1051 vec2d q;
1052 const vec2d *pp, *qq;
1053 for (i = trackSegId; (j = (i + nPathSeg) % nPathSeg) != trackSegId2; i++) {
1054 d = spline(3, l, s, y, ys);
1055 float bordermargin = (track->getSegmentPtr(j)->getRadius() > 200.0) ? 0.0 : myc->MARGIN;
1056 if (fabs(d) > (track->getSegmentPtr(j)->getWidth() - myc->CARWIDTH) / 2.0 - bordermargin) {
1057 o[collcarindex].overtakee = false;
1058 return 0;
1059 }
1060 newdisttomiddle[i - trackSegId] = d;
1061 l += dist(track->getSegmentPtr(j)->getMiddle(), track->getSegmentPtr((j+1)%nPathSeg)->getMiddle());
1062 }
1063
1064 // set up the path
1065 for (i = trackSegId; (j = (i + nPathSeg) % nPathSeg) != trackSegId2; i++) {
1066 pp = track->getSegmentPtr(j)->getMiddle();
1067 qq = track->getSegmentPtr(j)->getToRight();
1068 q = *pp + (*qq)*newdisttomiddle[i - trackSegId];
1069 psdyn->setLoc(&q, j);
1070 }
1071
1072 // reload old trajectory where needed
1073 for (i = trackSegId2; (j = (i+nPathSeg) % nPathSeg) != (trackSegId+AHEAD) % nPathSeg; i ++) {
1074 psdyn->setLoc(psopt->getOptLoc(j), j);
1075 }
1076
1077 // align previos point for getting correct speedsqr in Pathfinder::plan(...).
1078 int p = (trackSegId - 1 + nPathSeg) % nPathSeg;
1079 int e = (trackSegId + 1 + nPathSeg) % nPathSeg;
1080 smooth(trackSegId, p, e, 1.0);
1081
1082 return 1;
1083 } else {
1084 return 0;
1085 }
1086 }
1087
1088
1089 /* collect data about other cars relative to me */
updateOCar(int trackSegId,tSituation * s,MyCar * myc,OtherCar * ocar,tOCar * o)1090 inline int Pathfinder::updateOCar(int trackSegId, tSituation *s, MyCar* myc, OtherCar* ocar, tOCar* o)
1091 {
1092 const int start = (trackSegId - (int) ((1.0 + myc->CARLEN/2.0)/TRACKRES) + nPathSeg) % nPathSeg;
1093 const int end = (trackSegId + (int) (COLLDIST/TRACKRES) + nPathSeg) % nPathSeg;
1094
1095 int i, n = 0; /* counter for relevant cars */
1096
1097 for (i = 0; i < s->_ncars; i++) {
1098 tCarElt* car = ocar[i].getCarPtr();
1099 /* is it me ? */
1100 if (car != myc->getCarPtr()) {
1101 int seg = ocar[i].getCurrentSegId();
1102 /* get the next car to catch up */
1103 if (track->isBetween(start, end, seg) && !(car->_state & (RM_CAR_STATE_DNF | RM_CAR_STATE_PULLUP | RM_CAR_STATE_PULLSIDE | RM_CAR_STATE_PULLDN | RM_CAR_STATE_NO_SIMU))) {
1104 o[n].cosalpha = (*myc->getDir())*(*ocar[i].getDir());
1105 o[n].speed = ocar[i].getSpeed()*o[n].cosalpha;
1106 int j, k = track->diffSegId(trackSegId, seg);
1107
1108 // Compute distance along the path to the center of gravity of the opponent.
1109 if ( k < 40.0/TRACKRES) {
1110 o[n].dist = 0.0;
1111 int l = MIN(trackSegId, seg);
1112 for (j = l; j < l + k; j++) {
1113 o[n].dist += psdyn->getLength(j % nPathSeg);
1114 }
1115 } else {
1116 o[n].dist = k*TRACKRES;
1117 }
1118 o[n].collcar = &ocar[i];
1119 o[n].time = o[n].dist/(myc->getSpeed() - o[n].speed);
1120 if (o[n].time < 0.0) {
1121 o[n].time = FLT_MAX;
1122 }
1123
1124 o[n].disttomiddle = track->distToMiddle(seg, ocar[i].getCurrentPos());
1125 o[n].speedsqr = sqr(o[n].speed);
1126 o[n].catchdist = (int) (o[n].dist/(MIN(myc->getSpeed(), sqrt(psdyn->getSpeedsqr(seg))) - ocar[i].getSpeed())*MIN(myc->getSpeed(), sqrt(psdyn->getSpeedsqr(seg))));//myc->getSpeed());
1127 o[n].catchsegid = ((int) (o[n].catchdist/TRACKRES) + trackSegId + nPathSeg) % nPathSeg;
1128 o[n].overtakee = false;
1129 o[n].disttopath = distToPath(seg, ocar[i].getCurrentPos());
1130 double gm = track->getSegmentPtr(seg)->getKfriction()*myc->CFRICTION;
1131 double qs = o[n].speedsqr;
1132 o[n].brakedist = (myc->getSpeedSqr() - o[n].speedsqr)*(myc->mass/(2.0*gm*g*myc->mass + (qs)*(gm*myc->ca)));
1133 o[n].mincorner = FLT_MAX;
1134 o[n].minorthdist = FLT_MAX;
1135 for (j = 0; j < 4; j++) {
1136 vec2d e(car->pub.corner[j].ax, car->pub.corner[j].ay);
1137 double corner = fabs(distToPath(seg, &e));
1138 double orthdist = track->distGFromPoint(myc->getCurrentPos(), myc->getDir(), &e) - myc->CARWIDTH/2.0;
1139 if (corner < o[n].mincorner) o[n].mincorner = corner;
1140 if (orthdist < o[n].minorthdist) o[n].minorthdist = orthdist;
1141 }
1142
1143 // Compute with of the car along the track.
1144 const vec2d* tr = track->getSegmentPtr(seg)->getToRight();
1145 vec2d trackdir(-tr->y, tr->x);
1146 float carcosa = trackdir*(*ocar[i].getDir());
1147 o[n].width = car->_dimension_x*sin(acos(carcosa)) + car->_dimension_y*carcosa;
1148 n++;
1149 }
1150 }
1151 }
1152 return n;
1153 }
1154
1155
updateOverlapTimer(int trackSegId,tSituation * s,MyCar * myc,OtherCar * ocar,tOCar * o,tOverlapTimer * ov)1156 inline void Pathfinder::updateOverlapTimer(int trackSegId, tSituation *s, MyCar* myc, OtherCar* ocar, tOCar* o, tOverlapTimer* ov)
1157 {
1158 const int start = (trackSegId - (int) (myc->OVERLAPSTARTDIST/TRACKRES) + nPathSeg) % nPathSeg;
1159 const int end = (trackSegId - (int) ((2.0 + myc->CARLEN/2.0)/TRACKRES) + nPathSeg) % nPathSeg;
1160 const int startfront = (trackSegId + (int) ((2.0 + myc->CARLEN/2.0)/TRACKRES)) % nPathSeg;
1161 const int endfront = (trackSegId + (int) (myc->OVERLAPSTARTDIST/TRACKRES)) % nPathSeg;
1162
1163 int i;
1164
1165 for (i = 0; i < s->_ncars; i++) {
1166 tCarElt* car = ocar[i].getCarPtr();
1167 tCarElt* me = myc->getCarPtr();
1168 /* is it me, and in case not, has the opponent more laps than me? */
1169 if ((car != me) && (car->race.laps > me->race.laps) &&
1170 !(car->_state & (RM_CAR_STATE_DNF | RM_CAR_STATE_PULLUP | RM_CAR_STATE_PULLSIDE | RM_CAR_STATE_PULLDN | RM_CAR_STATE_NO_SIMU))) {
1171 int seg = ocar[i].getCurrentSegId();
1172 if (track->isBetween(start, end, seg)) {
1173 ov[i].time += s->deltaTime;
1174 } else if (track->isBetween(startfront, endfront, seg)) {
1175 ov[i].time = myc->LAPBACKTIMEPENALTY;
1176 } else {
1177 if (ov[i].time > 0.0) ov[i].time -= s->deltaTime;
1178 else ov[i].time += s->deltaTime;
1179 }
1180 } else {
1181 ov[i].time = 0.0;
1182 }
1183 }
1184 }
1185
1186
1187 /* compute trajectory to let opponent overlap */
letoverlap(int trackSegId,tSituation * situation,MyCar * myc,OtherCar * ocar,tOverlapTimer * ov)1188 int Pathfinder::letoverlap(int trackSegId, tSituation *situation, MyCar* myc, OtherCar* ocar, tOverlapTimer* ov)
1189 {
1190 const int start = (trackSegId - (int) (myc->OVERLAPPASSDIST/TRACKRES) + nPathSeg) % nPathSeg;
1191 const int end = (trackSegId - (int) ((2.0 + myc->CARLEN/2.0)/TRACKRES) + nPathSeg) % nPathSeg;
1192 int k;
1193
1194 for (k = 0; k < situation->_ncars; k++) {
1195
1196 if ((ov[k].time > myc->OVERLAPWAITTIME) && track->isBetween(start, end, ocar[k].getCurrentSegId())) {
1197 /* let overtake */
1198 double s[4], y[4], ys[4];
1199 // TODO: constant.
1200 const int DST = 400;
1201
1202 // TODO: optslope correct or dynslope?
1203 ys[0] = pathDynSlope(trackSegId);
1204 if (fabs(ys[0]) > PI/180.0) return 0;
1205
1206 int trackSegId1 = (trackSegId + (int) (DST/(4.0*TRACKRES)) + nPathSeg) % nPathSeg;
1207 int trackSegId2 = (trackSegId + (int) (DST*3.0/(4.0*TRACKRES)) + nPathSeg) % nPathSeg;
1208 int trackSegId3 = (trackSegId + (int) (DST/TRACKRES) + nPathSeg) % nPathSeg;
1209 double width = track->getSegmentPtr(trackSegId1)->getWidth();
1210
1211 /* point 0 */
1212 y[0] = track->distToMiddle(trackSegId, myc->getCurrentPos());
1213
1214 /* point 1 */
1215 // TODO: REMOVE magic numbers.
1216 y[1] = sign(y[0])*MIN((width/2.0 - 2.0*myc->CARWIDTH - myc->MARGIN), (15.0/2.0));
1217 ys[1] = 0.0;
1218
1219 /* point 2 */
1220 y[2] = y[1];
1221 ys[2] = 0.0;
1222
1223 /* point 3*/
1224 y[3] = track->distToMiddle(trackSegId3, psopt->getOptLoc(trackSegId3));
1225 // TODO: optslope correct or dynslope?
1226 ys[3] = pathOptSlope(trackSegId3);
1227
1228 /* set up parameter s */
1229 s[0] = 0.0;
1230 s[1] = countSegments(trackSegId, trackSegId1)*TRACKRES;
1231 s[2] = s[1] + countSegments(trackSegId1, trackSegId2)*TRACKRES;
1232 s[3] = s[2] + countSegments(trackSegId2, trackSegId3)*TRACKRES;
1233
1234 /* check path for leaving to track */
1235 double newdisttomiddle[AHEAD], d;
1236 int i, j;
1237 double l = 0.0;
1238 vec2d q;
1239 const vec2d *pp, *qq;
1240 for (i = trackSegId; (j = (i + nPathSeg) % nPathSeg) != trackSegId3; i++) {
1241 d = spline(4, l, s, y, ys);
1242 if (fabs(d) > (track->getSegmentPtr(j)->getWidth() - myc->CARWIDTH) / 2.0 - myc->MARGIN) {
1243 return 0;
1244 }
1245 newdisttomiddle[i - trackSegId] = d;
1246 l += TRACKRES;
1247 }
1248
1249 /* set up the path */
1250 for (i = trackSegId; (j = (i + nPathSeg) % nPathSeg) != trackSegId3; i++) {
1251 pp = track->getSegmentPtr(j)->getMiddle();
1252 qq = track->getSegmentPtr(j)->getToRight();
1253 q = *pp + (*qq)*newdisttomiddle[i - trackSegId];
1254 psdyn->setLoc(&q, j);
1255 }
1256
1257 /* reload old trajectory where needed */
1258 for (i = trackSegId3; (j = (i+nPathSeg) % nPathSeg) != (trackSegId+AHEAD) % nPathSeg; i ++) {
1259 psdyn->setLoc(psopt->getOptLoc(j), j);
1260 }
1261
1262 /* reset all timer to max 3.0 */
1263 for (j = 0; j < situation->_ncars; j++) {
1264 ov[j].time = MIN(ov[j].time, 3.0);
1265 }
1266 return 1;
1267 }
1268 }
1269 return 0;
1270 }
1271