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