1 /***************************************************************************
2 
3     file                 : strategy.cpp
4     created              : Wed Sep 22 15:32:21 CET 2004
5     copyright            : (C) 2004 Bernhard Wymann
6     email                : berniw@bluewin.ch
7     version              : $Id: strategy.cpp,v 1.1.2.1 2008/05/28 21:34:45 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 	Very simple stategy sample implementation.
22 */
23 
24 
25 #include "strategy.h"
26 
27 const float SimpleStrategy::MAX_FUEL_PER_METER = 0.0008f;	// [kg/m] fuel consumtion.
28 const int SimpleStrategy::PIT_DAMMAGE = 5000;				// [-]
29 
30 
SimpleStrategy()31 SimpleStrategy::SimpleStrategy() :
32 	m_fuelchecked(false),
33 	m_fuelperlap(0.0f),
34 	m_lastpitfuel(0.0f),
35 	m_fuelsum(0.0f)
36 {
37 	// Nothing so far.
38 }
39 
40 
~SimpleStrategy()41 SimpleStrategy::~SimpleStrategy()
42 {
43 	// Nothing so far.
44 }
45 
46 
47 // Trivial strategy: fill in as much fuel as required for the whole race, or if the tank is
48 // too small fill the tank completely.
setFuelAtRaceStart(tTrack * t,void ** carParmHandle,tSituation * s,int index)49 void SimpleStrategy::setFuelAtRaceStart(tTrack* t, void **carParmHandle, tSituation *s, int index)
50 {
51 	// Load and set parameters.
52 	float fuel = GfParmGetNum(*carParmHandle, BT_SECT_PRIV, BT_ATT_FUELPERLAP, (char*) NULL, t->length*MAX_FUEL_PER_METER);
53 	m_expectedfuelperlap = fuel;
54 	float maxfuel = GfParmGetNum(*carParmHandle, SECT_CAR, PRM_TANK, (char*) NULL, 100.0f);
55 	fuel *= (s->_totLaps + 1.0f);
56 	m_lastfuel = MIN(fuel, maxfuel);
57 	GfParmSetNum(*carParmHandle, SECT_CAR, PRM_FUEL, (char*) NULL, m_lastfuel);
58 }
59 
60 
update(tCarElt * car,tSituation * s)61 void SimpleStrategy::update(tCarElt* car, tSituation *s)
62 {
63 	// Fuel statistics update.
64 	int id = car->_trkPos.seg->id;
65 	// Range must include enough segments to be executed once guaranteed.
66 	if (id >= 0 && id < 5 && !m_fuelchecked) {
67 		if (car->race.laps > 1) {
68 			m_fuelperlap = MAX(m_fuelperlap, (m_lastfuel+m_lastpitfuel-car->priv.fuel));
69 			m_fuelsum += (m_lastfuel+m_lastpitfuel-car->priv.fuel);
70 		}
71 		m_lastfuel = car->priv.fuel;
72 		m_lastpitfuel = 0.0;
73 		m_fuelchecked = true;
74 	} else if (id > 5) {
75 		m_fuelchecked = false;
76 	}
77 }
78 
79 
needPitstop(tCarElt * car,tSituation * s)80 bool SimpleStrategy::needPitstop(tCarElt* car, tSituation *s)
81 {
82 	// Question makes only sense if there is a pit.
83 	if (car->_pit != NULL) {
84 		// Do we need to refuel?
85 		int laps = car->_remainingLaps - car->_lapsBehindLeader;
86 		if (laps > 0) {
87 			float cmpfuel = (m_fuelperlap == 0.0f) ? m_expectedfuelperlap : m_fuelperlap;
88 			// TODO: Investigate if buggy for two pit stops in one lap, BUG?
89 			if (car->_fuel < 1.5*cmpfuel &&
90 				car->_fuel < laps*cmpfuel)
91 			{
92 					return true;
93 			}
94 		}
95 
96 		// Do we need to repair and the pit is free?
97 		if (car->_dammage > PIT_DAMMAGE && isPitFree(car)) {
98 			return true;
99 		}
100 	}
101 	return false;
102 }
103 
104 
isPitFree(tCarElt * car)105 bool SimpleStrategy::isPitFree(tCarElt* car)
106 {
107 	if (car->_pit != NULL) {
108 		if (car->_pit->pitCarIndex == TR_PIT_STATE_FREE) {
109 			return true;
110 		}
111 	}
112 	return false;
113 }
114 
115 
pitRefuel(tCarElt * car,tSituation * s)116 float SimpleStrategy::pitRefuel(tCarElt* car, tSituation *s)
117 {
118 	float fuel;
119 	float cmpfuel = (m_fuelperlap == 0.0f) ? m_expectedfuelperlap : m_fuelperlap;
120 	fuel = MAX(MIN((car->_remainingLaps + 1.0f)*cmpfuel - car->_fuel,
121 					car->_tank - car->_fuel),
122 			   0.0f);
123 	m_lastpitfuel = fuel;
124 	return fuel;
125 }
126 
127 
pitRepair(tCarElt * car,tSituation * s)128 int SimpleStrategy::pitRepair(tCarElt* car, tSituation *s)
129 {
130 	return car->_dammage;
131 }
132 
133 
update(tCarElt * car,tSituation * s)134 void SimpleStrategy2::update(tCarElt* car, tSituation *s)
135 {
136 	// Fuel statistics update.
137 	int id = car->_trkPos.seg->id;
138 	// Range must include enough segments to be executed once guaranteed.
139 	if (id >= 0 && id < 5 && !m_fuelchecked) {
140 		if (car->race.laps > 1) {
141 			//m_fuelperlap = MAX(m_fuelperlap, (m_lastfuel + m_lastpitfuel - car->priv.fuel));
142 			m_fuelsum += (m_lastfuel + m_lastpitfuel - car->priv.fuel);
143 			m_fuelperlap = (m_fuelsum/(car->race.laps - 1));
144 			// This is here for adding strategy decisions, otherwise it could be moved to pitRefuel
145 			// for efficiency.
146 			updateFuelStrategy(car, s);
147 		}
148 		m_lastfuel = car->priv.fuel;
149 		m_lastpitfuel = 0.0;
150 		m_fuelchecked = true;
151 	} else if (id > 5) {
152 		m_fuelchecked = false;
153 	}
154 }
155 
156 
setFuelAtRaceStart(tTrack * t,void ** carParmHandle,tSituation * s,int index)157 void SimpleStrategy2::setFuelAtRaceStart(tTrack* t, void **carParmHandle, tSituation *s, int index)
158 {
159 	// Load and set parameters.
160 	float fuel = GfParmGetNum(*carParmHandle, BT_SECT_PRIV, BT_ATT_FUELPERLAP, (char*) NULL, t->length*MAX_FUEL_PER_METER);
161 	m_expectedfuelperlap = fuel;
162 	// Pittime is pittime without refuel.
163 	m_pittime = GfParmGetNum(*carParmHandle, BT_SECT_PRIV, BT_ATT_PITTIME, (char*) NULL, 25.0f);
164 	m_bestlap = GfParmGetNum(*carParmHandle, BT_SECT_PRIV, BT_ATT_BESTLAP, (char*) NULL, 87.0f);
165 	m_worstlap = GfParmGetNum(*carParmHandle, BT_SECT_PRIV, BT_ATT_WORSTLAP, (char*) NULL, 87.0f);
166 	float maxfuel = GfParmGetNum(*carParmHandle, SECT_CAR, PRM_TANK, (char*) NULL, 100.0f);
167 
168 	// Fuel for the whole race.
169 	float fuelforrace = (s->_totLaps + 1.0f)*fuel;
170 	// Estimate minimum number of pit stops, -1 because the tank can be filled at the start.
171 	int pitstopmin = int(ceil(fuelforrace/maxfuel) - 1.0f);
172 	// Compute race times for min to min + 9 pit stops.
173 	int i;
174 	float mintime = FLT_MAX;
175 	int beststops = pitstopmin;
176 	m_lastfuel = maxfuel;
177 	for (i = 0; i < 10; i++) {
178 		float stintfuel = fuelforrace/(pitstopmin + i + 1);
179 		float fillratio = stintfuel/maxfuel;
180 		float avglapest = m_bestlap + (m_worstlap - m_bestlap)*fillratio;
181 		float racetime = (pitstopmin + i)*(m_pittime + stintfuel/8.0f) + s->_totLaps*avglapest;
182 		if (mintime > racetime) {
183 			mintime = racetime;
184 			beststops = i + pitstopmin;
185 			m_lastfuel = stintfuel;
186 			m_fuelperstint = stintfuel;
187 		}
188 	}
189 
190 	m_remainingstops = beststops;
191 	// Add fuel dependent on index to avoid fuel stop in the same lap.
192 	GfParmSetNum(*carParmHandle, SECT_CAR, PRM_FUEL, (char*) NULL, m_lastfuel + index*m_expectedfuelperlap);
193 }
194 
195 
updateFuelStrategy(tCarElt * car,tSituation * s)196 void SimpleStrategy2::updateFuelStrategy(tCarElt* car, tSituation *s)
197 {
198 	// Required additional fuel for the rest of the race. +1 because the computation happens right after
199 	// crossing the start line.
200 	float requiredfuel = ((car->_remainingLaps + 1) - ceil(car->_fuel/m_fuelperlap))*m_fuelperlap;
201 	if (requiredfuel < 0.0f) {
202 		// We have enough fuel to end the race, no further stop required.
203 		return;
204 	}
205 
206 	// Estimate minimum number of minimum remaining pit stops.
207 	int pitstopmin = int(ceil(requiredfuel/car->_tank));
208 	if (pitstopmin < 1) {
209 		// Should never come here becuase of the above test, leave it anyway.
210 		return;
211 	}
212 
213 	// Compute race times for min to min + 8 pit stops.
214 	int i;
215 	float mintime = FLT_MAX;
216 	int beststops = pitstopmin;
217 	for (i = 0; i < 9; i++) {
218 		float stintfuel = requiredfuel/(pitstopmin + i);
219 		float fillratio = stintfuel/car->_tank;
220 		float avglapest = m_bestlap + (m_worstlap - m_bestlap)*fillratio;
221 		float racetime = (pitstopmin + i)*(m_pittime + stintfuel/8.0f) + car->_remainingLaps*avglapest;
222 		if (mintime > racetime) {
223 			mintime = racetime;
224 			beststops = i + pitstopmin;
225 			m_fuelperstint = stintfuel;
226 		}
227 	}
228 
229 	m_remainingstops = beststops;
230 }
231 
232 
~SimpleStrategy2()233 SimpleStrategy2::~SimpleStrategy2()
234 {
235 	// Nothing so far.
236 }
237 
238 
pitRefuel(tCarElt * car,tSituation * s)239 float SimpleStrategy2::pitRefuel(tCarElt* car, tSituation *s)
240 {
241 	float fuel;
242 	if (m_remainingstops > 1) {
243 		fuel = MIN(m_fuelperstint, car->_tank - car->_fuel);
244 		m_remainingstops--;
245 	} else {
246 		float cmpfuel = (m_fuelperlap == 0.0f) ? m_expectedfuelperlap : m_fuelperlap;
247 		fuel = MAX(MIN((car->_remainingLaps + 1.0f)*cmpfuel - car->_fuel,
248 			       car->_tank - car->_fuel),
249 				   0.0f);
250 	}
251 
252 	m_lastpitfuel = fuel;
253 	return fuel;
254 }
255 
256