1 /***************************************************************************
2 
3     file                 : learn.cpp
4     created              : Wed Aug 28 16:36:00 CET 2004
5     copyright            : (C) 2004 by Bernhard Wymann
6     email                : berniw@bluewin.ch
7     version              : $Id: learn.cpp,v 1.3 2006/03/06 22:43:50 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 
21 #include "learn.h"
22 #include <portability.h>
23 
24 #define MAGIC1		0x34be1f01
25 // Change MAGIC2 if the learning file format changes.
26 #define MAGIC2		0x45aa9fbe
27 #define STRINGID	"TORCS"
28 
SegLearn(tTrack * t,tSituation * s,int driverindex)29 SegLearn::SegLearn(tTrack* t, tSituation *s, int driverindex)
30 {
31 	int i;
32 	radius = new float[t->nseg];
33 	updateid = new int[t->nseg];
34 	nseg = t->nseg;
35 
36 	if (!readKarma(t, s, radius, updateid, driverindex)) {
37 
38 		tTrackSeg *seg = t->seg;
39 
40 		// Switch seg to seg 0 for sure.
41 		while (seg->id != 0) {
42 			seg = seg->prev;
43 		}
44 
45 		for (i = 0; i < t->nseg; i++) {
46 			radius[i] = 0.0f;
47 			updateid[i] = i;
48 			// Search the last turn in case of a straight.
49 			if (seg->type == TR_STR) {
50 				tTrackSeg *cs = seg;
51 				while (cs->type == TR_STR) {
52 					cs = cs->prev;
53 				}
54 				updateid[seg->id] = cs->id;
55 			}
56 			seg = seg->next;
57 		}
58 	}
59 
60 	check = false;
61 	rmin = t->width/2.0f;
62 	prevtype = lastturn = TR_STR;
63 }
64 
65 
~SegLearn()66 SegLearn::~SegLearn()
67 {
68 	writeKarma();
69 	delete [] radius;
70 	delete [] updateid;
71 }
72 
73 
update(tSituation * s,tTrack * t,tCarElt * car,int alone,float offset,float outside,float * r)74 void SegLearn::update(tSituation *s, tTrack *t, tCarElt *car, int alone, float offset, float outside, float *r)
75 {
76 	// Still on the same segment, alone, offset near 0, check.
77 	tTrackSeg *seg = car->_trkPos.seg;
78 
79 	if (seg->type == lastturn || seg->type == TR_STR) {
80 		if (fabs(offset) < 0.2f &&
81 			check == true &&
82 			alone > 0
83 		) {
84 			// + to left, - to right
85 			float tomiddle = car->_trkPos.toMiddle;
86 			float dr = 0.0f;
87 			if (lastturn == TR_RGT) {
88 				dr = outside - tomiddle;
89 			} else if (lastturn == TR_LFT) {
90 				dr = outside + tomiddle;
91 			}
92 			if (dr < rmin) {
93 				rmin = dr;
94 			}
95 		} else {
96 			check = false;
97 		}
98 	}
99 
100 	if (seg->type != prevtype) {
101 		prevtype = seg->type;
102 		if (seg->type != TR_STR) {
103 			if (check == true) {
104 				tTrackSeg *cs = seg->prev;
105 				// Skip straights.
106 				while (cs->type == TR_STR) {
107 					cs = cs->prev;
108 				}
109 
110 				while (cs->type == lastturn) {
111 					if (radius[updateid[cs->id]] + rmin < 0.0f) {
112 						rmin = MAX(cs->radius - r[cs->id], rmin);
113 					}
114 					radius[updateid[cs->id]] += rmin;
115 					radius[updateid[cs->id]] = MIN(radius[updateid[cs->id]], 1000.0f);
116 					cs = cs->prev;
117 				}
118 			}
119 			check = true;
120 			rmin = MIN(seg->width/2.0f, seg->radius/10.0f);
121 			lastturn = seg->type;
122 		}
123 	}
124 }
125 
126 
writeKarma()127 void SegLearn::writeKarma()
128 {
129 	// Build the directory name.
130 	char path[sizeof(filename)];
131 	strncpy(path, filename, sizeof(path));
132 	char* end = strrchr(path, '/');
133 	if (end != NULL) {
134 		*end = '\0';
135 	}
136 
137 	// Create the directory and try to write data.
138 	if (GfCreateDir(path) == GF_DIR_CREATED) {
139 		// Try to write data.
140 		FILE *fd = fopen(filename, "wb");
141 		if (fd != NULL) {
142 			// Create header: Magic Number, #segments, string, version.
143 			int magic = MAGIC1;
144 			int magic2 = MAGIC2;
145 			char string[] = STRINGID;
146 
147 			// The magic numbers are used to catch 32/64 bit mismatches and little/big
148 			// endian mismatches. Here the patterns I expect at the beginning of the files.
149 			// I call 4 bytes a UNIT, MAGIC1 bytes A1, A2, A3, A4, MAGIC2 bytes B1, B2, B3, B4,
150 			// a zeroed byte 00):
151 			// 32bit big endian   : UNIT1-A1A2A3A4; UNIT2-B1B2B3B4; ...
152 			// 32bit little endian: UNIT1-A4A3A2A1; UNIT2-B4B3B2B1; ...
153 			// 64bit big endian   : UNIT1-00000000; UNIT2-A1A2A3A4; UNIT3-00000000; UNIT4-B1B2B3B4; ...
154 			// 64bit little endian: UNIT1-A4A3A2A1; UNIT2-00000000; UNIT3-B4B3B2B1; UNIT4-00000000: ...
155 			//
156 			// Like you can see there is created a unique pattern for each architecture in
157 			// UNIT1 and UNIT2.
158 
159 			fwrite(&magic, sizeof(magic), 1, fd);			// magic number.
160 			fwrite(&magic2, sizeof(magic2), 1, fd);			// magic number 2.
161 			fwrite(&nseg, sizeof(nseg), 1, fd);				// # segments.
162 			fwrite(string, sizeof(string), 1, fd);			// string.
163 
164 			for (int i = 0; i < nseg; i++) {
165 				fwrite(&updateid[i], sizeof(updateid[0]), 1, fd);
166 				fwrite(&radius[i], sizeof(radius[0]), 1, fd);
167         	}
168         	fclose(fd);
169 		}
170 	}
171 }
172 
173 
readKarma(tTrack * track,tSituation * s,float * radius,int * uid,int driverindex)174 bool SegLearn::readKarma(tTrack* track, tSituation *s, float *radius, int *uid, int driverindex)
175 {
176 	FILE* fd = getKarmaFilename(track, s, driverindex);
177 
178 	if (fd != NULL) {
179 		// Check if the file is valid.
180 		int magic = 0;
181 		int magic2 = 0;
182 		int nseg = 0;
183 		char string[sizeof(STRINGID)] = "";
184 
185 		fread(&magic, sizeof(magic), 1, fd);
186 		fread(&magic2, sizeof(magic2), 1, fd);
187 		fread(&nseg, sizeof(nseg), 1, fd);
188 		fread(string, sizeof(string), 1, fd);
189 
190 		if (magic == MAGIC1 && magic2 == MAGIC2 &&
191 			nseg == track->nseg &&
192 			strncmp(string, STRINGID, sizeof(string)) == 0
193 			)
194 		{
195 			for (int i = 0; i < track->nseg; i++) {
196 				fread(&uid[i], sizeof(uid[0]), 1, fd);
197 				fread(&radius[i], sizeof(radius[0]), 1, fd);
198         	}
199 			fclose(fd);
200 			return true;
201 		}
202 		fclose(fd);
203 	}
204 	return false;
205 }
206 
207 
getKarmaFilename(tTrack * track,tSituation * s,int driverindex)208 FILE* SegLearn::getKarmaFilename(tTrack* track, tSituation *s, int driverindex)
209 {
210 	const int TBUFSIZE = 256;
211 	char tbuf[TBUFSIZE];
212 	char* trackname = strrchr(track->filename, '/') + 1;
213 	char* tracknameend = strchr(trackname, '.') - 1;
214 
215 	strncpy(tbuf, trackname, tracknameend-trackname+1);
216 	tbuf[tracknameend-trackname+1] = 0;
217 
218 	FILE* fd;
219 	char buffer[sizeof(filename)];
220 
221 	switch (s->_raceType) {
222 		case RM_TYPE_RACE:
223 			fd = tryKarmaFilename(buffer, sizeof(buffer), "%sdrivers/bt/%d/race/%s.karma", driverindex, tbuf, s->_raceType == RM_TYPE_RACE);
224 			if ( fd != NULL) {
225 				return fd;
226 			} // not found, try the next.
227 		case RM_TYPE_QUALIF:
228 			fd = tryKarmaFilename(buffer, sizeof(buffer), "%sdrivers/bt/%d/qualifying/%s.karma", driverindex, tbuf, s->_raceType == RM_TYPE_QUALIF);
229 			if ( fd != NULL) {
230 				return fd;
231 			} // not found, try the next.
232 		case RM_TYPE_PRACTICE:
233 			fd = tryKarmaFilename(buffer, sizeof(buffer), "%sdrivers/bt/%d/practice/%s.karma", driverindex, tbuf, s->_raceType == RM_TYPE_PRACTICE);
234 			if ( fd != NULL) {
235 				return fd;
236 			} // not found, try the next.
237 		default:
238 			return NULL;
239 			break;
240 	}
241 }
242 
243 
tryKarmaFilename(char * buffer,int size,const char * path,int driverindex,const char * tbuf,bool storelocalfilename)244 FILE* SegLearn::tryKarmaFilename(char* buffer, int size, const char *path, int driverindex, const char *tbuf, bool storelocalfilename)
245 {
246 	// First construct a path to the local directory ($HOME/...).
247 	snprintf(buffer, size, path, GetLocalDir(), driverindex, tbuf);
248 	if (storelocalfilename == true) {
249 		strncpy(filename, buffer, sizeof(filename));
250 	}
251 
252 	// Try to open the local file.
253 	FILE* fd;
254 	if ((fd = fopen(buffer, "rb")) != NULL) {
255 		return fd;
256 	}
257 
258 	// Not found, try the global path.
259 	snprintf(buffer, size, path, GetDataDir(), driverindex, tbuf);
260 	return fopen(buffer, "rb");
261 }
262