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