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