Merge lp:~armagetronad-dev/armagetronad/trunk-armagetronad-fortress_ai into lp:~armagetronad-dev/armagetronad/trunk-armagetronad-work
- trunk-armagetronad-fortress_ai
- Merge into trunk-armagetronad-work
Proposed by
Yann Kaiser
Status: | Work in progress |
---|---|
Proposed branch: | lp:~armagetronad-dev/armagetronad/trunk-armagetronad-fortress_ai |
Merge into: | lp:~armagetronad-dev/armagetronad/trunk-armagetronad-work |
Diff against target: |
5751 lines (+3742/-936) (has conflicts) 28 files modified
src/Makefile.am (+5/-1) src/engine/eCamera.cpp (+5/-0) src/engine/eGameObject.cpp (+7/-0) src/engine/eGameObject.h (+5/-5) src/engine/eSensor.cpp (+1/-1) src/engine/eSensor.h (+2/-2) src/tools/tError.cpp (+6/-0) src/tools/tError.h (+4/-0) src/tools/tMath.h (+14/-0) src/tron/gAIBase.cpp (+549/-131) src/tron/gAIBase.h (+92/-7) src/tron/gAINavigator.cpp (+1803/-0) src/tron/gAINavigator.h (+423/-0) src/tron/gCycle.cpp (+144/-736) src/tron/gCycle.h (+28/-8) src/tron/gCycleMovement.cpp (+23/-15) src/tron/gCycleMovement.h (+4/-0) src/tron/gGame.cpp (+1/-1) src/tron/gSensor.h (+1/-1) src/tron/gWall.cpp (+4/-4) src/tron/zone/zAI.cpp (+290/-0) src/tron/zone/zAI.h (+64/-0) src/tron/zone/zFortress.cpp (+204/-2) src/tron/zone/zFortress.h (+10/-0) src/tron/zone/zShape.cpp (+10/-10) src/tron/zone/zShape.h (+13/-9) src/tron/zone/zZone.cpp (+25/-2) src/tron/zone/zZone.h (+5/-1) Text conflict in src/tron/gAIBase.cpp Text conflict in src/tron/gAIBase.h Text conflict in src/tron/gCycle.cpp |
To merge this branch: | bzr merge lp:~armagetronad-dev/armagetronad/trunk-armagetronad-fortress_ai |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Manuel Moos | Needs Fixing | ||
Armagetron Advanced SQUAT | user feedback | Pending | |
Review via email: mp+11975@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 936. By Manuel Moos
-
Taught the AI to aim for the enemy cycle, not the gap, and to avoid going in narrow tunnels.
- 937. By Manuel Moos
-
Better insertions. Still crappy followup.
- 938. By Manuel Moos
-
Merging from mainline.
- 939. By Manuel Moos
-
Merging memory leak fix.
- 940. By Manuel Moos
-
Merged another memory leak.
- 941. By Manuel Moos
-
Merging from trunk again.
- 942. By Manuel Moos
-
Merging from mainline.
- 943. By Manuel Moos
-
merging from mainline.
- 944. By Manuel Moos
-
Merging from mainline.
- 945. By Manuel Moos
-
Merging from 0.4.
Unmerged revisions
- 945. By Manuel Moos
-
Merging from 0.4.
- 944. By Manuel Moos
-
Merging from mainline.
- 943. By Manuel Moos
-
merging from mainline.
- 942. By Manuel Moos
-
Merging from mainline.
- 941. By Manuel Moos
-
Merging from trunk again.
- 940. By Manuel Moos
-
Merged another memory leak.
- 939. By Manuel Moos
-
Merging memory leak fix.
- 938. By Manuel Moos
-
Merging from mainline.
- 937. By Manuel Moos
-
Better insertions. Still crappy followup.
- 936. By Manuel Moos
-
Taught the AI to aim for the enemy cycle, not the gap, and to avoid going in narrow tunnels.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/Makefile.am' |
2 | --- src/Makefile.am 2015-01-02 07:18:54 +0000 |
3 | +++ src/Makefile.am 2019-01-04 23:26:28 +0000 |
4 | @@ -149,7 +149,10 @@ |
5 | render/rTextureRenderTarget.cpp render/rTextureRenderTarget.h render/rGLuintObject.cpp render/rGLuintObject.h |
6 | |
7 | |
8 | -libtron_a_SOURCES=tron/gAIBase.cpp tron/gAIBase.h tron/gAICharacter.cpp tron/gAICharacter.h tron/gArena.cpp tron/gArena.h\ |
9 | +libtron_a_SOURCES=tron/gAIBase.cpp tron/gAIBase.h \ |
10 | + tron/gAICharacter.cpp tron/gAICharacter.h \ |
11 | + tron/gAINavigator.cpp tron/gAINavigator.h \ |
12 | + tron/gArena.cpp tron/gArena.h\ |
13 | tron/gArmagetron.cpp tron/gCamera.cpp tron/gCamera.h tron/gCycle.cpp tron/gCycle.h tron/gCycleMovement.cpp\ |
14 | tron/gJoystick.h tron/gJoystick.cpp\ |
15 | tron/gCycleMovement.h tron/gExplosion.cpp tron/gExplosion.h tron/gGame.cpp tron/gGame.h\ |
16 | @@ -177,6 +180,7 @@ |
17 | if BUILDZONESV2 |
18 | libtron_a_SOURCES +=\ |
19 | tron/zone/zZone.cpp tron/zone/zZone.h\ |
20 | + tron/zone/zAI.cpp tron/zone/zAI.h\ |
21 | tron/zone/zTimedZone.cpp tron/zone/zTimedZone.h\ |
22 | tron/zone/zShape.cpp tron/zone/zShape.h\ |
23 | tron/zone/zMonitor.h tron/zone/zMonitor.cpp\ |
24 | |
25 | === modified file 'src/engine/eCamera.cpp' |
26 | --- src/engine/eCamera.cpp 2013-10-28 22:02:36 +0000 |
27 | +++ src/engine/eCamera.cpp 2019-01-04 23:26:28 +0000 |
28 | @@ -543,6 +543,11 @@ |
29 | |
30 | centerPos=eCoord(100,100); |
31 | centerSpeedSmooth=0; |
32 | + if( !Center() ) |
33 | + { |
34 | + SwitchCenter(1); |
35 | + } |
36 | + |
37 | if ( Center() ) |
38 | { |
39 | centerPos = Center()->PredictPosition(); |
40 | |
41 | === modified file 'src/engine/eGameObject.cpp' |
42 | --- src/engine/eGameObject.cpp 2013-10-28 22:02:36 +0000 |
43 | +++ src/engine/eGameObject.cpp 2019-01-04 23:26:28 +0000 |
44 | @@ -144,6 +144,13 @@ |
45 | tCHECK_DEST; |
46 | } |
47 | |
48 | +eCoord eGameObject::Position()const{return pos;} |
49 | +eCoord eGameObject::Direction()const{return dir;} |
50 | +eCoord eGameObject::LastDirection()const{return dir;} |
51 | +REAL eGameObject::DeathTime()const{return deathTime;} |
52 | +REAL eGameObject::Speed()const{return 0;} |
53 | + |
54 | + |
55 | // returns the type of this object (important for interaction of |
56 | // two gameObjects) |
57 | //gameobject_type gameobject::type(){return ArmageTron_GENERIC;} |
58 | |
59 | === modified file 'src/engine/eGameObject.h' |
60 | --- src/engine/eGameObject.h 2011-08-22 19:38:52 +0000 |
61 | +++ src/engine/eGameObject.h 2019-01-04 23:26:28 +0000 |
62 | @@ -134,11 +134,11 @@ |
63 | eGameObject(eGrid *grid, const eCoord &p,const eCoord &d, eFace *currentface, bool autodelete=1); |
64 | virtual ~eGameObject(); |
65 | |
66 | - virtual eCoord Position()const{return pos;} |
67 | - virtual eCoord Direction()const{return dir;} |
68 | - virtual eCoord LastDirection()const{return dir;} |
69 | - virtual REAL DeathTime()const{return deathTime;} |
70 | - virtual REAL Speed()const{return 20;} |
71 | + virtual eCoord Position()const; |
72 | + virtual eCoord Direction()const; |
73 | + virtual eCoord LastDirection()const; |
74 | + virtual REAL DeathTime()const; |
75 | + virtual REAL Speed()const; |
76 | |
77 | //! returns a guess about which other object killed this |
78 | virtual eGameObject const * Killer() const { return NULL; } |
79 | |
80 | === modified file 'src/engine/eSensor.cpp' |
81 | --- src/engine/eSensor.cpp 2008-12-29 17:15:32 +0000 |
82 | +++ src/engine/eSensor.cpp 2019-01-04 23:26:28 +0000 |
83 | @@ -34,7 +34,7 @@ |
84 | //#define DEBUGLINE |
85 | #endif |
86 | |
87 | -eSensor::eSensor(eGameObject *o,const eCoord &start,const eCoord &d) |
88 | +eSensor::eSensor(eGameObject const * o,const eCoord &start,const eCoord &d) |
89 | :eStackGameObject(o->grid, start,d,o->currentFace) |
90 | ,hit(1000),ehit(NULL),lr(0), owned(o) , inverseSpeed_(0) |
91 | { |
92 | |
93 | === modified file 'src/engine/eSensor.h' |
94 | --- src/engine/eSensor.h 2010-08-08 13:55:24 +0000 |
95 | +++ src/engine/eSensor.h 2019-01-04 23:26:28 +0000 |
96 | @@ -43,7 +43,7 @@ |
97 | int lr; // and direction it goes to (left/right) |
98 | eCoord before_hit; // a point shortly before that eWall |
99 | |
100 | - eSensor(eGameObject *o,const eCoord &start,const eCoord &d); |
101 | + eSensor(eGameObject const * o,const eCoord &start,const eCoord &d); |
102 | |
103 | virtual void PassEdge(const eWall *w,REAL time,REAL,int =1); |
104 | // virtual void PassEdge(eEdge *e,REAL time,REAL a,int recursion=1); |
105 | @@ -56,7 +56,7 @@ |
106 | inline eSensor const & GetInverseSpeed( REAL & inverseSpeed ) const; //!< Gets the inverse speed of the sensor |
107 | eGameObject const * GetOwner() const { return owned; } //!< Returns the gameobject owning this sensor |
108 | protected: |
109 | - tCHECKED_PTR(eGameObject) owned; |
110 | + tCHECKED_PTR( eGameObject const ) owned; |
111 | private: |
112 | REAL inverseSpeed_; //! the inverse speed of the sensor; walls far away will be checked for opacity a bit in the future if this is set. |
113 | }; |
114 | |
115 | === modified file 'src/tools/tError.cpp' |
116 | --- src/tools/tError.cpp 2011-08-24 21:08:38 +0000 |
117 | +++ src/tools/tError.cpp 2019-01-04 23:26:28 +0000 |
118 | @@ -51,6 +51,12 @@ |
119 | #endif |
120 | } |
121 | |
122 | +void st_NotImplemented( char const * function ) |
123 | +{ |
124 | + std::cerr << "Missing implementation of " << function << ".\n"; |
125 | + st_Breakpoint(); |
126 | +} |
127 | + |
128 | #ifndef WIN32 |
129 | |
130 | void st_PresentError( const char* caption, const char *message ) |
131 | |
132 | === modified file 'src/tools/tError.h' |
133 | --- src/tools/tError.h 2010-10-21 20:07:14 +0000 |
134 | +++ src/tools/tError.h 2019-01-04 23:26:28 +0000 |
135 | @@ -52,6 +52,9 @@ |
136 | |
137 | int st_debugValid(tLevel l,tChannel c); |
138 | |
139 | +// marks that something is left to be done here |
140 | +void st_NotImplemented( char const * function ); |
141 | +#define tTODO() st_NotImplemented( __FUNCTION__ ) |
142 | |
143 | #define tERR_DUMP(level,stream,stuff) if(st_debugValid(level,stream)) std::cout << setw(28) << __FUNCTION__ << " : " << stuff << '\n' |
144 | |
145 | @@ -66,6 +69,7 @@ |
146 | #define tASSERT_EVAL( x ) { if ( !( x ) ){ char const * mess = "Assertion " #x " failed"; tERR_ERROR_INT( mess ); } } |
147 | |
148 | #else /* DEBUG */ |
149 | +#define tTODO() |
150 | #define tERR_DUMP(level,stream,stuff) |
151 | #define tERR_FLOW() |
152 | #define tERR_FLOW_HIGH() |
153 | |
154 | === modified file 'src/tools/tMath.h' |
155 | --- src/tools/tMath.h 2013-11-23 20:50:07 +0000 |
156 | +++ src/tools/tMath.h 2019-01-04 23:26:28 +0000 |
157 | @@ -44,11 +44,13 @@ |
158 | #include <ieeefp.h> |
159 | #endif |
160 | |
161 | +//! returns wiether a float value is good and finite |
162 | inline bool good( REAL f ) |
163 | { |
164 | return isfinite( f ); |
165 | } |
166 | |
167 | +//! clamps a value between two others |
168 | static inline bool clamp(REAL &c, REAL min, REAL max){ |
169 | tASSERT(min <= max); |
170 | |
171 | @@ -73,4 +75,16 @@ |
172 | return false; |
173 | } |
174 | |
175 | +//! returns the minumum of two values |
176 | +static inline REAL tMin(REAL a, REAL b) |
177 | +{ |
178 | + return a > b ? b : a; |
179 | +} |
180 | + |
181 | +//! returns the maximum of two values |
182 | +static inline REAL tMax(REAL a, REAL b) |
183 | +{ |
184 | + return a > b ? a : b; |
185 | +} |
186 | + |
187 | #endif |
188 | |
189 | === modified file 'src/tron/gAIBase.cpp' |
190 | --- src/tron/gAIBase.cpp 2011-11-20 22:01:15 +0000 |
191 | +++ src/tron/gAIBase.cpp 2019-01-04 23:26:28 +0000 |
192 | @@ -39,13 +39,19 @@ |
193 | #include "eFloor.h" |
194 | #include "eDebugLine.h" |
195 | #include "gAICharacter.h" |
196 | +#include "gAINavigator.h" |
197 | #include "tReferenceHolder.h" |
198 | #include "tRandom.h" |
199 | #include "tRecorder.h" |
200 | +#include "zone/zZone.h" |
201 | #include <stdlib.h> |
202 | #include <cstdlib> |
203 | #include <memory> |
204 | |
205 | +#ifdef DEBUG |
206 | +#include "tCommandLine.h" |
207 | +#endif |
208 | + |
209 | #include "nProtoBuf.h" |
210 | #include "gAIBase.pb.h" |
211 | |
212 | @@ -65,12 +71,6 @@ |
213 | |
214 | static tReferenceHolder< gAIPlayer > sg_AIReferences; |
215 | |
216 | -#ifdef DEBUG |
217 | -//#define TESTSTATE AI_PATH |
218 | -//#define TESTSTATE AI_TRACE |
219 | -#endif |
220 | -//#define DEBUGLINE |
221 | - |
222 | static tCONTROLLED_PTR(gAITeam) sg_AITeam = NULL; |
223 | |
224 | gSimpleAIFactory *gSimpleAIFactory::factory_ = NULL; |
225 | @@ -173,7 +173,10 @@ |
226 | return delay; |
227 | } |
228 | |
229 | +<<<<<<< TREE |
230 | |
231 | +======= |
232 | +>>>>>>> MERGE-SOURCE |
233 | static gAICharacter* BestIQ( int iq ) |
234 | { |
235 | int i; |
236 | @@ -314,8 +317,8 @@ |
237 | |
238 | if ( best ) |
239 | { |
240 | - gAIPlayer *ai = tNEW( gAIPlayer ) (); |
241 | - ai->character = best; |
242 | + gAIPlayer *ai = tNEW( gAIPlayer ) (); |
243 | + ai->character_ = best; |
244 | ai->SetName( best->name ); |
245 | ai->SetTeam( t ); |
246 | ai->UpdateTeam(); |
247 | @@ -589,7 +592,14 @@ |
248 | return false; |
249 | } |
250 | |
251 | - |
252 | +// see if the given Cycle is trapped currently |
253 | +bool IsTrapped(const eGameObject *trapped, const gCycle *other) |
254 | +{ |
255 | + gCycle const * cycle = dynamic_cast< gCycle const * >( trapped ); |
256 | + if( cycle ) |
257 | + return IsTrapped( cycle, other ); |
258 | + return false; |
259 | +} |
260 | |
261 | |
262 | // data about a loop |
263 | @@ -603,6 +613,8 @@ |
264 | void AddCycle(const gCycle* c){closedIn[closedIn.Len()] = c;} |
265 | }; |
266 | |
267 | +#ifdef OBSOLETES |
268 | + |
269 | // hit data |
270 | class gHitData{ |
271 | public: |
272 | @@ -892,7 +904,7 @@ |
273 | |
274 | }; |
275 | |
276 | - |
277 | +#endif // OBSOLETES |
278 | |
279 | |
280 | |
281 | @@ -986,8 +998,160 @@ |
282 | } |
283 | } |
284 | |
285 | - |
286 | - |
287 | +// ******************* |
288 | +// * Tracing state * |
289 | +// ******************* |
290 | + |
291 | +class gStateTrace: public gAIPlayer::State |
292 | +{ |
293 | +public: |
294 | + gStateTrace( gAIPlayer & ai, int dir ): gAIPlayer::State( ai ), dir_( dir ){} |
295 | + |
296 | + // evaluator for tracing |
297 | + class Evaluator: public gAINavigator::PathEvaluator |
298 | + { |
299 | + public: |
300 | + Evaluator( gCycle & cycle, int dir ): cycle_( cycle ), dir_( dir ){} |
301 | + |
302 | + virtual void Evaluate( gAINavigator::Path const & path, gAINavigator::PathEvaluation & evaluation ) const |
303 | + { |
304 | + gAINavigator::WallHug const * hug = 0, * noHug = 0; |
305 | + if( dir_ > 0 ) |
306 | + { |
307 | + hug = &path.left; |
308 | + noHug = &path.right; |
309 | + } |
310 | + else |
311 | + { |
312 | + hug = &path.right; |
313 | + noHug = &path.left; |
314 | + } |
315 | + |
316 | + bool good = hug->owner && hug->owner->Team() != cycle_.Team(); |
317 | + if( good ) |
318 | + { |
319 | + if( hug->owner != noHug->owner ) |
320 | + { |
321 | + evaluation.score = 50; |
322 | + } |
323 | + else if( ( path.shortTermDirection - path.longTermDirection ).NormSquared() < .01 ) |
324 | + { |
325 | + evaluation.score = 50 + .5*Adjust( path.immediateDistance/(cycle_.Speed()*cycle_.GetTurnDelay() ) ); |
326 | + } |
327 | + } |
328 | + } |
329 | + private: |
330 | + gCycle & cycle_; |
331 | + int dir_; |
332 | + }; |
333 | + |
334 | + virtual REAL Think( REAL maxStep ) |
335 | + { |
336 | + Navigator().UpdatePaths(); |
337 | + gAINavigator::EvaluationManager manager( Navigator().GetPaths() ); |
338 | + manager.Evaluate( gAINavigator::SuicideEvaluator( *Parent().Object() ), 1 ); |
339 | + manager.Reset(); |
340 | + manager.Evaluate( Evaluator( *Parent().Object(), dir_ ), 1 ); |
341 | + manager.Evaluate( gAINavigator::SpaceEvaluator( *Parent().Object() ), .5 ); |
342 | + manager.Evaluate( gAINavigator::PlanEvaluator(), .1 ); |
343 | + |
344 | + gAINavigator::CycleControllerBasic controller; |
345 | + return manager.Finish( controller, *Parent().Object(), maxStep ); |
346 | + } |
347 | +private: |
348 | + int dir_; |
349 | +}; |
350 | + |
351 | +// ******************** |
352 | +// * tailchase helper * |
353 | +// ******************** |
354 | + |
355 | +// likes to chase its own tail |
356 | +class gTailChaseEvaluator: public gAINavigator::PathEvaluator |
357 | +{ |
358 | +public: |
359 | + gTailChaseEvaluator( gCycle const & cycle ): cycle_( cycle ) |
360 | + { |
361 | + } |
362 | + |
363 | + void Evaluate( gAINavigator::Path const & path, gAINavigator::PathEvaluation & evaluation ) const |
364 | + { |
365 | + evaluation.score = 0; |
366 | + |
367 | + // don't do anything if we're tunneling. Danger affot. |
368 | + // if( path.left.owner == path.right.owner ) |
369 | + // { |
370 | + // return; |
371 | + // } |
372 | + |
373 | + // total wall length |
374 | + REAL len = cycle_.ThisWallsLength(); |
375 | + if( len < 0 ) |
376 | + { |
377 | + return; |
378 | + } |
379 | + |
380 | + if( path.left.owner == &cycle_ ) // && path.left.lr == 1 ) |
381 | + { |
382 | + evaluation.score += 200 * path.left.hitDistance/len - 100; |
383 | + } |
384 | + if( path.right.owner == &cycle_ ) // && path.right.lr == -1 ) |
385 | + { |
386 | + evaluation.score += 200 * path.right.hitDistance/len - 100; |
387 | + } |
388 | + if ( evaluation.score < 0 ) |
389 | + { |
390 | + evaluation.score = 0; |
391 | + } |
392 | + } |
393 | +private: |
394 | + gCycle const & cycle_; //!< the owning cycle |
395 | +}; |
396 | + |
397 | + |
398 | +// ******************* |
399 | +// * Survival state * |
400 | +// ******************* |
401 | + |
402 | +class gStateSurvive: public gAIPlayer::State |
403 | +{ |
404 | +public: |
405 | + gStateSurvive( gAIPlayer & ai ): gAIPlayer::State( ai ){} |
406 | + |
407 | + virtual REAL Think( REAL maxStep ) |
408 | + { |
409 | + gCycle & cycle = *Parent().Object(); |
410 | + Navigator().UpdatePaths(); |
411 | + gAINavigator::EvaluationManager manager( Navigator().GetPaths() ); |
412 | + manager.Evaluate( gAINavigator::SuicideEvaluator( cycle ), 1 ); |
413 | + manager.Evaluate( gAINavigator::SuicideEvaluator( cycle, maxStep ), 1 ); |
414 | + manager.Evaluate( gAINavigator::TrapEvaluator( cycle ), 1 ); |
415 | + manager.Reset(); |
416 | + manager.Evaluate( gAINavigator::CowardEvaluator( cycle ), 5 ); |
417 | + manager.Evaluate( gAINavigator::SpaceEvaluator( cycle ), 1 ); |
418 | + manager.Evaluate( gAINavigator::RandomEvaluator(), .01 ); |
419 | + manager.Evaluate( gAINavigator::PlanEvaluator(), .1 ); |
420 | + |
421 | + gAINavigator::CycleControllerBasic controller; |
422 | + return manager.Finish( controller, *Parent().Object(), maxStep ); |
423 | + } |
424 | +}; |
425 | + |
426 | +void gAIPlayer::SwitchToSurvival() |
427 | +{ |
428 | + SwitchToState( tNEW( gStateSurvive )( *this ) ); |
429 | +} |
430 | + |
431 | +void gAIPlayer::SetTarget( eNetGameObject * target ) |
432 | +{ |
433 | + this->target_ = target; |
434 | + |
435 | + // let's see how well this works: |
436 | + // state = AI_CLOSECOMBAT; |
437 | + |
438 | + // start with it right now |
439 | + nextTime_ = -1; |
440 | +} |
441 | |
442 | // called whenever cylce a drives close to the wall of cylce b. |
443 | // directions: aDir tells whether the wall is to the left (-1) or right(1) |
444 | @@ -998,6 +1162,8 @@ |
445 | int aDir, int bDir, REAL bDist, int winding) |
446 | { |
447 | |
448 | + return; |
449 | + |
450 | |
451 | tASSERT(aa && bb); |
452 | gCycle * a = dynamic_cast< gCycle * >( const_cast< gCycleMovement * > ( aa ) ); |
453 | @@ -1020,35 +1186,42 @@ |
454 | if (a->Team() != b->Team()) |
455 | { |
456 | gAIPlayer* ai = dynamic_cast<gAIPlayer*>(b->Player()); |
457 | - if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 5) |
458 | - if(aDir != bDir && ai->nextStateChange < se_GameTime() + 5 && |
459 | - ai->lastChangeAttempt < se_GameTime() - 5 ) |
460 | + if( ai && dynamic_cast< zZone const * >( ai->GetTarget() ) ) |
461 | + { |
462 | + // ai is attacking or defending a zone, don't disturb it |
463 | + return; |
464 | + } |
465 | + |
466 | + if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 5 ) |
467 | + if(aDir != bDir ) |
468 | { |
469 | REAL behind = b->GetDistance() - bDist; |
470 | if (a->Speed() > b->Speed() * 1.2f && behind < (a->Speed() - b->Speed()) * 10) |
471 | { // a is faster. Try to escape. |
472 | - ai->SetTraceSide(aDir > 0 ? 1 : -1); |
473 | - ai->SwitchToState(AI_TRACE, 10); |
474 | - ai->target = const_cast< gCycle * >( a ); |
475 | + ai->SwitchToState( tNEW(gStateTrace)( *ai, aDir > 0 ? 1 : -1 ) ); |
476 | + ai->target_ = const_cast< gCycle * >( a ); |
477 | } |
478 | else// if (a->Speed() < b->Speed() * 1.1f) |
479 | { // b is faster. Attack. |
480 | - ai->SetTraceSide(aDir > 0 ? -1 : 1); |
481 | - ai->SwitchToState(AI_TRACE, 10 + behind / ( a->Speed() + b->Speed() ) ); |
482 | - ai->target = const_cast< gCycle * >( a ); |
483 | + ai->SwitchToState( tNEW(gStateTrace)( *ai, aDir > 0 ? -1 : 1 ) ); |
484 | + ai->target_ = const_cast< gCycle * >( a ); |
485 | } |
486 | } |
487 | |
488 | // what to do if the AI player traces his opponent by accident? Trace On! |
489 | ai = dynamic_cast<gAIPlayer*>(a->Player()); |
490 | + |
491 | + if( ai && dynamic_cast< zZone const * >( ai->GetTarget() ) ) |
492 | + { |
493 | + // ai is attacking or defending a zone, don't disturb it |
494 | + return; |
495 | + } |
496 | + |
497 | if (ai && ai->Character() && ai->Character()->properties[AI_DETECTTRACE] > 0) |
498 | - if(aDir != bDir && ai->nextStateChange < se_GameTime() + 5 && |
499 | - ai->lastChangeAttempt < se_GameTime() - 5 ) |
500 | + if(aDir != bDir ) |
501 | { |
502 | - REAL behind = b->GetDistance() - bDist; |
503 | - ai->SetTraceSide(aDir > 0 ? 1 : -1); |
504 | - ai->SwitchToState(AI_TRACE, 10 + 4 * behind / a->Speed()); |
505 | - ai->target = const_cast< gCycle * >( b ); |
506 | + ai->SwitchToState( tNEW(gStateTrace)( *ai, aDir > 0 ? 1 : -1 ) ); |
507 | + ai->target_ = const_cast< gCycle * >( b ); |
508 | } |
509 | } |
510 | } |
511 | @@ -1102,35 +1275,25 @@ |
512 | //! creates a netobject form sync data |
513 | gAIPlayer::gAIPlayer( Game::AIPlayerSync const & sync, nSenderInfo const & sender ): |
514 | ePlayerNetID(sync.base(), sender ), |
515 | - character(NULL), |
516 | - // target(NULL), |
517 | - lastPath(se_GameTime()-100), |
518 | - lastTime(se_GameTime()), |
519 | - nextTime(0), |
520 | - concentration(1), |
521 | - log(NULL) |
522 | + character_(NULL), |
523 | + lastTime_(se_GameTime()), |
524 | + nextTime_(0), |
525 | + concentration_(1) |
526 | { |
527 | } |
528 | |
529 | |
530 | gAIPlayer::gAIPlayer(): |
531 | simpleAI_(NULL), |
532 | - character(NULL), |
533 | + character_(NULL), |
534 | // target(NULL), |
535 | - lastPath(se_GameTime()-100), |
536 | - lastTime(se_GameTime()), |
537 | - nextTime(0), |
538 | - concentration(1), |
539 | - log(NULL) |
540 | + lastTime_(se_GameTime()), |
541 | + nextTime_(0), |
542 | + concentration_(1) |
543 | { |
544 | - character = NULL; |
545 | ClearTarget(); |
546 | - traceSide = 1; |
547 | - freeSide = 0; |
548 | - log = NULL; |
549 | |
550 | // find a good color |
551 | - |
552 | current_ai=(current_ai+1) % MAXAI_COLOR; |
553 | int take_ai=current_ai; |
554 | int try_ai=current_ai; |
555 | @@ -1260,7 +1423,7 @@ |
556 | // int index = ((int)ai->character - (int)&gAICharacter::s_Characters(0))/sizeof(gAICharacter); |
557 | // inGame(index) = true; |
558 | |
559 | - if (!worstIQ || !worstIQ->character || fabs(worstIQ->character->iq - iq) < fabs(ai->character->iq - iq) ) |
560 | + if (!worstIQ || !worstIQ->character_ || fabs(worstIQ->character_->iq - iq) < fabs(ai->character_->iq - iq) ) |
561 | worstIQ = ai; |
562 | |
563 | count++; |
564 | @@ -1286,8 +1449,8 @@ |
565 | count = pcount; |
566 | |
567 | iqperfect = true; |
568 | - if (bestIQ && worstIQ && worstIQ->character ) |
569 | - iqperfect = (fabs(bestIQ->iq - iq) > fabs(worstIQ->character->iq - iq) * .9f); |
570 | + if (bestIQ && worstIQ && worstIQ->character_ ) |
571 | + iqperfect = (fabs(bestIQ->iq - iq) > fabs(worstIQ->character_->iq - iq) * .9f); |
572 | |
573 | |
574 | // count complete. Do something! |
575 | @@ -1318,7 +1481,7 @@ |
576 | // too litte AIs. Create one. |
577 | gAIPlayer *ai = tNEW(gAIPlayer)(); |
578 | ai->SetName( bestIQ->name ); |
579 | - ai->character = bestIQ; |
580 | + ai->character_ = bestIQ; |
581 | |
582 | sg_AIReferences.Add( ai ); |
583 | |
584 | @@ -1348,8 +1511,6 @@ |
585 | |
586 | } |
587 | |
588 | - |
589 | - |
590 | // Possible state changes: |
591 | // Every state -> Survive for 20 seconds if the victim is dead or can be assumed dead soon, or if the situation gets too dangerous |
592 | |
593 | @@ -1364,6 +1525,7 @@ |
594 | // CloseCombat -> Path if the vicim gets out of view |
595 | |
596 | |
597 | +#ifdef OLD_AI_OUTDATED |
598 | void gAIPlayer::SetTraceSide(int side) |
599 | { |
600 | REAL time = se_GameTime(); |
601 | @@ -1387,8 +1549,15 @@ |
602 | // flag set if pathfinding is enabled. It's an expensive opeeration, so we just turn it |
603 | // off if the PC can't handle it. Doesn't doo much good, anyway. |
604 | static bool sg_pathEnabled = true; |
605 | +#endif // OLD_AI |
606 | |
607 | // state change: |
608 | +void gAIPlayer::SwitchToState( State * state ) |
609 | +{ |
610 | + state_ = state; |
611 | +} |
612 | + |
613 | +#ifdef OLD_AI_OUTDATED |
614 | void gAIPlayer::SwitchToState(gAI_STATE nextState, REAL minTime) |
615 | { |
616 | int thisAbility = 10 - character->properties[AI_STATE_TRACE]; |
617 | @@ -1883,7 +2052,6 @@ |
618 | data.thinkAgain *= .7; |
619 | } |
620 | |
621 | - |
622 | void gAIPlayer::ThinkCloseCombat( ThinkData & data ) |
623 | { |
624 | int lr=0; |
625 | @@ -1899,7 +2067,8 @@ |
626 | // REAL ls=left.hit; |
627 | // REAL rs=right.hit; |
628 | |
629 | - if ( bool( target ) && !IsTrapped(target, Object()) && nextStateChange < se_GameTime() ) |
630 | + zZone const * zoneTarget = dynamic_cast< zZone const * >( GetTarget() ); |
631 | + if ( bool( target ) && !zoneTarget && !IsTrapped(target, Object()) && nextStateChange < se_GameTime() ) |
632 | { |
633 | gSensor p(Object(),Object()->Position(),target->Position() - Object()->Position()); |
634 | p.detect(REAL(1)); |
635 | @@ -1907,6 +2076,7 @@ |
636 | { |
637 | SwitchToState(sg_pathEnabled ? AI_PATH : AI_SURVIVE, 5); |
638 | EmergencySurvive( data ); |
639 | + |
640 | return; |
641 | } |
642 | } |
643 | @@ -1931,7 +2101,7 @@ |
644 | REAL enemyspeed=target->Speed(); |
645 | |
646 | ed=REAL(fabs(enemypos.x)+fabs(enemypos.y)); |
647 | - ed/=enemyspeed; |
648 | + ed/=(enemyspeed+Object()->Speed()); |
649 | |
650 | // transform coordinates relative to us: |
651 | enemypos=enemypos.Turn(dir.Conj()).Turn(0,1); |
652 | @@ -1953,6 +2123,32 @@ |
653 | } |
654 | // now we can even assume the enemy is on our right side. |
655 | |
656 | + if ( zoneTarget && idler.get() ) |
657 | + { |
658 | + if ( enemypos.y < 0 || ( data.front.front.wallType != gSENSOR_SELF && data.front.front.distance < Object()->Speed() * Object()->GetTurnDelay() ) ) |
659 | + { |
660 | + gAINavigator::Wish wish( *idler ); |
661 | + wish.turn = -side; |
662 | + |
663 | + // extra strong turn wish if wall is not mine |
664 | + if( sides[1]->front.wallType != gSENSOR_SELF && sides[0]->front.wallType != gSENSOR_SELF ) |
665 | + { |
666 | + wish.turn *= 2; |
667 | + } |
668 | + |
669 | + if ( enemypos.x > -enemypos.y * 2 ) |
670 | + { |
671 | + wish.minDistance = Object()->ThisWallsLength()/2; |
672 | + } |
673 | + data.thinkAgain = idler->Activate( Object()->LastTime(), 0, 0, &wish ); |
674 | + } |
675 | + else |
676 | + { |
677 | + data.thinkAgain = idler->Activate( Object()->LastTime(), 0, 0 ); |
678 | + } |
679 | + return; |
680 | + } |
681 | + |
682 | // consider his ping and our reaction time |
683 | #define REACTION .2 |
684 | |
685 | @@ -1971,6 +2167,7 @@ |
686 | |
687 | REAL ourdist=REACTION*ourspeed;; |
688 | |
689 | + |
690 | |
691 | // now we consider the worst case: we drive straight on, |
692 | enemypos.y-=ourdist; |
693 | @@ -2015,17 +2212,6 @@ |
694 | } |
695 | } |
696 | else if (enemypos.y*ourspeed<-enemypos.x*enemyspeed){ |
697 | - /* |
698 | - // good attack position |
699 | - if (enemypos.x<rs && rs < range*.99){ |
700 | - #ifdef DEBUG |
701 | - con << "BOX!\n"; |
702 | - #endif |
703 | - turn+=10; |
704 | - lr-=side; |
705 | - } |
706 | - |
707 | - */ |
708 | |
709 | REAL canCutIfDriveOn = enemypos.x*ourspeed - fs * (enemyspeed - ourspeed); |
710 | canCutIfDriveOn -= enemypos.y*enemyspeed; |
711 | @@ -2067,6 +2253,7 @@ |
712 | if (!EmergencySurvive(data, 1, -lr)) |
713 | nextThought = 0; |
714 | |
715 | +<<<<<<< TREE |
716 | data.thinkAgain = ed/2 + nextThought; |
717 | } |
718 | |
719 | @@ -2710,25 +2897,188 @@ |
720 | |
721 | |
722 | |
723 | +======= |
724 | + data.thinkAgain = ed + nextThought; |
725 | +} |
726 | +#endif // OLD_AI |
727 | + |
728 | +// ********** |
729 | +// * States * |
730 | +// ********** |
731 | + |
732 | +//!@param player the AI player the state belongs to |
733 | +gAIPlayer::State::State( gAIPlayer & player ) |
734 | +: parent_( player ) |
735 | +{ |
736 | +} |
737 | + |
738 | +gAIPlayer::State::~State() |
739 | +{ |
740 | +} |
741 | + |
742 | +//!@return time in seconds until a new thought is needed |
743 | +REAL gAIPlayer::State::Think( REAL maxStep ) |
744 | +{ |
745 | + tASSERT(0); |
746 | + return 0; |
747 | +} |
748 | + |
749 | +//!@return the navigator to use |
750 | +gAINavigator & gAIPlayer::State::Navigator() |
751 | +{ |
752 | + tASSERT( Parent().navigator_.get() ); |
753 | + return *(Parent().navigator_.get()); |
754 | +} |
755 | + |
756 | +//!@return the navigator to use |
757 | +gAINavigator const & gAIPlayer::State::Navigator() const |
758 | +{ |
759 | + tASSERT( Parent().navigator_.get() ); |
760 | + return *( Parent().navigator_.get() ); |
761 | +} |
762 | + |
763 | +//!@return the character |
764 | +gAICharacter const & gAIPlayer::State::Character() const |
765 | +{ |
766 | + tASSERT( Parent().character_ ); |
767 | + return *Parent().character_; |
768 | +} |
769 | + |
770 | +gAIPlayer::StateGrind::StateGrind( gAIPlayer & player ) |
771 | +: State( player ) |
772 | +{ |
773 | +} |
774 | + |
775 | +gAIPlayer::StateGrind::~StateGrind(){} |
776 | + |
777 | +REAL gAIPlayer::StateGrind::Think( REAL maxStep ) |
778 | +{ |
779 | + Parent().SwitchToSurvival(); |
780 | + /* |
781 | + |
782 | + REAL range = Delay() * Object()->Speed() * .5; |
783 | + |
784 | + if( data.front.front.wallType == gSENSOR_TEAMMATE ) |
785 | + { |
786 | + if ( substate == AI_GRIND_GRIND ) |
787 | + { |
788 | + if ( data.front.front.distance > range * .1 ) |
789 | + { |
790 | + data.thinkAgain = data.front.front.distance/Object()->Speed(); |
791 | + return; |
792 | + } |
793 | + else |
794 | + { |
795 | + data.turn = data.front.front.lr; |
796 | + data.thinkAgain = Object()->GetTurnDelay(); |
797 | + return; |
798 | + } |
799 | + } |
800 | + else |
801 | + { |
802 | + data.turn = data.front.front.lr; |
803 | + data.thinkAgain = Object()->GetTurnDelay(); |
804 | + state = AI_SURVIVE; |
805 | + return; |
806 | + } |
807 | + } |
808 | + |
809 | + if( data.left.front.wallType == gSENSOR_TEAMMATE ) |
810 | + { |
811 | + if ( data.left.front.distance > range ) |
812 | + { |
813 | + substate = AI_GRIND_GRIND; |
814 | + data.turn = -1; |
815 | + data.thinkAgain = data.left.front.distance/Object()->Speed(); |
816 | + return; |
817 | + } |
818 | + else if ( state == AI_GRIND ) |
819 | + { |
820 | + substate = AI_GRIND_SPLIT_RIGHT; |
821 | + return; |
822 | + } |
823 | + } |
824 | + |
825 | + if( data.right.front.wallType == gSENSOR_TEAMMATE ) |
826 | + { |
827 | + if ( data.right.front.distance > range ) |
828 | + { |
829 | + substate = AI_GRIND_GRIND; |
830 | + data.turn = 1; |
831 | + data.thinkAgain = data.right.front.distance/Object()->Speed(); |
832 | + return; |
833 | + } |
834 | + else if ( state == AI_GRIND ) |
835 | + { |
836 | + substate = AI_GRIND_SPLIT_LEFT; |
837 | + return; |
838 | + } |
839 | + } |
840 | + |
841 | + // SPLIT! |
842 | + if( substate == AI_GRIND_SPLIT_LEFT ) |
843 | + { |
844 | + if ( data.right.front.wallType != gSENSOR_TEAMMATE ) |
845 | + { |
846 | + data.thinkAgain = data.left.front.distance/Object()->Speed(); |
847 | + data.turn = -1; |
848 | + } |
849 | + else |
850 | + { |
851 | + data.thinkAgain = Delay() * 2; |
852 | + return; |
853 | + } |
854 | + } |
855 | + |
856 | + if( substate == AI_GRIND_SPLIT_RIGHT ) |
857 | + { |
858 | + if( data.left.front.wallType != gSENSOR_TEAMMATE ) |
859 | + { |
860 | + data.thinkAgain = data.right.front.distance/Object()->Speed(); |
861 | + data.turn = 1; |
862 | + } |
863 | + else |
864 | + { |
865 | + data.thinkAgain = Delay() * 2; |
866 | + return; |
867 | + } |
868 | + } |
869 | + |
870 | + SwitchToState( AI_SURVIVE ); |
871 | + data.thinkAgain = .5; |
872 | + |
873 | + */ |
874 | + |
875 | + return 0; |
876 | +} |
877 | +>>>>>>> MERGE-SOURCE |
878 | |
879 | void gAIPlayer::RightBeforeDeath(int triesLeft) // is called right before the vehicle gets destroyed. |
880 | { |
881 | + // sadly, we have no energy for this |
882 | + if( triesLeft > concentration_ ) |
883 | + { |
884 | + return; |
885 | + } |
886 | + |
887 | if ( nCLIENT == sn_GetNetState() ) |
888 | return; |
889 | |
890 | if ( simpleAI_ ) |
891 | return; |
892 | |
893 | - if (!character) |
894 | + if ( !character_ ) |
895 | { |
896 | st_Breakpoint(); |
897 | return; |
898 | } |
899 | |
900 | + CreateNavigator(); |
901 | + |
902 | gRandomController random( randomizer_ ); |
903 | |
904 | // think again immediately after this |
905 | - nextTime = lastTime; |
906 | + nextTime_ = lastTime_; |
907 | |
908 | #ifdef DEBUG_X |
909 | if (log && !Object()->Alive()) |
910 | @@ -2740,9 +3090,10 @@ |
911 | } |
912 | #endif |
913 | |
914 | - if (!Object()->Alive() || ( character && Random() * 10 > character->properties[AI_EMERGENCY] ) ) |
915 | + if (!Object()->Alive() || ( character_ && Random() * 10 > character_->properties[AI_EMERGENCY] ) ) |
916 | return; |
917 | |
918 | +<<<<<<< TREE |
919 | |
920 | // get the delay between two turns |
921 | REAL delay = Delay(); |
922 | @@ -2799,38 +3150,66 @@ |
923 | gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1); |
924 | } |
925 | #endif |
926 | +======= |
927 | + if( triesLeft <= 0 ) |
928 | + { |
929 | + gAINavigator::SuicideEvaluator::SetEmergency( true ); |
930 | + } |
931 | + |
932 | + Think( 0 ); |
933 | + |
934 | + gAINavigator::SuicideEvaluator::SetEmergency( false ); |
935 | +>>>>>>> MERGE-SOURCE |
936 | } |
937 | |
938 | gAIPlayer * sg_watchAI = 0; |
939 | |
940 | void gAIPlayer::NewObject() // called when we control a new object |
941 | { |
942 | +<<<<<<< TREE |
943 | lastTime = se_GameTime(); |
944 | lastPath = 0; |
945 | lastChangeAttempt = 0; |
946 | lazySideChange = 0; |
947 | path.Clear(); |
948 | +======= |
949 | + lastTime_ = 0; |
950 | + |
951 | + navigator_.reset(); |
952 | +>>>>>>> MERGE-SOURCE |
953 | |
954 | - if (character) |
955 | + if ( character_ ) |
956 | { |
957 | +<<<<<<< TREE |
958 | nextTime = lastTime + character->properties[AI_STARTSTRAIGHT] * gArena::SizeMultiplier()/gCycleMovement::SpeedMultiplier(); |
959 | nextStateChange = lastTime + character->properties[AI_STATECHANGE]; |
960 | state = (gAI_STATE)character->properties[AI_STARTSTATE]; |
961 | +======= |
962 | + nextTime_ = character_->properties[AI_STARTSTRAIGHT] * gArena::SizeMultiplier()/gCycleMovement::SpeedMultiplier(); |
963 | + switch ( character_->properties[AI_STARTSTATE] ) |
964 | + { |
965 | + default: |
966 | + state_ = tNEW(gStateSurvive)(*this); |
967 | + break; |
968 | + } |
969 | +>>>>>>> MERGE-SOURCE |
970 | } |
971 | else |
972 | { |
973 | - nextTime = 10; |
974 | - nextStateChange = 30; |
975 | - state = AI_TRACE; |
976 | + nextTime_ = 10; |
977 | + state_ = tNEW(gStateSurvive)(*this); |
978 | } |
979 | |
980 | - if (log) |
981 | - delete log; |
982 | - log = NULL; |
983 | + if( TeamListID() > 0 ) |
984 | + { |
985 | + state_ = tNEW(StateGrind)(*this); |
986 | + nextTime_ = Delay() * 2 + .1; |
987 | + } |
988 | |
989 | ClearTarget(); |
990 | } |
991 | |
992 | +/* |
993 | static gAISensor * sg_GetSensor( int currentDirectionNumber, gCycle const & object, int turn, REAL side, REAL range, REAL corridor, REAL & mindist ) |
994 | { |
995 | // determine the current direction |
996 | @@ -2875,8 +3254,44 @@ |
997 | |
998 | return ret; |
999 | } |
1000 | - |
1001 | -REAL gAIPlayer::Think(){ |
1002 | +*/ |
1003 | + |
1004 | +void gAIPlayer::CreateNavigator() |
1005 | +{ |
1006 | + gCycle * cycle = Object(); |
1007 | + if( cycle && !navigator_.get() ) |
1008 | + { |
1009 | + navigator_ = std::auto_ptr< gAINavigator >( tNEW( gAINavigator( cycle ) ) ); |
1010 | + } |
1011 | +} |
1012 | + |
1013 | +#ifdef DEBUG |
1014 | +static int sg_breakOnThought = 0; |
1015 | +class tCommandLineBreakOnThought: public tCommandLineAnalyzer |
1016 | +{ |
1017 | + virtual bool DoAnalyze( tCommandLineParser & parser ) //! Analyzes the command line option |
1018 | + { |
1019 | + tString value; |
1020 | + if( parser.GetOption( value, "--breakonthought" ) ) |
1021 | + { |
1022 | + sg_breakOnThought = value.ToInt(); |
1023 | + |
1024 | + return true; |
1025 | + } |
1026 | + return false; |
1027 | + } |
1028 | + |
1029 | + virtual void DoHelp( std::ostream & s ) //! Prints option help |
1030 | + { |
1031 | + s << "--breakonthought <thought> : sets a breakpoint on the given thought of the AI (the static variable 'count' in gAIBase::Think())\n\n"; |
1032 | + } |
1033 | +}; |
1034 | +static tCommandLineBreakOnThought sg_breakOnThoughtCommandLine; |
1035 | +#endif |
1036 | + |
1037 | +REAL gAIPlayer::Think( REAL maxStep ){ |
1038 | + concentration_ -= 1; |
1039 | + |
1040 | if ( !simpleAI_ ) |
1041 | { |
1042 | gSimpleAIFactory * factory = gSimpleAIFactory::Get(); |
1043 | @@ -2886,27 +3301,23 @@ |
1044 | } |
1045 | } |
1046 | |
1047 | + CreateNavigator(); |
1048 | + |
1049 | if ( simpleAI_ ) |
1050 | { |
1051 | - return simpleAI_->Think(); |
1052 | + return simpleAI_->Think( maxStep ); |
1053 | } |
1054 | |
1055 | - // get the delay between two turns |
1056 | - REAL delay = Delay(); |
1057 | - |
1058 | -#ifdef DEBUG_X |
1059 | - if (log && !Object()->Alive()) |
1060 | + if( GetTarget() && ( !GetTarget()->Alive() || IsTrapped( GetTarget(), Object() ) ) ) |
1061 | { |
1062 | - log->Print(); |
1063 | - st_Breakpoint(); |
1064 | - delete log; |
1065 | - log = NULL; |
1066 | + // no longer makes sense to chase |
1067 | + ClearTarget(); |
1068 | } |
1069 | -#endif |
1070 | |
1071 | if (!Object()->Alive()) |
1072 | return 100; |
1073 | |
1074 | +<<<<<<< TREE |
1075 | emergency = false; |
1076 | // return 1; |
1077 | |
1078 | @@ -2960,23 +3371,22 @@ |
1079 | SwitchToState(AI_SURVIVE, 1); |
1080 | #endif |
1081 | |
1082 | +======= |
1083 | +#ifdef DEBUG |
1084 | +>>>>>>> MERGE-SOURCE |
1085 | { |
1086 | eDebugLine::SetTimeout(.5); |
1087 | eDebugLine::SetColor (0, 1, 0); |
1088 | eCoord p = Object()->Position(); |
1089 | eDebugLine::Draw(p, .5, p, 5.5); |
1090 | eDebugLine::SetTimeout(0); |
1091 | - } |
1092 | - |
1093 | - triesLeft = 10; |
1094 | - |
1095 | - REAL ret = 1; |
1096 | - |
1097 | - //not the best solution, but still better than segfault... |
1098 | - if(left.get() != 0 && right.get() != 0) { |
1099 | - ThinkData data( front, *left, *right); |
1100 | - switch (state) |
1101 | + |
1102 | + // to debug specific situations on playback |
1103 | + static int count = 0; |
1104 | + count++; |
1105 | + if( count == sg_breakOnThought ) |
1106 | { |
1107 | +<<<<<<< TREE |
1108 | case AI_SURVIVE: |
1109 | ThinkSurvive(data); |
1110 | break; |
1111 | @@ -2993,19 +3403,14 @@ |
1112 | case AI_CLOSECOMBAT: |
1113 | ThinkCloseCombat(data); |
1114 | break; |
1115 | +======= |
1116 | + st_Breakpoint(); |
1117 | +>>>>>>> MERGE-SOURCE |
1118 | } |
1119 | - ActOnData( data ); |
1120 | - ret = data.thinkAgain; |
1121 | - } |
1122 | - |
1123 | -#ifdef DEBUG_X |
1124 | - { |
1125 | - gAISensor front(Object(),Object()->Position(),dir, side, range, range*.3, 0); |
1126 | - gAISensor left(Object(),Object()->Position(),dir.Turn(eCoord(0,1)), side, range, range*.3, -1); |
1127 | - gAISensor right(Object(),Object()->Position(),dir.Turn(eCoord(0,-1)), side, range, range*.3, 1); |
1128 | } |
1129 | #endif |
1130 | |
1131 | +<<<<<<< TREE |
1132 | REAL mindist = front.distance * front.distance * 8; |
1133 | |
1134 | const tList<eGameObject>& gameObjects = Object()->Grid()->GameObjects(); |
1135 | @@ -3108,41 +3513,57 @@ |
1136 | } |
1137 | |
1138 | const REAL relax=25; |
1139 | +======= |
1140 | + if( state_ ) |
1141 | + { |
1142 | + tJUST_CONTROLLED_PTR< State > keepalive( state_ ); |
1143 | + return state_->Think( maxStep ); |
1144 | + } |
1145 | + |
1146 | + return 10; |
1147 | +} |
1148 | +>>>>>>> MERGE-SOURCE |
1149 | |
1150 | void gAIPlayer::Timestep(REAL time){ |
1151 | - if (!character) |
1152 | + if (!character_) |
1153 | { |
1154 | st_Breakpoint(); |
1155 | return; |
1156 | } |
1157 | |
1158 | + const REAL relax=3; |
1159 | + |
1160 | // don't think if the object is not up to date |
1161 | if ( Object() && Object()->LastTime() < time - EPS ) |
1162 | return; |
1163 | |
1164 | - REAL ts=time-lastTime; |
1165 | - lastTime=time; |
1166 | - |
1167 | - if (concentration < 0) |
1168 | - concentration = 0; |
1169 | - |
1170 | - concentration += 4*(character->properties[AI_REACTION]+1) * ts/relax; |
1171 | - concentration=concentration/(1+ts/relax); |
1172 | - |
1173 | - if (bool(Object()) && Object()->Alive() && nextTime<time){ |
1174 | + REAL ts=time-lastTime_; |
1175 | + lastTime_=time; |
1176 | + |
1177 | + concentration_ += (character_->properties[AI_REACTION]+1) * ts; |
1178 | + concentration_ = concentration_/(1+ts/relax); |
1179 | + if( concentration_ < 0 ) |
1180 | + { |
1181 | + nextTime_ = time - concentration_ + .01; |
1182 | + return; |
1183 | + } |
1184 | + |
1185 | + if (bool(Object()) && Object()->Alive() && nextTime_<time){ |
1186 | gRandomController random( randomizer_ ); |
1187 | |
1188 | - REAL nextthought=Think(); |
1189 | + REAL target = (character_->properties[AI_REACTION]+1); |
1190 | + REAL safethought = 4 / (target + 3 * concentration_ ); |
1191 | + |
1192 | + REAL nextthought=Think( safethought ); |
1193 | // if (nextthought>.9) nextthought=REAL(.9); |
1194 | |
1195 | - if (nextthought<REAL(.6-concentration)) nextthought=REAL(.6-concentration); |
1196 | - |
1197 | - nextTime=nextTime+nextthought; |
1198 | - |
1199 | - //con << concentration << "\t" << nextthought << '\t' << ts << '\n'; |
1200 | - |
1201 | - if(.1+4*nextthought<1) |
1202 | - concentration*=REAL(.1+4*nextthought); |
1203 | + if( nextthought < -concentration_ ) |
1204 | + { |
1205 | + nextthought = -concentration_; |
1206 | + } |
1207 | + nextTime_=nextTime_+nextthought; |
1208 | + |
1209 | + //con << concentration_ << "\t" << nextthought << '\t' << ts << '\n'; |
1210 | } |
1211 | } |
1212 | |
1213 | @@ -3154,12 +3575,9 @@ |
1214 | |
1215 | gAIPlayer::~gAIPlayer() |
1216 | { |
1217 | - target=NULL; |
1218 | + target_=NULL; |
1219 | ClearObject(); |
1220 | tCHECK_DEST; |
1221 | - |
1222 | - delete log; |
1223 | - log = NULL; |
1224 | } |
1225 | |
1226 | void gAIPlayer::ClearAll() |
1227 | |
1228 | === modified file 'src/tron/gAIBase.h' |
1229 | --- src/tron/gAIBase.h 2011-11-23 15:32:48 +0000 |
1230 | +++ src/tron/gAIBase.h 2019-01-04 23:26:28 +0000 |
1231 | @@ -18,7 +18,7 @@ |
1232 | GNU General Public License for more details. |
1233 | |
1234 | You should have received a copy of the GNU General Public License |
1235 | -along with this program; if not, write to the Free Software |
1236 | +5along with this program; if not, write to the Free Software |
1237 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
1238 | |
1239 | *************************************************************************** |
1240 | @@ -46,6 +46,7 @@ |
1241 | |
1242 | namespace Game{ class AIPlayerSync; class AITeamSync; } |
1243 | |
1244 | +<<<<<<< TREE |
1245 | typedef enum |
1246 | { AI_SURVIVE = 0, // just try to stay alive |
1247 | AI_TRACE, // trace a wall |
1248 | @@ -56,6 +57,8 @@ |
1249 | } |
1250 | gAI_STATE; |
1251 | |
1252 | +======= |
1253 | +>>>>>>> MERGE-SOURCE |
1254 | class gSimpleAI |
1255 | { |
1256 | public: |
1257 | @@ -64,9 +67,9 @@ |
1258 | } |
1259 | |
1260 | // do the thinking |
1261 | - inline REAL Think() |
1262 | + inline REAL Think( REAL maxStep ) |
1263 | { |
1264 | - return DoThink(); |
1265 | + return DoThink( maxStep ); |
1266 | } |
1267 | |
1268 | virtual ~gSimpleAI(); |
1269 | @@ -74,7 +77,7 @@ |
1270 | gCycle * Object(){ return object_; } |
1271 | void SetObject( gCycle * cycle ){ object_ = cycle; } |
1272 | protected: |
1273 | - virtual REAL DoThink() = 0; |
1274 | + virtual REAL DoThink( REAL maxStep ) = 0; |
1275 | private: |
1276 | tJUST_CONTROLLED_PTR< gCycle > object_; |
1277 | }; |
1278 | @@ -93,6 +96,7 @@ |
1279 | |
1280 | class gAIPlayer: public ePlayerNetID{ |
1281 | friend class gAITeam; |
1282 | +<<<<<<< TREE |
1283 | |
1284 | tReproducibleRandomizer randomizer_; |
1285 | protected: |
1286 | @@ -182,6 +186,18 @@ |
1287 | virtual void ActOnData( ThinkDataBase & data ); |
1288 | public: |
1289 | gAICharacter* Character() const {return character;} |
1290 | +======= |
1291 | +public: |
1292 | + //! set sight on target (side effects: switch state accordingly) |
1293 | + void SetTarget( eNetGameObject * target ); |
1294 | + |
1295 | + eNetGameObject const * GetTarget() const |
1296 | + { |
1297 | + return target_; |
1298 | + } |
1299 | + |
1300 | + gAICharacter* Character() const {return character_;} |
1301 | +>>>>>>> MERGE-SOURCE |
1302 | |
1303 | // virtual void AddRef(); |
1304 | // virtual void Release(); |
1305 | @@ -212,13 +228,13 @@ |
1306 | |
1307 | static void SetNumberOfAIs(int num, int minPlayers, int iq, int tries=3); // make sure this many AI players are in the game (with approximately the given IQ) |
1308 | |
1309 | - void ClearTarget(){target=NULL;} |
1310 | + void ClearTarget(){target_=NULL;} |
1311 | |
1312 | virtual void ControlObject(eNetGameObject *c){ ePlayerNetID::ControlObject( c ); simpleAI_ = NULL; } |
1313 | virtual void ClearObject(){ ePlayerNetID::ClearObject(); simpleAI_ = NULL; } |
1314 | |
1315 | // do some thinking. Return value: time to think again |
1316 | - virtual REAL Think(); |
1317 | + virtual REAL Think( REAL maxStep ); |
1318 | |
1319 | bool Alive(){ |
1320 | return bool(Object()) && Object()->Alive(); |
1321 | @@ -255,6 +271,76 @@ |
1322 | // void WriteSync( Game::AIPlayerSync & sync, bool init ); |
1323 | //! returns the descriptor responsible for this class |
1324 | virtual nNetObjectDescriptorBase const & DoGetDescriptor() const; |
1325 | + |
1326 | + // an AI state |
1327 | + class State: public tReferencable< State > |
1328 | + { |
1329 | + public: |
1330 | + State( gAIPlayer & player ); |
1331 | + virtual ~State(); |
1332 | + |
1333 | + //! executes the state's work |
1334 | + virtual REAL Think( REAL maxStep ) = 0; |
1335 | + protected: |
1336 | + gAIPlayer & Parent(){ return parent_; } |
1337 | + gAIPlayer const & Parent() const { return parent_; } |
1338 | + |
1339 | + gAINavigator & Navigator(); |
1340 | + gAINavigator const & Navigator() const; |
1341 | + |
1342 | + gAICharacter const & Character() const; |
1343 | + private: |
1344 | + gAIPlayer & parent_; |
1345 | + }; |
1346 | + |
1347 | + friend class State; |
1348 | + |
1349 | + // state management |
1350 | + void SwitchToState( State * newState ); |
1351 | + State * GetState() const{ return state_; } |
1352 | + |
1353 | + // switch to survival state |
1354 | + void SwitchToSurvival(); |
1355 | + |
1356 | + // state for grinding at start |
1357 | + class StateGrind: public State |
1358 | + { |
1359 | + public: |
1360 | + enum Substate |
1361 | + { |
1362 | + AI_NONE = 0, |
1363 | + AI_GRIND_GRIND, // currently getting closer to teammate's wall |
1364 | + AI_GRIND_SPLIT_LEFT, AI_GRIND_SPLIT_RIGHT, // split as soon as you overtake your teammate |
1365 | + AI_SUBSTATE_COUNT |
1366 | + }; |
1367 | + |
1368 | + StateGrind( gAIPlayer & player ); |
1369 | + virtual ~StateGrind(); |
1370 | + |
1371 | + //! executes the state's work |
1372 | + virtual REAL Think( REAL maxStep ); |
1373 | + }; |
1374 | +private: |
1375 | + tReproducibleRandomizer randomizer_; |
1376 | + |
1377 | + gSimpleAI * simpleAI_; |
1378 | + gAICharacter * character_; // our specification of abilities |
1379 | + |
1380 | + // for all offensive modes: |
1381 | + nObserverPtr< eNetGameObject > target_; // the current victim |
1382 | + |
1383 | + // basic thinking resource management |
1384 | + REAL lastTime_; |
1385 | + REAL nextTime_; |
1386 | + REAL concentration_; |
1387 | + |
1388 | + //! navigator |
1389 | + std::auto_ptr< gAINavigator > navigator_; |
1390 | + |
1391 | + //! state |
1392 | + tJUST_CONTROLLED_PTR< State > state_; |
1393 | + |
1394 | + void CreateNavigator(); |
1395 | }; |
1396 | |
1397 | // the AI team |
1398 | @@ -279,5 +365,4 @@ |
1399 | virtual bool IsHuman() const { return false; } // does this team consist of humans? |
1400 | }; |
1401 | |
1402 | - |
1403 | #endif |
1404 | |
1405 | === added file 'src/tron/gAINavigator.cpp' |
1406 | --- src/tron/gAINavigator.cpp 1970-01-01 00:00:00 +0000 |
1407 | +++ src/tron/gAINavigator.cpp 2019-01-04 23:26:28 +0000 |
1408 | @@ -0,0 +1,1803 @@ |
1409 | +/* |
1410 | + |
1411 | +************************************************************************* |
1412 | + |
1413 | +ArmageTron -- Just another Tron Lightcycle Game in 3D. |
1414 | +Copyright (C) 2005 by |
1415 | +and the AA DevTeam (see the file AUTHORS(.txt) in the main source directory) |
1416 | + |
1417 | +************************************************************************** |
1418 | + |
1419 | +This program is free software; you can redistribute it and/or |
1420 | +modify it under the terms of the GNU General Public License |
1421 | +as published by the Free Software Foundation; either version 2 |
1422 | +of the License, or (at your option) any later version. |
1423 | + |
1424 | +This program is distributed in the hope that it will be useful, |
1425 | +but WITHOUT ANY WARRANTY; without even the implied warranty of |
1426 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1427 | +GNU General Public License for more details. |
1428 | + |
1429 | +You should have received a copy of the GNU General Public License |
1430 | +along with this program; if not, write to the Free Software |
1431 | +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
1432 | + |
1433 | +*************************************************************************** |
1434 | + |
1435 | +*/ |
1436 | + |
1437 | +#include "gAINavigator.h" |
1438 | + |
1439 | +#include "tRandom.h" |
1440 | +#include "tSysTime.h" |
1441 | +#include "tMath.h" |
1442 | + |
1443 | +#include "eGrid.h" |
1444 | + |
1445 | +#include "gCycle.h" |
1446 | +#include "gWall.h" |
1447 | + |
1448 | +#ifndef HUGE |
1449 | +#define HUGE 1e+38 |
1450 | +#endif |
1451 | + |
1452 | +gAINavigator::Settings::Settings() |
1453 | +: newWallBlindness(-.1) |
1454 | +, range( 1 ) |
1455 | +{} |
1456 | + |
1457 | +gAINavigator::Wish::Wish( gAINavigator const & idler ) |
1458 | +: turn(0) |
1459 | +, maxDisadvantage( HUGE ) |
1460 | +{ |
1461 | + gCycle & owner = *idler.Owner(); |
1462 | + minDistance = owner.GetTurnDelay() * owner.Speed(); |
1463 | +} |
1464 | + |
1465 | +// ************************* |
1466 | +// * Sensors * |
1467 | +// ************************* |
1468 | + |
1469 | +gAINavigator::Sensor::Sensor(gAINavigator & ai,const eCoord &start,const eCoord &d) |
1470 | +: gSensor(ai.Owner(),start,d) |
1471 | +, ai_( ai ) |
1472 | +, hitOwner_( 0 ) |
1473 | +, hitTime_ ( 0 ) |
1474 | +, hitDistance_( ai.Owner()->MaxWallsLength() ) |
1475 | +, lrSuggestion_( 0 ) |
1476 | +, windingNumber_( 0 ) |
1477 | +{ |
1478 | + if ( hitDistance_ <= 0 ) |
1479 | + hitDistance_ = ai.Owner()->GetDistance(); |
1480 | +} |
1481 | + |
1482 | +/* |
1483 | +// do detection and additional stuff |
1484 | +void detect( REAL range ) |
1485 | +{ |
1486 | +gSensor::detect( range ); |
1487 | +} |
1488 | +*/ |
1489 | + |
1490 | +void gAINavigator::Sensor::PassEdge(const eWall *ww,REAL time,REAL a,int r) |
1491 | +{ |
1492 | + try{ |
1493 | + gSensor::PassEdge(ww,time,a,r); |
1494 | + } |
1495 | + catch( eSensorFinished & e ) |
1496 | + { |
1497 | + if ( DoExtraDetectionStuff() ) |
1498 | + throw; |
1499 | + } |
1500 | +} |
1501 | + |
1502 | +extern REAL sg_cycleRubberWallShrink; |
1503 | + |
1504 | +bool gAINavigator::Sensor::DoExtraDetectionStuff() |
1505 | +{ |
1506 | + // move towards the beginning of a wall |
1507 | + lrSuggestion_ = -lr; |
1508 | + |
1509 | + switch ( type ) |
1510 | + { |
1511 | + case gSENSOR_NONE: |
1512 | + case gSENSOR_RIM: |
1513 | + lrSuggestion_ = 0; |
1514 | + return true; |
1515 | + default: |
1516 | + // unless it is an enemy, follow his wall instead (uncomment for a nasty cowardy campbot) |
1517 | + // lrSuggestion *= -1; |
1518 | + case gSENSOR_SELF: |
1519 | + { |
1520 | + // determine whether we're hitting the front or back half of his wall |
1521 | + if ( !ehit ) |
1522 | + return true; |
1523 | + eWall * wall = ehit->GetWall(); |
1524 | + if ( !wall ) |
1525 | + return true; |
1526 | + gPlayerWall * playerWall = dynamic_cast< gPlayerWall * >( wall ); |
1527 | + if ( !playerWall ) |
1528 | + return true; |
1529 | + hitOwner_ = playerWall->Cycle(); |
1530 | + if ( !hitOwner_ ) |
1531 | + return true; |
1532 | + |
1533 | + // gAINavigator & enemyChatBot = Get( hitOwner_ ); |
1534 | + |
1535 | + REAL wallAlpha = playerWall->Edge()->Ratio( before_hit ); |
1536 | + // that's an unreliable source |
1537 | + if ( wallAlpha < 0 ) |
1538 | + wallAlpha = 0; |
1539 | + if ( wallAlpha > 1 ) |
1540 | + wallAlpha = 1; |
1541 | + hitDistance_ = hitOwner_->GetDistance() - playerWall->Pos( wallAlpha ); |
1542 | + hitTime_ = playerWall->Time( wallAlpha ); |
1543 | + windingNumber_ = playerWall->WindingNumber(); |
1544 | + |
1545 | + // don't see new walls |
1546 | + if ( hitTime_ > hitOwner_->LastTime() - ai_.settings_.newWallBlindness && hitOwner_ != owned ) |
1547 | + { |
1548 | + ehit = NULL; |
1549 | + hit = 1.01; |
1550 | + return false; |
1551 | + } |
1552 | + |
1553 | + // don't see vanishing walls |
1554 | + { |
1555 | + // TODO: this is a bit wrong in the face of diagonals and quantizized driving directions. |
1556 | + REAL distanceToHit = dir.Norm() * hit; |
1557 | + REAL rubberDistance = 0; |
1558 | + |
1559 | + // assume we can waste half our rubber waiting for the wall to get lost |
1560 | + { |
1561 | + REAL rubberGranted, rubberEffectiveness; |
1562 | + sg_RubberValues( ai_.owner_->Player(), ai_.owner_->Speed(), rubberGranted, rubberEffectiveness ); |
1563 | + rubberDistance = ( rubberGranted - ai_.owner_->GetRubber() ) * .5; |
1564 | + } |
1565 | + if( type == gSENSOR_SELF ) |
1566 | + { |
1567 | + rubberDistance *= sg_cycleRubberWallShrink; |
1568 | + } |
1569 | + |
1570 | + REAL timeToHit = distanceToHit/ai_.owner_->Speed(); |
1571 | + if( !playerWall->IsDangerous( wallAlpha, ai_.owner_->LastTime() + timeToHit ) ) |
1572 | + { |
1573 | + // wall will be gone until we get there. ignore. |
1574 | + ehit = false; |
1575 | + hit = 1.01; |
1576 | + return false; |
1577 | + } |
1578 | + } |
1579 | + |
1580 | + // REAL cycleDistance = hitOwner_->GetDistance(); |
1581 | + |
1582 | + // REAL wallStart = 0; |
1583 | + |
1584 | + /* |
1585 | + if ( gCycle::WallsLength() > 0 ) |
1586 | + { |
1587 | + wallStart = cyclePos - playerWall->Cycle()->ThisWallsLength(); |
1588 | + if ( wallStart < 0 ) |
1589 | + wallStart = 0; |
1590 | + } |
1591 | + */ |
1592 | + } |
1593 | + } |
1594 | + |
1595 | + return true; |
1596 | +} |
1597 | + |
1598 | +// check how far the hit wall extends straight into the given direction |
1599 | +REAL gAINavigator::Sensor::HitWallExtends( eCoord const & dir, eCoord const & origin ) |
1600 | +{ |
1601 | + if ( !ehit || !ehit->Other() ) |
1602 | + { |
1603 | + return 0; |
1604 | + } |
1605 | + |
1606 | + REAL ret = -HUGE; |
1607 | + eCoord ends[2] = { *ehit->Point(), *ehit->Other()->Point() }; |
1608 | + for ( int i = 1; i>=0; --i ) |
1609 | + { |
1610 | + REAL newRet = eCoord::F( dir, ends[i]-origin ); |
1611 | + if ( newRet > ret ) |
1612 | + ret = newRet; |
1613 | + } |
1614 | + |
1615 | + return ret; |
1616 | +} |
1617 | + |
1618 | +// ************************* |
1619 | +// * Controllers * |
1620 | +// ************************* |
1621 | + |
1622 | +//!@param cycle the cycle to execute the action |
1623 | +//!@param dir direction to turn into |
1624 | +void gAINavigator::CycleController::Turn( gCycle & cycle , int dir ) |
1625 | +{ |
1626 | +} |
1627 | + |
1628 | +//!@param cycle the cycle to execute the action |
1629 | +//!@param brake whether to brake or not |
1630 | +void gAINavigator::CycleController::Brake( gCycle & cycle, bool brake ) |
1631 | +{ |
1632 | +} |
1633 | + |
1634 | +gAINavigator::CycleController::~CycleController(){} |
1635 | + |
1636 | +void gAINavigator::CycleControllerBasic::Turn( gCycle & cycle , int dir ) |
1637 | +{ |
1638 | + cycle.Turn( dir ); |
1639 | +} |
1640 | + |
1641 | +void gAINavigator::CycleControllerBasic::Brake( gCycle & cycle, bool brake ) |
1642 | +{ |
1643 | + cycle.Act( &gCycle::s_brake, brake ? 1 : -1 ); |
1644 | +} |
1645 | + |
1646 | +gAINavigator::CycleControllerBasic::~CycleControllerBasic(){} |
1647 | + |
1648 | +void gAINavigator::CycleControllerAction::Turn( gCycle & cycle , int dir ) |
1649 | +{ |
1650 | + cycle.Act( dir > 0 ? &gCycle::se_turnRight : &gCycle::se_turnLeft, 1 ); |
1651 | +} |
1652 | + |
1653 | +void gAINavigator::CycleControllerAction::Brake( gCycle & cycle, bool brake ) |
1654 | +{ |
1655 | + cycle.Act( &gCycle::s_brake, brake ? 1 : -1 ); |
1656 | +} |
1657 | + |
1658 | +gAINavigator::CycleControllerAction::~CycleControllerAction(){} |
1659 | + |
1660 | +// ************************* |
1661 | +// * Path * |
1662 | +// ************************* |
1663 | + |
1664 | +void gAINavigator::Path::Fill( gAINavigator const & navigator, Sensor const & left, Sensor const & right, eCoord const & shortDir, eCoord const & longDir, int turn ) |
1665 | +{ |
1666 | + // check orientation |
1667 | + tASSERT( left.Direction() * right.Direction() >= 0 ); |
1668 | + |
1669 | + this->distance = navigator.Distance( left, right ); |
1670 | + this->shortTermDirection = shortDir; |
1671 | + this->longTermDirection = longDir; |
1672 | + |
1673 | + this->left.FillFrom( left ); |
1674 | + this->right.FillFrom( right ); |
1675 | + |
1676 | + // get width by checking the edges (there's a better way for sure, but for now, this suffices) |
1677 | + eHalfEdge const * leftEdge = left.ehit; |
1678 | + eHalfEdge const * rightEdge = right.ehit; |
1679 | + width = HUGE; |
1680 | + if( leftEdge && rightEdge ) |
1681 | + { |
1682 | + // get distance of the endpoints of the edges to the extended |
1683 | + // line of the other edge |
1684 | + eCoord const & leftBeg = *leftEdge->Point(); |
1685 | + eCoord leftDir = leftEdge->Vec(); |
1686 | + eCoord const & rightBeg = *rightEdge->Point(); |
1687 | + eCoord rightDir = rightEdge->Vec(); |
1688 | + eCoord offset = leftBeg - rightBeg; |
1689 | + |
1690 | + REAL widthLeftBeg = offset * rightDir; |
1691 | + REAL widthLeftEnd = widthLeftBeg + leftDir * rightDir; |
1692 | + REAL widthRightBeg = -(offset * leftDir); |
1693 | + REAL widthRightEnd = widthRightBeg + rightDir * leftDir; |
1694 | + |
1695 | + // take the minimum of those distances without sign change |
1696 | + width = this->left.distance + this->right.distance; |
1697 | + if ( widthLeftBeg * widthLeftEnd >= 0 ) |
1698 | + { |
1699 | + width = tMin( width, tMin( fabs(widthLeftBeg), fabs(widthLeftEnd) )/rightDir.Norm() ); |
1700 | + } |
1701 | + if ( widthRightBeg * widthRightEnd >= 0 ) |
1702 | + { |
1703 | + width = tMin( width, tMin( fabs(widthRightBeg), fabs(widthRightEnd) )/leftDir.Norm() ); |
1704 | + } |
1705 | + } |
1706 | + |
1707 | + this->turn = turn; |
1708 | + this->driveOn = 0; |
1709 | +} |
1710 | + |
1711 | +//!@param controller the controller to use for the execution |
1712 | +//!@param cycle the cycle to execute the action |
1713 | +//!@param maxStep maximal timestep the caller suggests |
1714 | +REAL gAINavigator::Path::Take( CycleController & controller, gCycle & cycle, REAL maxStep ) |
1715 | +{ |
1716 | + if( driveOn > 0 ) |
1717 | + { |
1718 | + REAL driveOnTime = driveOn/cycle.Speed(); |
1719 | + if( maxStep > driveOnTime ) |
1720 | + { |
1721 | + maxStep = driveOnTime; |
1722 | + } |
1723 | + // driveOn = 0; |
1724 | + return maxStep; |
1725 | + } |
1726 | + |
1727 | + if( turn != 0 ) |
1728 | + { |
1729 | + if( !cycle.CanMakeTurn( turn ) ) |
1730 | + { |
1731 | + return driveOn = cycle.GetNextTurn( turn ) - cycle.LastTime(); |
1732 | + } |
1733 | + |
1734 | + controller.Turn( cycle, turn ); |
1735 | + } |
1736 | + |
1737 | + return maxStep; |
1738 | +} |
1739 | + |
1740 | +gAINavigator::Path::~Path() |
1741 | +{ |
1742 | +} |
1743 | + |
1744 | +gAINavigator::Path::Path() |
1745 | +: distance(HUGE) |
1746 | +, immediateDistance(HUGE) |
1747 | +, width(HUGE) |
1748 | +, shortTermDirection(0,0) |
1749 | +, longTermDirection(0,0) |
1750 | +, followedSince(0) |
1751 | +, turn(0) |
1752 | +, driveOn(0) |
1753 | +{ |
1754 | +} |
1755 | + |
1756 | +// ************************* |
1757 | +// * PathGroup * |
1758 | +// ************************* |
1759 | + |
1760 | + |
1761 | +//!@return the number of paths accessible via GetPath |
1762 | +int gAINavigator::PathGroup::GetPathCount() const |
1763 | +{ |
1764 | + return PATH_COUNT; |
1765 | +} |
1766 | + |
1767 | +//!@param id the ID of the path, between 0 and GetPathCount()-1 |
1768 | +//!@return the path of given ID |
1769 | +gAINavigator::Path const & gAINavigator::PathGroup::GetPath( int id ) const |
1770 | +{ |
1771 | + tASSERT( id >= 0 && id < PATH_COUNT ); |
1772 | + return paths[ id ]; |
1773 | +} |
1774 | + |
1775 | +//!@param controller the controller to use for the execution |
1776 | +//!@param cycle the cycle to execute the action |
1777 | +//!@param id the ID of the path, between 0 and GetPathCount()-1 |
1778 | +//!@param maxStep maximal timestep the caller suggests |
1779 | +REAL gAINavigator::PathGroup::TakePath( CycleController & controller, gCycle & cycle, int id, REAL maxStep ) |
1780 | +{ |
1781 | + // save plan |
1782 | + last = GetPath( id ); |
1783 | + |
1784 | + // execute plan |
1785 | + REAL ret = last.Take( controller, cycle, maxStep ); |
1786 | + |
1787 | + // clear all paths |
1788 | + for( int i = GetPathCount()-1; i >= 0; --i ) |
1789 | + { |
1790 | + Path & path = AccessPath( i ); |
1791 | + path.left = path.right = WallHug(); |
1792 | + path.distance = path.immediateDistance = HUGE; |
1793 | + path.followedSince = 0; |
1794 | + path.driveOn = 0; |
1795 | + } |
1796 | + |
1797 | + last.followedSince++; |
1798 | + |
1799 | + // check whether execution was successful |
1800 | + if( last.driveOn > 0 ) |
1801 | + { |
1802 | + // turn was not executed. memorize that it was a good choilce |
1803 | + paths[ id ].followedSince = last.followedSince; |
1804 | + return ret; |
1805 | + } |
1806 | + |
1807 | + // transfer last use stats |
1808 | + switch( id ) |
1809 | + { |
1810 | + case PATH_UTURN_LEFT: |
1811 | + paths[ PATH_UTURN_LEFT ].followedSince = last.followedSince; |
1812 | + paths[ PATH_TURN_LEFT ].followedSince = last.followedSince; |
1813 | + break; |
1814 | + case PATH_UTURN_RIGHT: |
1815 | + paths[ PATH_UTURN_RIGHT ].followedSince = last.followedSince; |
1816 | + paths[ PATH_TURN_RIGHT ].followedSince = last.followedSince; |
1817 | + break; |
1818 | + case PATH_ZIGZAG_LEFT: |
1819 | + if ( last.driveOn ) |
1820 | + { |
1821 | + paths[ PATH_TURN_LEFT ].followedSince = last.followedSince; |
1822 | + paths[ PATH_ZIGZAG_LEFT].followedSince = last.followedSince; |
1823 | + } |
1824 | + else |
1825 | + { |
1826 | + paths[ PATH_STRAIGHT ].followedSince = last.followedSince; |
1827 | + paths[ PATH_TURN_RIGHT ].followedSince = last.followedSince; |
1828 | + } |
1829 | + break; |
1830 | + case PATH_ZIGZAG_RIGHT: |
1831 | + if ( last.driveOn ) |
1832 | + { |
1833 | + paths[ PATH_TURN_RIGHT ].followedSince = last.followedSince; |
1834 | + paths[ PATH_ZIGZAG_RIGHT].followedSince = last.followedSince; |
1835 | + } |
1836 | + else |
1837 | + { |
1838 | + paths[ PATH_STRAIGHT ].followedSince = last.followedSince; |
1839 | + paths[ PATH_TURN_LEFT ].followedSince = last.followedSince; |
1840 | + } |
1841 | + break; |
1842 | + case PATH_TURN_LEFT: |
1843 | + case PATH_STRAIGHT: |
1844 | + case PATH_TURN_RIGHT: |
1845 | + paths[ PATH_STRAIGHT ].followedSince = last.followedSince; |
1846 | + break; |
1847 | + } |
1848 | + |
1849 | + return ret; |
1850 | +} |
1851 | + |
1852 | +//!@return the path taken last time |
1853 | +gAINavigator::Path const & gAINavigator::PathGroup::GetLastPath() const |
1854 | +{ |
1855 | + return last; |
1856 | +} |
1857 | + |
1858 | +gAINavigator::PathGroup::PathGroup() |
1859 | +{ |
1860 | + // pretend we went straight |
1861 | + paths[ PATH_STRAIGHT ].followedSince = 100; |
1862 | +} |
1863 | +gAINavigator::PathGroup::~PathGroup(){} |
1864 | + |
1865 | +//!@param id the ID of the path, between 0 and GetPathCount()-1 |
1866 | +//!@return the path of given ID |
1867 | +gAINavigator::Path & gAINavigator::PathGroup::AccessPath( int id ) |
1868 | +{ |
1869 | + tASSERT( id >= 0 && id < PATH_COUNT ); |
1870 | + return paths[ id ]; |
1871 | +} |
1872 | + |
1873 | +// ************************* |
1874 | +// * Evaluation * |
1875 | +// ************************* |
1876 | + |
1877 | +gAINavigator::PathEvaluation::PathEvaluation() : veto( false ), score( 0 ), nextThought( HUGE ){} |
1878 | + |
1879 | +//!@param path the path to evaluate |
1880 | +//!@param evaluation place to store the result |
1881 | +void gAINavigator::PathEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const{} |
1882 | +gAINavigator::PathEvaluator::~PathEvaluator(){} |
1883 | + |
1884 | +//!@param path the path to evaluate |
1885 | +//!@param evaluation place to store the result |
1886 | +void gAINavigator::SuicideEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
1887 | +{ |
1888 | + REAL speed = cycle_.Speed(); |
1889 | + REAL referenceDistance = speed*timeFrame_; |
1890 | + REAL distance = path.distance; |
1891 | + if( path.immediateDistance < distance ) |
1892 | + { |
1893 | + distance = path.immediateDistance; |
1894 | + } |
1895 | + |
1896 | + // check if this is the forward path |
1897 | + if( !emergency_ && |
1898 | + eCoord::F( path.shortTermDirection, cycle_.Direction() ) > .99 ) |
1899 | + { |
1900 | + distance += cycle_.GetTurnDelay()*speed; |
1901 | + } |
1902 | + |
1903 | + REAL timeToLive = distance/referenceDistance; |
1904 | + evaluation.score = Adjust(timeToLive/100); |
1905 | + if( timeToLive < 1 ) |
1906 | + { |
1907 | + evaluation.veto = true; |
1908 | + } |
1909 | +} |
1910 | + |
1911 | +bool gAINavigator::SuicideEvaluator::emergency_ = false; |
1912 | + |
1913 | +void gAINavigator::SuicideEvaluator::SetEmergency( bool emergency ) |
1914 | +{ |
1915 | + emergency_ = emergency; |
1916 | +} |
1917 | + |
1918 | +gAINavigator::SuicideEvaluator::SuicideEvaluator( gCycle const & cycle ): cycle_( cycle ), timeFrame_( cycle.GetTurnDelay() ){} |
1919 | +gAINavigator::SuicideEvaluator::SuicideEvaluator( gCycle const & cycle, REAL timeFrame ): cycle_( cycle ), timeFrame_( timeFrame ){} |
1920 | +gAINavigator::SuicideEvaluator::~SuicideEvaluator(){} |
1921 | + |
1922 | +//!@param path the path to evaluate |
1923 | +//!@param evaluation place to store the result |
1924 | +void gAINavigator::TrapEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
1925 | +{ |
1926 | + if( space_ <= 0 ) |
1927 | + { |
1928 | + return; |
1929 | + } |
1930 | + evaluation.score = Adjust( path.distance/space_ ); |
1931 | + REAL trapLength = cycle_.GetTurnDelay() * cycle_.Speed(); |
1932 | + if( path.distance < space_ && path.left.owner == path.right.owner && path.left.owner && |
1933 | + ( path.immediateDistance < trapLength || ( path.left.distance < trapLength && path.right.distance < trapLength ) || path.left.owner == &cycle_ ) ) |
1934 | + { |
1935 | + evaluation.veto = true; |
1936 | + } |
1937 | +} |
1938 | + |
1939 | +gAINavigator::TrapEvaluator::TrapEvaluator( gCycle const & cycle ) |
1940 | +: cycle_( cycle ) |
1941 | +{ |
1942 | + space_ = .25 * cycle.ThisWallsLength(); |
1943 | +} |
1944 | + |
1945 | +gAINavigator::TrapEvaluator::TrapEvaluator( gCycle const & cycle, REAL space ) |
1946 | +: cycle_( cycle ) |
1947 | +, space_( space ){} |
1948 | +gAINavigator::TrapEvaluator::~TrapEvaluator(){} |
1949 | + |
1950 | +void gAINavigator::RandomEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
1951 | +{ |
1952 | + static tReproducibleRandomizer randomizer; |
1953 | + evaluation.score = randomizer.Get() * 100; |
1954 | +} |
1955 | + |
1956 | +gAINavigator::RandomEvaluator::RandomEvaluator(){} |
1957 | +gAINavigator::RandomEvaluator::~RandomEvaluator(){} |
1958 | + |
1959 | +gAINavigator::CowardEvaluator::CowardEvaluator( gCycle const & cycle ): cycle_( cycle ){} |
1960 | +gAINavigator::CowardEvaluator::~CowardEvaluator(){} |
1961 | + |
1962 | +void gAINavigator::CowardEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
1963 | +{ |
1964 | + evaluation.score = 100; |
1965 | + if( path.left.owner || path.right.owner ) |
1966 | + { |
1967 | + REAL turnDelay = cycle_.GetTurnDelay() * cycle_.Speed(); |
1968 | + if( path.width < turnDelay * 2 ) |
1969 | + { |
1970 | + evaluation.score = tMax(0,(path.width/turnDelay)-1); |
1971 | + } |
1972 | + { |
1973 | + if( path.left.owner && path.left.owner->Alive() && path.left.owner->Team() != cycle_.Team() && path.left.lr == 1 ) |
1974 | + { |
1975 | + evaluation.score = 0; |
1976 | + } |
1977 | + if( path.right.owner && path.right.owner->Alive() && path.right.owner->Team() != cycle_.Team() && path.right.lr == -1 ) |
1978 | + { |
1979 | + evaluation.score = 0; |
1980 | + } |
1981 | + } |
1982 | + } |
1983 | +} |
1984 | + |
1985 | +gAINavigator::TunnelEvaluator::TunnelEvaluator( gCycle const & cycle ): cycle_( cycle ){} |
1986 | +gAINavigator::TunnelEvaluator::~TunnelEvaluator(){} |
1987 | + |
1988 | +void gAINavigator::TunnelEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
1989 | +{ |
1990 | + evaluation.score = 100; |
1991 | + |
1992 | + // check for tunnel situation |
1993 | + if( path.left.owner && path.right.owner && path.left.owner != path.right.owner ) |
1994 | + { |
1995 | + // the narrower, the worse the score gets |
1996 | + REAL referenceWidth = cycle_.GetTurnDelay() * cycle_.Speed() * 2; |
1997 | + if( path.width < referenceWidth ) |
1998 | + { |
1999 | + REAL w = (path.width/referenceWidth - .25)/.75; |
2000 | + w = tMin(0,w); |
2001 | + evaluation.score = 100*w*w; |
2002 | + } |
2003 | + } |
2004 | +} |
2005 | + |
2006 | +gAINavigator::SpaceEvaluator::SpaceEvaluator( gCycle const & cycle ) |
2007 | +: referenceDistance_( cycle.MaxWallsLength() ) |
2008 | +{ |
2009 | + if( referenceDistance_ <= 0 ) |
2010 | + { |
2011 | + referenceDistance_ = cycle.GetDistance() + cycle.Speed() * 5; |
2012 | + } |
2013 | +} |
2014 | + |
2015 | +gAINavigator::SpaceEvaluator::SpaceEvaluator( REAL referenceDistance ) |
2016 | +: referenceDistance_( referenceDistance ) |
2017 | +{} |
2018 | + |
2019 | +void gAINavigator::SpaceEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
2020 | +{ |
2021 | + evaluation.score = Adjust(path.distance/referenceDistance_); |
2022 | +} |
2023 | + |
2024 | +gAINavigator::SpaceEvaluator::~SpaceEvaluator(){} |
2025 | + |
2026 | +//!@param path the path to evaluate |
2027 | +//!@param evaluation place to store the result |
2028 | +void gAINavigator::PlanEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
2029 | +{ |
2030 | + evaluation.score = Adjust( path.followedSince/3.0 ); |
2031 | +} |
2032 | + |
2033 | +gAINavigator::PlanEvaluator::~PlanEvaluator(){} |
2034 | + |
2035 | +//!@param paths the path group to evaluate |
2036 | +gAINavigator::EvaluationManager::EvaluationManager( PathGroup & paths ) |
2037 | +: paths_( paths ) |
2038 | +, bestPath_( -1 ) |
2039 | +{ |
2040 | + // evaluations_.reserve( paths.GetPathCount() ); |
2041 | + for( int i = 0; i < paths.GetPathCount(); ++i ) |
2042 | + { |
2043 | + //evaluations_.push_back( PathEvaluation() ); |
2044 | + |
2045 | + // store preliminary 'best' path |
2046 | + // if( paths.GetPath( i ).followedSince ) |
2047 | + // { |
2048 | + // bestPath_ = i; |
2049 | + // } |
2050 | + } |
2051 | +} |
2052 | + |
2053 | +//! evaluate a path. |
2054 | +void gAINavigator::RubberEvaluator::Evaluate( Path const & path, PathEvaluation & evaluation ) const |
2055 | +{ |
2056 | + // rubber that is about to get burned |
2057 | + REAL burn = rubberLeft_ - path.immediateDistance; |
2058 | + if( burn < 0 ) |
2059 | + { |
2060 | + burn = 0; |
2061 | + } |
2062 | + if( burn > maxRubber_ ) |
2063 | + { |
2064 | + burn = maxRubber_; |
2065 | + } |
2066 | + evaluation.score = ( 1 - burn/maxRubber_ ) * 100; |
2067 | +} |
2068 | + |
2069 | +gAINavigator::RubberEvaluator::RubberEvaluator( gCycle const & cycle ) |
2070 | +{ |
2071 | + Init( cycle, cycle.GetTurnDelay() ); |
2072 | +} |
2073 | + |
2074 | +gAINavigator::RubberEvaluator::RubberEvaluator( gCycle const & cycle, REAL maxTime ) |
2075 | +{ |
2076 | + Init( cycle, maxTime ); |
2077 | +} |
2078 | + |
2079 | +gAINavigator::RubberEvaluator::~RubberEvaluator() |
2080 | +{ |
2081 | +} |
2082 | + |
2083 | +void gAINavigator::RubberEvaluator::Init( gCycle const & cycle, REAL maxTime ) |
2084 | +{ |
2085 | + // compensate for the addition of rubber in the stored sensor distances |
2086 | + REAL rubberGranted, rubberEffectiveness; |
2087 | + REAL speed = cycle.Speed(); |
2088 | + sg_RubberValues( cycle.Player(), speed, rubberGranted, rubberEffectiveness ); |
2089 | + REAL rubberLeft = ( rubberGranted - cycle.GetRubber() )*rubberEffectiveness; |
2090 | + maxRubber_ = maxTime * speed; |
2091 | + |
2092 | + // account for inevitable loss |
2093 | + rubberLeft_ = rubberLeft + maxRubber_; |
2094 | + |
2095 | + if( maxRubber_ > rubberLeft ) |
2096 | + { |
2097 | + maxRubber_ = rubberLeft; |
2098 | + } |
2099 | + |
2100 | + if( maxRubber_ < EPS ) |
2101 | + { |
2102 | + maxRubber_ = EPS; |
2103 | + } |
2104 | +} |
2105 | + |
2106 | +// ************************* |
2107 | +// * FollowEvaluator * |
2108 | +// ************************* |
2109 | + |
2110 | +gAINavigator::FollowEvaluator::FollowEvaluator( gCycle const & cycle ): cycle_( cycle ), blocker_( 0 ), blockedBySelf_( false ) |
2111 | +{ |
2112 | +} |
2113 | + |
2114 | +gAINavigator::FollowEvaluator::~FollowEvaluator() |
2115 | +{ |
2116 | +} |
2117 | + |
2118 | +//!@ param direction direction to turn in |
2119 | +//!@ param targetVelocity velocity of the target |
2120 | +//!@ param targetPosition current position of the target relative to cycle |
2121 | +//!@ param data solution filled in |
2122 | +void gAINavigator::FollowEvaluator::SolveTurn( int direction, eCoord const & targetVelocity, eCoord const & targetPosition, SolveTurnData & data ) |
2123 | +{ |
2124 | + eCoord velocityDifference = cycle_.Speed() * cycle_.Direction() - targetVelocity; |
2125 | + |
2126 | + // get the velocity after the turn |
2127 | + int winding = cycle_.WindingNumber(); |
2128 | + cycle_.Grid()->Turn( winding, direction ); |
2129 | + data.turnDir = cycle_.Grid()->GetDirection( winding ); |
2130 | + eCoord turnVelocityDifference = data.turnDir * cycle_.Speed() * GetTurnSpeedFactor() - targetVelocity; |
2131 | + |
2132 | + // some little algebra to solve velocityDifference * turnTime + turnVelocityDifference * Quality == targetPosition |
2133 | + REAL determinant = turnVelocityDifference * velocityDifference; |
2134 | + if( fabs( determinant ) < EPS ) |
2135 | + { |
2136 | + // division by zero error, problem is not well defined. approximate. |
2137 | + data.turnTime = 0; |
2138 | + |
2139 | + REAL velocityDifferenceLen = velocityDifference.NormSquared(); |
2140 | + if( velocityDifferenceLen > EPS ) |
2141 | + { |
2142 | + data.turnTime = eCoord::F( velocityDifference, targetPosition )/velocityDifferenceLen; |
2143 | + } |
2144 | + } |
2145 | + else |
2146 | + { |
2147 | + // the better case |
2148 | + data.turnTime = ( turnVelocityDifference * targetPosition )/determinant; |
2149 | + } |
2150 | + |
2151 | + // but halt, clamp it, we can't rewind (we've gone too far) |
2152 | + if ( data.turnTime < 0 ) |
2153 | + { |
2154 | + data.turnTime = 0; |
2155 | + } |
2156 | + |
2157 | + // fetch an alternative turn direction (the same as before with 4 axes) |
2158 | + // so that we have a guaranteed nonnegative positive quality outcome among the two |
2159 | + winding = cycle_.WindingNumber(); |
2160 | + cycle_.Grid()->Turn( winding, -direction ); |
2161 | + eCoord antiTurnDir = -cycle_.Grid()->GetDirection( winding ); |
2162 | + |
2163 | + // and handle the rest primitively via dot product |
2164 | + data.quality = eCoord::F( antiTurnDir, targetPosition - velocityDifference * data.turnTime ); |
2165 | + data.quality /= antiTurnDir.Norm(); |
2166 | +} |
2167 | + |
2168 | +//! sensor picking up several walls between cycle and target |
2169 | +class gTargetSensor:public gSensor |
2170 | +{ |
2171 | +public: |
2172 | + int lastOwnLR; //!< the last LR value of a hit with an own wall |
2173 | + tCHECKED_PTR_CONST(eHalfEdge) lastOwnEHit; //!< the edge hit there |
2174 | + |
2175 | + gTargetSensor(eGameObject const * o,const eCoord &start,const eCoord &d) |
2176 | + :gSensor(o,start,d), lastOwnLR(0), lastOwnEHit(0) {} |
2177 | + |
2178 | + virtual void PassEdge(const eWall *w,REAL time,REAL a,int i) |
2179 | + { |
2180 | + try |
2181 | + { |
2182 | + gSensor::PassEdge( w, time, a, i ); |
2183 | + } |
2184 | + catch( eSensorFinished & e ) |
2185 | + { |
2186 | + if( type == gSENSOR_SELF ) |
2187 | + { |
2188 | + // copy the last own wall we see |
2189 | + lastOwnLR = lr; |
2190 | + lastOwnEHit = ehit; |
2191 | + ehit = 0; |
2192 | + } |
2193 | + else |
2194 | + { |
2195 | + throw; |
2196 | + } |
2197 | + } |
2198 | + } |
2199 | +}; |
2200 | + |
2201 | +void gAINavigator::FollowEvaluator::SetTarget( eCoord const & target, eCoord const & velocity ) |
2202 | +{ |
2203 | + // determine direction to center |
2204 | + toTarget_ = target - cycle_.Position(); |
2205 | + |
2206 | + blocker_ = 0; |
2207 | + blockedBySelf_ = false; |
2208 | + |
2209 | + // check whether the path is blocked |
2210 | + gTargetSensor sensor( &cycle_, cycle_.Position(), toTarget_ ); |
2211 | + sensor.detect( .99 ); |
2212 | + |
2213 | + if( sensor.type != gSENSOR_NONE ) |
2214 | + { |
2215 | + if( sensor.lastOwnEHit ) |
2216 | + { |
2217 | + gPlayerWall const * ownWall = dynamic_cast< gPlayerWall const * >( sensor.lastOwnEHit->GetWall() ); |
2218 | + if ( ownWall ) |
2219 | + { |
2220 | + blocker_ = ownWall->Cycle(); |
2221 | + blockedBySelf_ = true; |
2222 | + tASSERT( blocker_ == &cycle_ ); |
2223 | + if( ownWall->Pos(.5) > blocker_->GetDistance() - blocker_->ThisWallsLength()*.9 ) |
2224 | + { |
2225 | + int lr = toTarget_ * ownWall->Vec() > 0 ? 1 : -1; |
2226 | + |
2227 | + // if we block our own recent path, turn around |
2228 | + if( ownWall->Pos(.5) > blocker_->GetDistance() - blocker_->ThisWallsLength()*.75 ) |
2229 | + { |
2230 | + lr *= -1; |
2231 | + } |
2232 | + |
2233 | + int winding = cycle_.WindingNumber(); |
2234 | + cycle_.Grid()->Turn( winding, lr ); |
2235 | + toTarget_ = cycle_.Grid()->GetDirection( winding ); |
2236 | + return; |
2237 | + } |
2238 | + } |
2239 | + } |
2240 | + else if ( sensor.ehit ) |
2241 | + { |
2242 | + gPlayerWall const * w = dynamic_cast< gPlayerWall const * >( sensor.ehit->GetWall() ); |
2243 | + if ( w ) |
2244 | + { |
2245 | + blocker_ = w->Cycle(); |
2246 | + |
2247 | + // just follow enemy wall in the inverse direction, but keep an eye on the target |
2248 | + toTarget_.Normalize(); |
2249 | + eCoord follow = - w->Vec(); |
2250 | + follow.Normalize(); |
2251 | + |
2252 | + // hah, but if we're faster than the other guy, try to overtake him. |
2253 | + if( blocker_->Speed() < cycle_.Speed() ) |
2254 | + { |
2255 | + follow *= -1; |
2256 | + } |
2257 | + |
2258 | + toTarget_ += follow * .9; |
2259 | + toTarget_.Normalize(); |
2260 | + return; |
2261 | + } |
2262 | + } |
2263 | + } |
2264 | + |
2265 | + // determine next possible left or right directions |
2266 | + SolveTurnData left, right; |
2267 | + SolveTurn( -1, velocity, toTarget_, left ); |
2268 | + SolveTurn( 1, velocity, toTarget_, right ); |
2269 | + |
2270 | + REAL quality = 0; |
2271 | + |
2272 | + // pick the best one |
2273 | + if( left.quality > right.quality ) |
2274 | + { |
2275 | + quality = left.quality; |
2276 | + toTarget_ = left.turnDir; |
2277 | + turnTime_ = left.turnTime; |
2278 | + } |
2279 | + else |
2280 | + { |
2281 | + quality = right.quality; |
2282 | + toTarget_ = right.turnDir; |
2283 | + turnTime_ = right.turnTime; |
2284 | + } |
2285 | + |
2286 | + if( turnTime_ < 0 ) |
2287 | + { |
2288 | + turnTime_ = HUGE; |
2289 | + } |
2290 | + else |
2291 | + { |
2292 | + // bend direction into current direction |
2293 | + toTarget_.Normalize(); |
2294 | + toTarget_ += cycle_.Direction()*turnTime_*20; |
2295 | + } |
2296 | + |
2297 | + toTarget_.Normalize(); |
2298 | +} |
2299 | + |
2300 | +void gAINavigator::FollowEvaluator::Evaluate( gAINavigator::Path const & path, gAINavigator::PathEvaluation & evaluation ) const |
2301 | +{ |
2302 | + eCoord pathDir = path.shortTermDirection; |
2303 | + REAL f = eCoord::F( toTarget_, pathDir ); |
2304 | + if( f > 0 && f*f > .5 * pathDir.NormSquared() ) |
2305 | + { |
2306 | + evaluation.nextThought = turnTime_; |
2307 | + } |
2308 | + |
2309 | + evaluation.score = 50 * f + 50; |
2310 | +} |
2311 | + |
2312 | +//!@param evaluator evaluator doing the core work |
2313 | +//!@param scale weight factor for that work |
2314 | +void gAINavigator::EvaluationManager::Evaluate( PathEvaluator const & evaluator, BlendMode mode, REAL scale, REAL offset ) |
2315 | +{ |
2316 | + REAL bestScore = -HUGE; |
2317 | + REAL bestDistance = HUGE; |
2318 | + |
2319 | + for( int i = paths_.GetPathCount()-1; i >= 0 ; --i ) |
2320 | + { |
2321 | + PathEvaluation & store = evaluations_[i]; |
2322 | + if( store.veto ) |
2323 | + { |
2324 | + continue; |
2325 | + } |
2326 | + |
2327 | + PathEvaluation evaluation; |
2328 | + Path const & path = paths_.GetPath(i); |
2329 | + evaluator.Evaluate( path, evaluation ); |
2330 | + evaluation.score = evaluation.score * scale + offset; |
2331 | + |
2332 | + // update stored evaluation |
2333 | + switch ( mode ) |
2334 | + { |
2335 | + case BLEND_ADD: |
2336 | + store.score += evaluation.score; |
2337 | + break; |
2338 | + case BLEND_MULT: |
2339 | + store.score *= evaluation.score; |
2340 | + break; |
2341 | + case BLEND_MAX: |
2342 | + if( evaluation.score > store.score ) |
2343 | + { |
2344 | + store.score = evaluation.score; |
2345 | + } |
2346 | + break; |
2347 | + case BLEND_MIN: |
2348 | + if( evaluation.score < store.score ) |
2349 | + { |
2350 | + store.score = evaluation.score; |
2351 | + } |
2352 | + break; |
2353 | + } |
2354 | + |
2355 | + if ( evaluation.nextThought < store.nextThought ) |
2356 | + { |
2357 | + store.nextThought = evaluation.nextThought; |
2358 | + } |
2359 | + |
2360 | + if ( evaluation.veto ) |
2361 | + { |
2362 | + store.veto = true; |
2363 | + } |
2364 | + else if ( bestPath_ < 0 || store.score > bestScore || ( store.score == bestScore && path.distance < bestDistance ) ) |
2365 | + { |
2366 | + bestPath_ = i; |
2367 | + bestScore = store.score; |
2368 | + bestDistance = path.distance; |
2369 | + } |
2370 | + } |
2371 | + |
2372 | + if( bestPath_ < 0 ) |
2373 | + { |
2374 | +#ifdef DEBUG |
2375 | + con << "PANIC!\n"; |
2376 | +#endif |
2377 | + |
2378 | + // PANIC. No path ever was not vetoed. Oh well. Take the best anyway and be done. |
2379 | + for( int i = paths_.GetPathCount()-1; i >= 0 ; --i ) |
2380 | + { |
2381 | + PathEvaluation & store = evaluations_[i]; |
2382 | + Path const & path = paths_.GetPath(i); |
2383 | + if ( bestPath_ < 0 || store.score > bestScore || ( store.score == bestScore && path.distance < bestDistance ) ) |
2384 | + { |
2385 | + bestPath_ = i; |
2386 | + bestScore = store.score; |
2387 | + bestDistance = path.distance; |
2388 | + } |
2389 | + } |
2390 | + } |
2391 | +} |
2392 | + |
2393 | +//! reset scores, but don't forget veto |
2394 | +void gAINavigator::EvaluationManager::Reset() |
2395 | +{ |
2396 | + for( int i = paths_.GetPathCount()-1; i >= 0; --i ) |
2397 | + { |
2398 | + evaluations_[i].score = 0; |
2399 | + } |
2400 | +} |
2401 | + |
2402 | +//!@param controller controller issuing the commands |
2403 | +//!@param cycle cycle getting controlled |
2404 | +//!@param maxStep suggestion for next timestep |
2405 | +REAL gAINavigator::EvaluationManager::Finish( CycleController & controller, gCycle & cycle, REAL maxStep ){ |
2406 | + if( bestPath_ >= 0 ) |
2407 | + { |
2408 | + REAL thisMaxStep = evaluations_[ bestPath_ ].nextThought; |
2409 | + if( maxStep > thisMaxStep ) |
2410 | + { |
2411 | + maxStep = thisMaxStep; |
2412 | + } |
2413 | + return paths_.TakePath( controller, cycle, bestPath_, maxStep ); |
2414 | + } |
2415 | + else |
2416 | + { |
2417 | + return maxStep; |
2418 | + } |
2419 | +} |
2420 | + |
2421 | +gAINavigator::gAINavigator( gCycle * owner ) |
2422 | +: lastTurn_( 0 ) |
2423 | +, nextTurn_ ( 0 ) |
2424 | +, turnedRecently_ ( 0 ) |
2425 | +, owner_ ( owner ) |
2426 | +{ |
2427 | +} |
2428 | + |
2429 | +gAINavigator::WallHug::WallHug() |
2430 | +: owner ( NULL ), lastTimeSeen ( 0 ), distance( HUGE ), lr( 0 ) |
2431 | +{ |
2432 | +} |
2433 | + |
2434 | +void gAINavigator::WallHug::FillFrom( Sensor const & sensor ) |
2435 | +{ |
2436 | + owner = sensor.hitOwner_; |
2437 | + lr = sensor.lr; |
2438 | + hitDistance = sensor.hitDistance_; |
2439 | + distance = sensor.hit * sensor.Direction().Norm(); |
2440 | + if( owner ) |
2441 | + { |
2442 | + lastTimeSeen = owner->LastTime(); |
2443 | + } |
2444 | +} |
2445 | + |
2446 | +// determines the distance between two sensors; the size should give the likelyhood |
2447 | +// to survive if you pass through a gap between the two selected walls |
2448 | +REAL gAINavigator::Distance( Sensor const & a, Sensor const & b ) const |
2449 | +{ |
2450 | + // make sure a is left from b |
2451 | + if ( a.Direction() * b.Direction() < 0 ) |
2452 | + return Distance( b, a ); |
2453 | + |
2454 | + bool self = a.type == gSENSOR_SELF || b.type == gSENSOR_SELF; |
2455 | + bool rim = a.type == gSENSOR_RIM || b.type == gSENSOR_RIM; |
2456 | + |
2457 | + // some big distance to return if we don't know anything better |
2458 | + REAL bigDistance = owner_->MaxWallsLength(); |
2459 | + if ( bigDistance <= 0 ) |
2460 | + bigDistance = owner_->GetDistance(); |
2461 | + |
2462 | + REAL totallyFree = bigDistance * 8; |
2463 | + REAL halfFree = bigDistance * 6; |
2464 | + REAL rimTunnel = bigDistance * 4; |
2465 | + REAL tunnel = bigDistance * 2; |
2466 | + |
2467 | + if ( a.type == gSENSOR_NONE || b.type == gSENSOR_NONE ) |
2468 | + { |
2469 | + // empty space! Woo! |
2470 | + if ( a.type == gSENSOR_NONE && b.type == gSENSOR_NONE ) |
2471 | + { |
2472 | + // totally empty space! groovy! |
2473 | + return totallyFree; |
2474 | + } |
2475 | + else |
2476 | + { |
2477 | + return halfFree; |
2478 | + } |
2479 | + } |
2480 | + else if ( a.hitOwner_ != b.hitOwner_ ) |
2481 | + { |
2482 | + // different owners? Great, there has to be a way through! |
2483 | + if ( rim ) |
2484 | + { |
2485 | + if ( !self ) |
2486 | + { |
2487 | + // we love going between the rim and enemies |
2488 | + return rimTunnel; |
2489 | + } |
2490 | + else |
2491 | + { |
2492 | + // we don't love going between own wall and rim |
2493 | + return bigDistance * .001 + ( a.before_hit - b.before_hit).Norm(); |
2494 | + } |
2495 | + } |
2496 | + |
2497 | + if ( self ) |
2498 | + { |
2499 | + REAL trapFactor = .5; |
2500 | + if ( a.type == gSENSOR_SELF && a.lr == +1 ) |
2501 | + { |
2502 | + // we're trapping another player and us with him. bad idea. |
2503 | + return a.hitDistance_ * trapFactor; |
2504 | + } |
2505 | + if ( b.type == gSENSOR_SELF && b.lr == -1 ) |
2506 | + { |
2507 | + return b.hitDistance_ * trapFactor; |
2508 | + } |
2509 | + } |
2510 | + |
2511 | + return tunnel; |
2512 | + } |
2513 | + else if ( rim ) |
2514 | + { |
2515 | + // at least one rim wall? Take the distance between the hit positions. |
2516 | + return ( a.before_hit - b.before_hit).Norm(); |
2517 | + } |
2518 | + else if ( a.lr != b.lr ) |
2519 | + { |
2520 | + // well, the longer the wall segment between the two points, the better. |
2521 | + return fabsf( a.hitDistance_ - b.hitDistance_ ) * 2; |
2522 | + } |
2523 | + |
2524 | + // default: hit distance |
2525 | + return ( a.before_hit - b.before_hit).Norm(); |
2526 | +} |
2527 | + |
2528 | +bool gAINavigator::CanMakeTurn( uActionPlayer * action ) |
2529 | +{ |
2530 | + return owner_->CanMakeTurn( ( action == &gCycle::se_turnRight ) ? 1 : -1 ); |
2531 | +} |
2532 | + |
2533 | +//!@return group of future paths |
2534 | +gAINavigator::PathGroup & gAINavigator::GetPaths() |
2535 | +{ |
2536 | + return paths_; |
2537 | +} |
2538 | + |
2539 | +// check whether two sensors effecively hit the same wall |
2540 | +static inline bool sg_SameWall( gAINavigator::Sensor const & a, |
2541 | + gAINavigator::Sensor const & b ) |
2542 | +{ |
2543 | + if( a.ehit == b.ehit ) |
2544 | + { |
2545 | + return true; |
2546 | + } |
2547 | + if( a.hitOwner_ != b.hitOwner_ ) |
2548 | + { |
2549 | + return false; |
2550 | + } |
2551 | + if( b.hit == 0 && a.hit != 0 ) |
2552 | + { |
2553 | + return false; |
2554 | + } |
2555 | + |
2556 | + REAL ratio = a.hit/b.hit; |
2557 | + if( ratio < .99 || ratio > 1.01 ) |
2558 | + { |
2559 | + return false; |
2560 | + } |
2561 | + |
2562 | + return true; |
2563 | +} |
2564 | + |
2565 | +// generates immediate distance data from sensor |
2566 | +static void sg_AddSensor( gAINavigator::Path & path, gAINavigator::Sensor const & sensor, REAL range, REAL rubber ) |
2567 | +{ |
2568 | + REAL r = path.distance; |
2569 | + if( sensor.type != gSENSOR_NONE ) |
2570 | + { |
2571 | + // check for the direct problem that driving in that direction causes: we're about to hit a wall, |
2572 | + // and rubber is going to run out eventually. |
2573 | + r = sensor.hit * range + rubber; |
2574 | + } |
2575 | + |
2576 | + if( r < path.immediateDistance ) |
2577 | + { |
2578 | + path.immediateDistance = r; |
2579 | + } |
2580 | +} |
2581 | + |
2582 | +// interpolates gaps of sensor data |
2583 | +static void sg_FillSensorGap( gAINavigator::Sensor const & left, gAINavigator::Sensor const & right, gAINavigator::Sensor & gap ) |
2584 | +{ |
2585 | + // check orientation |
2586 | + tASSERT( left.Direction() * right.Direction() >= 0 ); |
2587 | + |
2588 | + if( gap.type == gSENSOR_NONE ) |
2589 | + { |
2590 | + // do left and right sensors fit? |
2591 | + if ( left.type != gSENSOR_NONE && |
2592 | + left.type == right.type && |
2593 | + left.lr == right.lr && |
2594 | + left.hitOwner_ == right.hitOwner_ ) |
2595 | + { |
2596 | + gap.type = left.type; |
2597 | + gap.lr = left.lr; |
2598 | + gap.hitOwner_ = left.hitOwner_; |
2599 | + gap.hitTime_ = ( left.hitTime_ + right.hitTime_ )/2; |
2600 | + gap.hitDistance_ = ( left.hitDistance_ + right.hitDistance_ )/2; |
2601 | + gap.hit = 1.0; |
2602 | + |
2603 | + return; |
2604 | + } |
2605 | + |
2606 | + // is there an enemy that may block? |
2607 | + if( left.type == gSENSOR_ENEMY && left.lr == 1 ) |
2608 | + { |
2609 | + gap.type = left.type; |
2610 | + gap.lr = left.lr; |
2611 | + gap.hitOwner_ = left.hitOwner_; |
2612 | + gap.hitTime_ = left.hitTime_; |
2613 | + gap.hitDistance_ = 0; |
2614 | + gap.hit = left.hitDistance_/gap.Direction().Norm(); |
2615 | + } |
2616 | + |
2617 | + // is there an enemy that may block? |
2618 | + if( right.type == gSENSOR_ENEMY && right.lr == -1 ) |
2619 | + { |
2620 | + gap.type = right.type; |
2621 | + gap.lr = right.lr; |
2622 | + gap.hitOwner_ = right.hitOwner_; |
2623 | + gap.hitTime_ = right.hitTime_; |
2624 | + gap.hitDistance_ = 0; |
2625 | + gap.hit = right.hitDistance_/gap.Direction().Norm(); |
2626 | + } |
2627 | + } |
2628 | +} |
2629 | + |
2630 | + |
2631 | +static inline void sg_NoNeg( REAL & r ) |
2632 | +{ |
2633 | + if( r < 0 ) |
2634 | + r = 0; |
2635 | +} |
2636 | + |
2637 | +void gAINavigator::UpdatePaths() |
2638 | +{ |
2639 | + REAL lookahead = settings_.range; // seconds to plan ahead |
2640 | + |
2641 | + // cylce data |
2642 | + REAL speed = owner_->Speed(); |
2643 | + eCoord dir = owner_->Direction(); |
2644 | + eCoord pos = owner_->Position(); |
2645 | + |
2646 | + REAL range = speed * lookahead; |
2647 | + eCoord scanDir = dir * range; |
2648 | + |
2649 | + // get extra time we get through rubber usage |
2650 | + REAL rubberGranted, rubberEffectiveness; |
2651 | + sg_RubberValues( owner_->player, speed, rubberGranted, rubberEffectiveness ); |
2652 | + REAL rubberLeft = ( rubberGranted - owner_->GetRubber() )*rubberEffectiveness; |
2653 | + // REAL rubberTime = rubberLeft/speed; |
2654 | + // REAL rubberRatio = owner_->GetRubber()/rubberGranted; |
2655 | + |
2656 | + // this distance needs to be considered close |
2657 | + REAL close = rubberLeft + speed * owner_->GetTurnDelay(); |
2658 | + |
2659 | + REAL narrowFront = 1; |
2660 | + |
2661 | + // these checks can hit our last wall and fail. Temporarily set it to NULL. |
2662 | + tJUST_CONTROLLED_PTR< gNetPlayerWall > lastWall = owner_->lastWall; |
2663 | + owner_->lastWall = NULL; |
2664 | + |
2665 | + Sensor forward ( *this, pos, scanDir ); |
2666 | + forward.detect(1); |
2667 | + |
2668 | + // cast four diagonal rays |
2669 | + Sensor forwardLeft ( *this, pos, scanDir.Turn(+1,+1 ) ); |
2670 | + Sensor left ( *this, pos, scanDir.Turn( 0,+1 ) ); |
2671 | + Sensor backwardLeft( *this, pos, scanDir.Turn(-1,+narrowFront) ); |
2672 | + forwardLeft.detect(1); |
2673 | + left.detect(1); |
2674 | + backwardLeft.detect(1); |
2675 | + Sensor forwardRight ( *this, pos, scanDir.Turn(+1,-1 ) ); |
2676 | + Sensor right ( *this, pos, scanDir.Turn( 0,-1 ) ); |
2677 | + Sensor backwardRight( *this, pos, scanDir.Turn(-1,-narrowFront) ); |
2678 | + forwardRight.detect(1); |
2679 | + right.detect(1); |
2680 | + backwardRight.detect(1); |
2681 | + |
2682 | + sg_FillSensorGap( backwardLeft, forwardLeft, left ); |
2683 | + sg_FillSensorGap( forwardLeft, forwardRight, forward ); |
2684 | + sg_FillSensorGap( forwardRight, backwardRight, right ); |
2685 | + |
2686 | + // sensor going backwards with fake entries |
2687 | + Sensor self( *this, pos, scanDir.Turn(-1, 0) ); |
2688 | + self.before_hit = pos; |
2689 | + self.windingNumber_ = owner_->windingNumber_; |
2690 | + self.type = gSENSOR_SELF; |
2691 | + self.hit = 0; |
2692 | + if ( owner_->CurrentWall() ) |
2693 | + { |
2694 | + self.ehit = owner_->CurrentWall()->Edge(); |
2695 | + } |
2696 | + self.hitDistance_ = 0; |
2697 | + self.hitOwner_ = owner_; |
2698 | + self.hitTime_ = owner_->LastTime(); |
2699 | + |
2700 | + // get directions |
2701 | + eCoord forwardDir = dir; |
2702 | + eGrid * grid = owner_->Grid(); |
2703 | + eCoord backwardDir = - forwardDir; |
2704 | + int winding = owner_->WindingNumber(); |
2705 | + int wl = winding, wr = winding; |
2706 | + grid->Turn( wl, -1 ); |
2707 | + grid->Turn( wr, 1 ); |
2708 | + eCoord leftDir = grid->GetDirection( wl ); |
2709 | + eCoord rightDir = grid->GetDirection( wr ); |
2710 | + |
2711 | + // fill paths, first the simple cases |
2712 | + self.lr = -1; |
2713 | + { |
2714 | + // U-Turn left |
2715 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_UTURN_LEFT ); |
2716 | + path.Fill( *this, self, left, leftDir, backwardDir, -1 ); |
2717 | + sg_AddSensor( path, left, range, rubberLeft ); |
2718 | + |
2719 | + // see if there would be more space to the right |
2720 | + if( forward.hit > left.hit && |
2721 | + forwardRight.hit > left.hit && |
2722 | + right.hit > left.hit && |
2723 | + left.hit * range < close ) |
2724 | + { |
2725 | + // calculate rubber usage of the two possibilities |
2726 | + REAL rubberOffset = owner_->GetTurnDelay() * owner_->Speed()/range; |
2727 | + REAL pRubberUsageA = rubberOffset - right.hit; |
2728 | + REAL pRubberUsageB = pRubberUsageA - left.hit; |
2729 | + REAL pRubberUsageC = rubberOffset - forward.hit; |
2730 | + REAL uRubberUsage = rubberOffset - left.hit; |
2731 | + sg_NoNeg( pRubberUsageA ); |
2732 | + sg_NoNeg( pRubberUsageB ); |
2733 | + sg_NoNeg( pRubberUsageC ); |
2734 | + sg_NoNeg( uRubberUsage ); |
2735 | + if( uRubberUsage > pRubberUsageA + pRubberUsageB + pRubberUsageC ) |
2736 | + { |
2737 | + // transform it into a P-Turn |
2738 | + path.turn *= -1; |
2739 | + path.immediateDistance = HUGE; |
2740 | + sg_AddSensor( path, forward, range, rubberLeft ); |
2741 | + sg_AddSensor( path, right, range, rubberLeft ); |
2742 | + sg_AddSensor( path, forwardRight, range, rubberLeft ); |
2743 | + } |
2744 | + } |
2745 | + } |
2746 | + |
2747 | + self.lr = 1; |
2748 | + // U-Turn right |
2749 | + { |
2750 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_UTURN_RIGHT ); |
2751 | + path.Fill( *this, right, self, rightDir, backwardDir, 1 ); |
2752 | + sg_AddSensor( path, right, range, rubberLeft ); |
2753 | + |
2754 | + // see if there would be more space to the left |
2755 | + if( forward.hit > right.hit && |
2756 | + forwardLeft.hit > right.hit && |
2757 | + left.hit > right.hit && |
2758 | + right.hit * range < close ) |
2759 | + { |
2760 | + // calculate rubber usage of the two possibilities |
2761 | + REAL rubberOffset = owner_->GetTurnDelay() * owner_->Speed()/range; |
2762 | + REAL pRubberUsageA = rubberOffset - left.hit; |
2763 | + REAL pRubberUsageB = pRubberUsageA - right.hit; |
2764 | + REAL pRubberUsageC = rubberOffset - forward.hit; |
2765 | + REAL uRubberUsage = rubberOffset - right.hit; |
2766 | + sg_NoNeg( pRubberUsageA ); |
2767 | + sg_NoNeg( pRubberUsageB ); |
2768 | + sg_NoNeg( pRubberUsageC ); |
2769 | + sg_NoNeg( uRubberUsage ); |
2770 | + if( uRubberUsage > pRubberUsageA + pRubberUsageB + pRubberUsageC ) |
2771 | + { |
2772 | + // transform it into a P-Turn |
2773 | + path.turn *= -1; |
2774 | + path.immediateDistance = HUGE; |
2775 | + sg_AddSensor( path, forward, range, rubberLeft ); |
2776 | + sg_AddSensor( path, left, range, rubberLeft ); |
2777 | + sg_AddSensor( path, forwardLeft, range, rubberLeft ); |
2778 | + } |
2779 | + } |
2780 | + } |
2781 | + |
2782 | + // straight ahead |
2783 | + { |
2784 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_STRAIGHT ); |
2785 | + path.Fill( *this, forwardLeft, forwardRight, forwardDir, forwardDir, 0 ); |
2786 | + sg_AddSensor( path, forward, range, rubberLeft ); |
2787 | + |
2788 | + // slighly favor going straight over anything else |
2789 | + path.distance *= 1.0001; |
2790 | + } |
2791 | + |
2792 | + // complicated cases |
2793 | + |
2794 | + // immediate turn right |
2795 | + { |
2796 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_TURN_RIGHT ); |
2797 | + path.Fill( *this, forwardRight, backwardRight, rightDir, rightDir, 1 ); |
2798 | + |
2799 | + REAL smallRightDistance = Distance( right, backwardRight ); |
2800 | + if( sg_SameWall( backwardRight, right ) || path.distance < smallRightDistance ) |
2801 | + { |
2802 | + // we did not pass a kink, so the smaller distance applies |
2803 | + path.distance = smallRightDistance; |
2804 | + } |
2805 | + |
2806 | + sg_AddSensor( path, right, range, rubberLeft ); |
2807 | + } |
2808 | + |
2809 | + // immediate turn left |
2810 | + { |
2811 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_TURN_LEFT ); |
2812 | + path.Fill( *this, backwardLeft, forwardLeft, leftDir, leftDir, -1 ); |
2813 | + |
2814 | + REAL smallLeftDistance = Distance( backwardLeft, left ); |
2815 | + if( sg_SameWall( backwardLeft, left ) || path.distance < smallLeftDistance ) |
2816 | + { |
2817 | + path.distance = smallLeftDistance; |
2818 | + } |
2819 | + |
2820 | + sg_AddSensor( path, left, range, rubberLeft ); |
2821 | + } |
2822 | + |
2823 | + // zigzag right |
2824 | + { |
2825 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_ZIGZAG_RIGHT ); |
2826 | + path.Fill( *this, forward, right, rightDir, forwardDir, 1 ); |
2827 | + |
2828 | + REAL driveOn = right.HitWallExtends( dir, pos ); |
2829 | + REAL side = forward.HitWallExtends( rightDir, pos ); |
2830 | + if( driveOn > 0 && ( driveOn < forward.hit * range * ( 1-EPS ) || forward.type == gSENSOR_NONE || side > right.hit * range * ( 1+EPS ) ) ) |
2831 | + { |
2832 | + // there is a gap waiting for us. Wait and take it. |
2833 | + path.driveOn = driveOn; |
2834 | + path.shortTermDirection = forwardDir; |
2835 | + path.longTermDirection = rightDir; |
2836 | + |
2837 | + sg_AddSensor( path, forward, range, rubberLeft ); |
2838 | + } |
2839 | + else |
2840 | + { |
2841 | + sg_AddSensor( path, right, range, rubberLeft ); |
2842 | + } |
2843 | + } |
2844 | + |
2845 | + // zigzag left |
2846 | + { |
2847 | + Path & path = GetPaths().AccessPath( PathGroup::PATH_ZIGZAG_LEFT ); |
2848 | + path.Fill( *this, left, forward, leftDir, forwardDir, -1 ); |
2849 | + |
2850 | + REAL driveOn = left.HitWallExtends( dir, pos ); |
2851 | + REAL side = forward.HitWallExtends( leftDir, pos ); |
2852 | + if( driveOn > 0 && ( driveOn < forward.hit * range * ( 1-EPS ) || forward.type == gSENSOR_NONE || side > left.hit * range * ( 1+EPS ) ) ) |
2853 | + { |
2854 | + // there is a gap waiting for us. Wait and take it. |
2855 | + path.driveOn = driveOn; |
2856 | + path.shortTermDirection = forwardDir; |
2857 | + path.longTermDirection = leftDir; |
2858 | + |
2859 | + sg_AddSensor( path, forward, range, rubberLeft ); |
2860 | + } |
2861 | + else |
2862 | + { |
2863 | + sg_AddSensor( path, left, range, rubberLeft ); |
2864 | + } |
2865 | + } |
2866 | +} |
2867 | + |
2868 | +//! does the main thinking at the current time, knowing the next thought can't be sooner than minstep |
2869 | +REAL gAINavigator::Activate( REAL currentTime, REAL minstep, REAL penalty, Wish * wish ) |
2870 | +{ |
2871 | + REAL lookahead = settings_.range; // seconds to plan ahead |
2872 | + |
2873 | + // cylce data |
2874 | + REAL speed = owner_->Speed(); |
2875 | + eCoord dir = owner_->Direction(); |
2876 | + eCoord pos = owner_->Position(); |
2877 | + |
2878 | + REAL range= speed * lookahead; |
2879 | + eCoord scanDir = dir * range; |
2880 | + |
2881 | + REAL frontFactor = .5; |
2882 | + |
2883 | + Sensor front(*this,pos,scanDir); |
2884 | + front.detect(frontFactor); |
2885 | + owner_->enemyInfluence.AddSensor( front, penalty, owner_ ); |
2886 | + |
2887 | + REAL minMoveOn = 0, maxMoveOn = 0, moveOn = 0; |
2888 | + |
2889 | + // get extra time we get through rubber usage |
2890 | + REAL rubberGranted, rubberEffectiveness; |
2891 | + sg_RubberValues( owner_->player, speed, rubberGranted, rubberEffectiveness ); |
2892 | + REAL rubberTime = ( rubberGranted - owner_->GetRubber() )*rubberEffectiveness/speed; |
2893 | + REAL rubberRatio = owner_->GetRubber()/rubberGranted; |
2894 | + |
2895 | + if ( front.ehit || wish ) |
2896 | + { |
2897 | + turnedRecently_ = false; |
2898 | + |
2899 | + // these checks can hit our last wall and fail. Temporarily set it to NULL. |
2900 | + tJUST_CONTROLLED_PTR< gNetPlayerWall > lastWall = owner_->lastWall; |
2901 | + owner_->lastWall = NULL; |
2902 | + |
2903 | + REAL narrowFront = 1; |
2904 | + |
2905 | + // cast four diagonal rays |
2906 | + Sensor forwardLeft ( *this, pos, scanDir.Turn(+1,+1 ) ); |
2907 | + Sensor backwardLeft( *this, pos, scanDir.Turn(-1,+narrowFront) ); |
2908 | + forwardLeft.detect(1); |
2909 | + backwardLeft.detect(1); |
2910 | + Sensor forwardRight ( *this, pos, scanDir.Turn(+1,-1 ) ); |
2911 | + Sensor backwardRight( *this, pos, scanDir.Turn(-1,-narrowFront) ); |
2912 | + forwardRight.detect(1); |
2913 | + backwardRight.detect(1); |
2914 | + |
2915 | + // determine survival chances in the four directions |
2916 | + REAL frontOpen = Distance ( forwardLeft, forwardRight ); |
2917 | + REAL leftOpen = Distance ( forwardLeft, backwardLeft ); |
2918 | + REAL rightOpen = Distance ( forwardRight, backwardRight ); |
2919 | + REAL rearOpen = Distance ( backwardLeft, backwardRight ); |
2920 | + |
2921 | + Sensor self( *this, pos, scanDir.Turn(-1, 0) ); |
2922 | + // fake entries |
2923 | + self.before_hit = pos; |
2924 | + self.windingNumber_ = owner_->windingNumber_; |
2925 | + self.type = gSENSOR_SELF; |
2926 | + self.hitDistance_ = 0; |
2927 | + self.hitOwner_ = owner_; |
2928 | + self.hitTime_ = currentTime; |
2929 | + self.lr = -1; |
2930 | + REAL rearLeftOpen = Distance( backwardLeft, self ); |
2931 | + self.lr = 1; |
2932 | + REAL rearRightOpen = Distance( backwardRight, self ); |
2933 | + |
2934 | + // override rim hugging |
2935 | + if ( forwardRight.type == gSENSOR_SELF && |
2936 | + forwardLeft.type == gSENSOR_RIM && |
2937 | + backwardRight.type == gSENSOR_SELF && |
2938 | + backwardLeft.type == gSENSOR_RIM && |
2939 | + forwardRight.lr == -1 && |
2940 | + backwardRight.lr == -1 ) |
2941 | + { |
2942 | + turnedRecently_ = true; |
2943 | + if ( rightOpen > speed * ( owner_->GetTurnDelay() - rubberTime * .8 ) ) |
2944 | + { |
2945 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
2946 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
2947 | + } |
2948 | + else |
2949 | + { |
2950 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
2951 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
2952 | + } |
2953 | + } |
2954 | + |
2955 | + if ( forwardLeft.type == gSENSOR_SELF && |
2956 | + forwardRight.type == gSENSOR_RIM && |
2957 | + backwardLeft.type == gSENSOR_SELF && |
2958 | + backwardRight.type == gSENSOR_RIM && |
2959 | + forwardLeft.lr == 1 && |
2960 | + backwardLeft.lr == 1 ) |
2961 | + { |
2962 | + turnedRecently_ = true; |
2963 | + if ( leftOpen > speed * ( owner_->GetTurnDelay() - rubberTime * .8 ) ) |
2964 | + { |
2965 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
2966 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
2967 | + } |
2968 | + else |
2969 | + { |
2970 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
2971 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
2972 | + } |
2973 | + } |
2974 | + |
2975 | + // get the best turn direction |
2976 | + int bestDir = ( leftOpen + rearLeftOpen > rightOpen + rearRightOpen ) ? 1 : -1; |
2977 | + |
2978 | + Sensor direct ( *this, pos, scanDir.Turn( 0, bestDir) ); |
2979 | + direct.detect( 1 ); |
2980 | + |
2981 | + // restore last wall |
2982 | + owner_->lastWall = lastWall; |
2983 | + |
2984 | + if( wish && wish->turn ) |
2985 | + { |
2986 | + REAL minDistance = wish->minDistance; |
2987 | + if( wish->turn > 0 ) |
2988 | + { |
2989 | + if( wish->maxDisadvantage + leftOpen + rearLeftOpen < frontOpen + rightOpen + rearRightOpen ) |
2990 | + { |
2991 | + wish = 0; |
2992 | + } |
2993 | + else if ( leftOpen < wish->minDistance ) |
2994 | + { |
2995 | + if( leftOpen + rearLeftOpen < wish->minDistance || frontOpen < wish->minDistance || rightOpen < wish->minDistance ) |
2996 | + { |
2997 | + wish = 0; |
2998 | + } |
2999 | + else if( wish->turn == -1 ) |
3000 | + { |
3001 | + wish = 0; |
3002 | + } |
3003 | + else |
3004 | + { |
3005 | + // double back |
3006 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
3007 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
3008 | + owner_->Act( &gCycle::se_turnRight, 1 ); |
3009 | + return owner_->GetTurnDelay() * 3; |
3010 | + } |
3011 | + } |
3012 | + else |
3013 | + { |
3014 | + bestDir = 1; |
3015 | + } |
3016 | + } |
3017 | + else if( wish->turn < 0 ) |
3018 | + { |
3019 | + if( wish->maxDisadvantage + rightOpen + rearRightOpen < frontOpen + leftOpen + rearLeftOpen ) |
3020 | + { |
3021 | + wish = 0; |
3022 | + } |
3023 | + else if ( rightOpen < wish->minDistance ) |
3024 | + { |
3025 | + if( rightOpen + rearRightOpen < wish->minDistance || frontOpen < wish->minDistance || leftOpen < wish->minDistance ) |
3026 | + { |
3027 | + wish = 0; |
3028 | + } |
3029 | + else if( wish->turn == -1 ) |
3030 | + { |
3031 | + wish = 0; |
3032 | + } |
3033 | + else |
3034 | + { |
3035 | + // double back |
3036 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
3037 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
3038 | + owner_->Act( &gCycle::se_turnLeft, 1 ); |
3039 | + return owner_->GetTurnDelay() * 3; |
3040 | + } |
3041 | + } |
3042 | + else |
3043 | + { |
3044 | + bestDir = -1; |
3045 | + } |
3046 | + } |
3047 | + |
3048 | + if( !wish && frontOpen > minDistance ) |
3049 | + { |
3050 | + // going straigt is not too bad |
3051 | + return Owner()->GetTurnDelay(); |
3052 | + } |
3053 | + |
3054 | + } |
3055 | + |
3056 | + uActionPlayer * bestAction = ( bestDir > 0 ) ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
3057 | + |
3058 | + REAL bestOpen = ( bestDir > 0 ) ? leftOpen : rightOpen; |
3059 | + Sensor & bestForward = ( bestDir > 0 ) ? forwardLeft : forwardRight; |
3060 | + Sensor & bestBackward = ( bestDir > 0 ) ? backwardLeft : backwardRight; |
3061 | + |
3062 | + // only turn if the hole has a shape that allows better entry after we do a zig-zag, or if we're past the good turning point |
3063 | + // see how the survival chance is distributed between forward and backward half |
3064 | + REAL forwardHalf = Distance ( direct, bestForward ); |
3065 | + REAL backwardHalf = Distance ( direct, bestBackward ); |
3066 | + |
3067 | + REAL forwardOverhang = bestForward.HitWallExtends( bestForward.Direction(), pos ); |
3068 | + REAL backwardOverhang = bestBackward.HitWallExtends( bestForward.Direction(), pos ); |
3069 | + |
3070 | + // we have to move forward this much before we can hope to turn |
3071 | + minMoveOn = bestBackward.HitWallExtends( dir, pos ); |
3072 | + |
3073 | + if( wish ) |
3074 | + { |
3075 | + if( backwardHalf < wish->minDistance ) |
3076 | + { |
3077 | + return (minMoveOn/owner_->Speed())*1.1; |
3078 | + } |
3079 | + |
3080 | + if ( !CanMakeTurn( bestAction ) ) |
3081 | + { |
3082 | + return owner_->GetNextTurn( -bestDir ) - owner_->LastTime(); |
3083 | + } |
3084 | + |
3085 | + owner_->Act( bestAction, 1 ); |
3086 | + return Owner()->GetTurnDelay(); |
3087 | + } |
3088 | + |
3089 | + // maybe the direct to the side sensor is better? |
3090 | + REAL minMoveOnOther = direct.HitWallExtends( dir, pos ); |
3091 | + |
3092 | + // determine how far we can drive on |
3093 | + maxMoveOn = bestForward.HitWallExtends( dir, pos ); |
3094 | + REAL maxMoveOnOther = front.HitWallExtends( dir, pos ); |
3095 | + if ( maxMoveOn > maxMoveOnOther ) |
3096 | + maxMoveOn = maxMoveOnOther; |
3097 | + |
3098 | + if ( maxMoveOn > minMoveOnOther && forwardHalf > backwardHalf && direct.hitOwner_ == bestBackward.hitOwner_ ) |
3099 | + { |
3100 | + backwardOverhang = direct.HitWallExtends( bestForward.Direction(), pos ); |
3101 | + minMoveOn = minMoveOnOther; |
3102 | + } |
3103 | + |
3104 | + // best place to turn |
3105 | + moveOn = .5 * ( minMoveOn * ( 1 + rubberRatio ) + maxMoveOn * ( 1 - rubberRatio ) ); |
3106 | + |
3107 | + // hit the brakes before you hit anything and if it's worth it |
3108 | + bool brake = sg_brakeCycle > 0 && |
3109 | + front.hit * lookahead * sg_cycleBrakeDeplete < owner_->GetBrakingReservoir() && |
3110 | + sg_brakeCycle * front.hit * lookahead < 2 * speed * owner_->GetBrakingReservoir() && |
3111 | + ( maxMoveOn - minMoveOn ) > 0 && |
3112 | + owner_->GetBrakingReservoir() * ( maxMoveOn - minMoveOn ) < speed * owner_->GetTurnDelay(); |
3113 | + if ( frontOpen < bestOpen && |
3114 | + ( forwardOverhang <= backwardOverhang || ( minMoveOn < 0 && moveOn < minstep * speed ) ) ) |
3115 | + { |
3116 | + turnedRecently_ = true; |
3117 | + |
3118 | + minMoveOn = maxMoveOn = moveOn = 0; |
3119 | + |
3120 | + { |
3121 | + if ( !CanMakeTurn( bestAction ) ) |
3122 | + { |
3123 | + return owner_->GetNextTurn( -bestDir ) - owner_->LastTime(); |
3124 | + } |
3125 | + |
3126 | + owner_->Act( bestAction, 1 ); |
3127 | + } |
3128 | + |
3129 | + brake = false; |
3130 | + } |
3131 | + else |
3132 | + { |
3133 | + // the best |
3134 | + REAL bestSoFar = frontOpen > bestOpen ? frontOpen : bestOpen; |
3135 | + bestSoFar *= ( 10 * ( 1 - rubberRatio ) + 1 ); |
3136 | + |
3137 | + if ( rearOpen > bestSoFar && ( rearLeftOpen > bestSoFar || rearRightOpen > bestSoFar ) ) |
3138 | + { |
3139 | + brake = false; |
3140 | + turnedRecently_ = true; |
3141 | + |
3142 | + bool goLeft = rearLeftOpen > rearRightOpen; |
3143 | + |
3144 | + // dead end. reverse into the opposite direction of the front wall |
3145 | + uActionPlayer * bestAction = goLeft ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
3146 | + uActionPlayer * otherAction = !goLeft ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
3147 | + Sensor & bestForward = goLeft ? forwardLeft : forwardRight; |
3148 | + Sensor & bestBackward = goLeft ? backwardLeft : backwardRight; |
3149 | + Sensor & otherForward = !goLeft ? forwardLeft : forwardRight; |
3150 | + Sensor & otherBackward = !goLeft ? backwardLeft : backwardRight; |
3151 | + |
3152 | + // space in the two directions available for turns |
3153 | + REAL bestHit = bestForward.hit > bestBackward.hit ? bestBackward.hit : bestForward.hit; |
3154 | + REAL otherHit = otherForward.hit > otherBackward.hit ? otherBackward.hit : otherForward.hit; |
3155 | + |
3156 | + bool wait = false; |
3157 | + |
3158 | + if ( !CanMakeTurn( bestAction ) ) |
3159 | + { |
3160 | + return owner_->GetNextTurn( -bestDir ) - owner_->LastTime(); |
3161 | + } |
3162 | + |
3163 | + // well, after a short turn to the right if space is tight |
3164 | + if ( bestHit * lookahead < owner_->GetTurnDelay() + rubberTime ) |
3165 | + { |
3166 | + if ( otherHit < bestForward.hit * 2 && front.hit * lookahead > owner_->GetTurnDelay() * 2 ) |
3167 | + { |
3168 | + // wait a bit, perhaps there will be a better spot |
3169 | + wait = true; |
3170 | + } |
3171 | + else |
3172 | + { |
3173 | + if ( !CanMakeTurn( otherAction ) ) |
3174 | + { |
3175 | + return owner_->GetNextTurn( bestDir ) - owner_->LastTime(); |
3176 | + } |
3177 | + |
3178 | + owner_->Act( otherAction, 1 ); |
3179 | + |
3180 | + // there needs to be space ahead to finish the maneuver correctly |
3181 | + if ( maxMoveOn < speed * owner_->GetTurnDelay() ) |
3182 | + { |
3183 | + // there isn't. oh well, turn into the wrong direction completely, see if I care |
3184 | + owner_->Act( otherAction, 1 ); |
3185 | + wait = true; |
3186 | + } |
3187 | + } |
3188 | + } |
3189 | + |
3190 | + if ( !wait ) |
3191 | + { |
3192 | + owner_->Act( bestAction, 1 ); |
3193 | + owner_->Act( bestAction, 1 ); |
3194 | + } |
3195 | + |
3196 | + minMoveOn = maxMoveOn = moveOn = 0; |
3197 | + } |
3198 | + } |
3199 | + |
3200 | + // execute brake command |
3201 | + owner_->Act( &gCycle::s_brake, brake ? 1 : -1 ); |
3202 | + } |
3203 | + |
3204 | + REAL space = moveOn; |
3205 | + REAL minTime = space/speed; |
3206 | + |
3207 | + if ( turnedRecently_ ) |
3208 | + minTime = owner_->GetTurnDelay(); |
3209 | + |
3210 | + return minTime; |
3211 | +} |
3212 | |
3213 | === added file 'src/tron/gAINavigator.h' |
3214 | --- src/tron/gAINavigator.h 1970-01-01 00:00:00 +0000 |
3215 | +++ src/tron/gAINavigator.h 2019-01-04 23:26:28 +0000 |
3216 | @@ -0,0 +1,423 @@ |
3217 | +/* |
3218 | + |
3219 | +************************************************************************* |
3220 | + |
3221 | +ArmageTron -- Just another Tron Lightcycle Game in 3D. |
3222 | +Copyright (C) 2005 by |
3223 | +and the AA DevTeam (see the file AUTHORS(.txt) in the main source directory) |
3224 | + |
3225 | +************************************************************************** |
3226 | + |
3227 | +This program is free software; you can redistribute it and/or |
3228 | +modify it under the terms of the GNU General Public License |
3229 | +as published by the Free Software Foundation; either version 2 |
3230 | +of the License, or (at your option) any later version. |
3231 | + |
3232 | +This program is distributed in the hope that it will be useful, |
3233 | +but WITHOUT ANY WARRANTY; without even the implied warranty of |
3234 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3235 | +GNU General Public License for more details. |
3236 | + |
3237 | +You should have received a copy of the GNU General Public License |
3238 | +along with this program; if not, write to the Free Software |
3239 | +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
3240 | + |
3241 | +*************************************************************************** |
3242 | + |
3243 | +*/ |
3244 | + |
3245 | +#ifndef ArmageTron_AI_NAVIGATOR_H |
3246 | +#define ArmageTron_AI_NAVIGATOR_H |
3247 | + |
3248 | +#include "gSensor.h" |
3249 | +#include "eCoord.h" |
3250 | + |
3251 | +class gCycle; |
3252 | + |
3253 | +//! AI helper class that knows the basics of staying alive. |
3254 | +class gAINavigator |
3255 | +{ |
3256 | + gAINavigator(); |
3257 | +public: |
3258 | + //! settings used by the idler bot |
3259 | + struct Settings |
3260 | + { |
3261 | + REAL newWallBlindness; //!< number of seconds new walls are invisible to the idler |
3262 | + REAL range; //!< seconds to plan ahead |
3263 | + |
3264 | + Settings(); |
3265 | + }; |
3266 | + |
3267 | + Settings settings_; // settings to use |
3268 | + |
3269 | + //! turn wish passed from outside |
3270 | + struct Wish |
3271 | + { |
3272 | + int turn; //!< direction to turn into (values >1 turn multiple times) |
3273 | + |
3274 | + REAL maxDisadvantage; //!< maximal disadvantage of the wish direction over others to have it still accepted |
3275 | + REAL minDistance; //!< minimal 'distance' value of the wish direction to have it accepted |
3276 | + |
3277 | + Wish( gAINavigator const & idler ); |
3278 | + private: |
3279 | + Wish(); |
3280 | + }; |
3281 | + |
3282 | + // sensor with additional data |
3283 | + class Sensor: public gSensor |
3284 | + { |
3285 | + public: |
3286 | + Sensor(gAINavigator & ai,const eCoord &start,const eCoord &d); |
3287 | + |
3288 | + virtual void PassEdge(const eWall *ww,REAL time,REAL a,int r); |
3289 | + |
3290 | + // check how far the hit wall extends straight into the given direction |
3291 | + REAL HitWallExtends( eCoord const & dir, eCoord const & origin ); |
3292 | + |
3293 | + gAINavigator & ai_; // AI using this sensor |
3294 | + gCycle * hitOwner_; // the owner of the hit wall |
3295 | + REAL hitTime_; // the time the hit wall was built at |
3296 | + REAL hitDistance_; // the distance of the wall to the cycle that built it |
3297 | + short lrSuggestion_; // sensor's oppinon on whether moving to the left or right of the hit wall is recommended (-1 for left, +1 for right) |
3298 | + int windingNumber_; // the number of turns (with sign) the cycle has taken |
3299 | + |
3300 | + private: |
3301 | + bool DoExtraDetectionStuff(); |
3302 | + }; |
3303 | + |
3304 | + //! ways to relay controls to a cycle |
3305 | + class CycleController |
3306 | + { |
3307 | + public: |
3308 | + virtual void Turn( gCycle & cycle , int dir ) = 0; //!< turns a cycle. dir < 0 turns left. |
3309 | + virtual void Brake( gCycle & cycle, bool brake ) = 0; //!< brakes a cycle |
3310 | + virtual ~CycleController(); |
3311 | + }; |
3312 | + |
3313 | + //! use direct control |
3314 | + class CycleControllerBasic: public CycleController |
3315 | + { |
3316 | + public: |
3317 | + virtual void Turn( gCycle & cycle , int dir ); //!< turns a cycle. |
3318 | + virtual void Brake( gCycle & cycle, bool brake ); //!< brakes a cycle |
3319 | + virtual ~CycleControllerBasic(); |
3320 | + }; |
3321 | + |
3322 | + //! use actions |
3323 | + class CycleControllerAction: public CycleController |
3324 | + { |
3325 | + public: |
3326 | + virtual void Turn( gCycle & cycle , int dir ); //!< turns a cycle. |
3327 | + virtual void Brake( gCycle & cycle, bool brake ); //!< brakes a cycle |
3328 | + virtual ~CycleControllerAction(); |
3329 | + }; |
3330 | + |
3331 | + //! describes walls |
3332 | + struct WallHug |
3333 | + { |
3334 | + gCycle const * owner; //! the cycle the walls we like belong to |
3335 | + REAL lastTimeSeen; //! the last time we saw such a wall |
3336 | + REAL hitDistance; //! driving distance of the wall that was hit |
3337 | + REAL distance; //! distance to the hit wall |
3338 | + int lr; //! direction the wall is running to as seen from us |
3339 | + |
3340 | + WallHug(); |
3341 | + |
3342 | + // fill data from sensor |
3343 | + void FillFrom( Sensor const & sensor ); |
3344 | + }; |
3345 | + |
3346 | + class PathGroup; |
3347 | + |
3348 | + //! a possible path that can be taken |
3349 | + class Path |
3350 | + { |
3351 | + friend class gAINavigator; |
3352 | + friend class PathGroup; |
3353 | + public: |
3354 | + REAL distance; //!< expected distance possible to travel this path without getting killed |
3355 | + REAL immediateDistance; //!< distance to next problem |
3356 | + REAL width; //!< width of the narrowest bit of the path |
3357 | + eCoord shortTermDirection; //!< direction to travel in in the short run |
3358 | + eCoord longTermDirection; //!< direction to travel in in the long run |
3359 | + int followedSince; //!< number of turns this path is already being followed |
3360 | + |
3361 | + WallHug left, right; //!< walls to the left and right of the path |
3362 | + private: |
3363 | + // fill in relevant data from sensors |
3364 | + void Fill( gAINavigator const & navigator, Sensor const & left, Sensor const & right, eCoord const & shortDir, eCoord const & longDir, int turn ); |
3365 | + REAL Take( CycleController & controller, gCycle & cycle, REAL maxStep ); //!< take that path. Return value: time to next check |
3366 | + ~Path(); |
3367 | + Path(); |
3368 | + |
3369 | + int turn; //!< direction to turn to next |
3370 | + REAL driveOn; //!< how long to drive straight before doing something (in distance) |
3371 | + }; |
3372 | + |
3373 | + class PathGroup |
3374 | + { |
3375 | + friend class gAINavigator; |
3376 | + public: |
3377 | + //! types of paths |
3378 | + enum PathID |
3379 | + { |
3380 | + PATH_UTURN_LEFT = 0, //!< turn around completely |
3381 | + PATH_TURN_LEFT, //!< turn left |
3382 | + PATH_ZIGZAG_LEFT, //!< turn left, then right, or wait, then turn left, looking for a hole |
3383 | + PATH_STRAIGHT, //!< go straight |
3384 | + PATH_ZIGZAG_RIGHT, |
3385 | + PATH_TURN_RIGHT, |
3386 | + PATH_UTURN_RIGHT, |
3387 | + PATH_COUNT |
3388 | + }; |
3389 | + |
3390 | + int GetPathCount() const; //!< returns the current number of paths |
3391 | + Path const & GetPath( int id ) const; //!< returns a path |
3392 | + REAL TakePath( CycleController & controller, gCycle & cycle, int id, REAL maxStep = 1E+30 ); //!< takes a path |
3393 | + Path const & GetLastPath() const; //!< the last path taken, with old info |
3394 | + |
3395 | + PathGroup(); |
3396 | + ~PathGroup(); |
3397 | + private: |
3398 | + Path & AccessPath( int id ); //!< returns a path |
3399 | + |
3400 | + Path last; //!< last path |
3401 | + Path paths[ PATH_COUNT ]; //!< fixed path list. Maybe add dynamic paths later. |
3402 | + }; |
3403 | + |
3404 | + //! evaluation of a path |
3405 | + struct PathEvaluation |
3406 | + { |
3407 | + bool veto; //!< was this path vetoed? |
3408 | + REAL score; //!< score of the path. 0: pointless suicide, 100: pretty good. |
3409 | + REAL nextThought; //!< seconds to next thought |
3410 | + |
3411 | + PathEvaluation(); |
3412 | + }; |
3413 | + |
3414 | + //! class evaluating pathes |
3415 | + class PathEvaluator |
3416 | + { |
3417 | + public: |
3418 | + //! evaluate a path. |
3419 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const = 0; |
3420 | + virtual ~PathEvaluator(); |
3421 | + |
3422 | + //! turns a value between 0 and infinity, where 1 would be an expected value, into a value |
3423 | + //! between 0 and 100 |
3424 | + static REAL Adjust( REAL input ) |
3425 | + { |
3426 | + return 100*(1-1/(1+input)); |
3427 | + } |
3428 | + }; |
3429 | + |
3430 | + //! simple evaluator: vetoes certain death moves, picks best space move |
3431 | + class SuicideEvaluator: public PathEvaluator |
3432 | + { |
3433 | + public: |
3434 | + SuicideEvaluator( gCycle const & cycle, REAL timeFrame ); |
3435 | + explicit SuicideEvaluator( gCycle const & cycle ); |
3436 | + |
3437 | + //! evaluate a path. |
3438 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3439 | + ~SuicideEvaluator(); |
3440 | + |
3441 | + static void SetEmergency( bool emergency ); |
3442 | + private: |
3443 | + gCycle const & cycle_; |
3444 | + REAL timeFrame_; |
3445 | + static bool emergency_; |
3446 | + }; |
3447 | + |
3448 | + //! simple evaluator: vetoes moves that trap self |
3449 | + class TrapEvaluator: public PathEvaluator |
3450 | + { |
3451 | + public: |
3452 | + TrapEvaluator( gCycle const & cycle, REAL space ); |
3453 | + explicit TrapEvaluator( gCycle const & cycle ); |
3454 | + |
3455 | + //! evaluate a path. |
3456 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3457 | + ~TrapEvaluator(); |
3458 | + |
3459 | + static void SetEmergency( bool emergency ); |
3460 | + private: |
3461 | + gCycle const & cycle_; |
3462 | + REAL space_; |
3463 | + }; |
3464 | + |
3465 | + //! simple evaluator: random noise |
3466 | + class RandomEvaluator: public PathEvaluator |
3467 | + { |
3468 | + public: |
3469 | + //! evaluate a path. |
3470 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3471 | + RandomEvaluator(); |
3472 | + ~RandomEvaluator(); |
3473 | + }; |
3474 | + |
3475 | + //! cowardly evaluator: try to move backwards on enemy walls |
3476 | + class CowardEvaluator: public PathEvaluator |
3477 | + { |
3478 | + public: |
3479 | + explicit CowardEvaluator( gCycle const & cycle ); |
3480 | + ~CowardEvaluator(); |
3481 | + |
3482 | + //! evaluate a path. |
3483 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3484 | + private: |
3485 | + gCycle const & cycle_; |
3486 | + }; |
3487 | + |
3488 | + //! tunnel evaluator: avoids tunneling between walls of different cycles |
3489 | + class TunnelEvaluator: public PathEvaluator |
3490 | + { |
3491 | + public: |
3492 | + explicit TunnelEvaluator( gCycle const & cycle ); |
3493 | + ~TunnelEvaluator(); |
3494 | + |
3495 | + //! evaluate a path. |
3496 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3497 | + private: |
3498 | + gCycle const & cycle_; |
3499 | + }; |
3500 | + |
3501 | + //! simple evaluator: measures available space compared to a passed-in value |
3502 | + class SpaceEvaluator: public PathEvaluator |
3503 | + { |
3504 | + public: |
3505 | + explicit SpaceEvaluator( gCycle const & cycle ); |
3506 | + explicit SpaceEvaluator( REAL referenceDistance ); |
3507 | + |
3508 | + //! evaluate a path. |
3509 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3510 | + ~SpaceEvaluator(); |
3511 | + private: |
3512 | + REAL referenceDistance_; |
3513 | + }; |
3514 | + |
3515 | + //! simple evaluator: likes to follow through with the plan |
3516 | + class PlanEvaluator: public PathEvaluator |
3517 | + { |
3518 | + public: |
3519 | + //! evaluate a path. |
3520 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3521 | + ~PlanEvaluator(); |
3522 | + }; |
3523 | + |
3524 | + //! simple evaluator: does not like it if rubber gets burned |
3525 | + class RubberEvaluator: public PathEvaluator |
3526 | + { |
3527 | + public: |
3528 | + //! evaluate a path. |
3529 | + virtual void Evaluate( Path const & path, PathEvaluation & evaluation ) const; |
3530 | + explicit RubberEvaluator( gCycle const & cylce ); |
3531 | + RubberEvaluator( gCycle const & cycle, REAL maxTime ); |
3532 | + ~RubberEvaluator(); |
3533 | + private: |
3534 | + void Init( gCycle const & cycle, REAL maxTime ); |
3535 | + REAL rubberLeft_; //!< amount of rubber left to burn with inevitable loss due to turn delay factored in |
3536 | + REAL maxRubber_; //!< maximal rubber possible to burn |
3537 | + }; |
3538 | + |
3539 | + //! evaluator for following a moving target |
3540 | + class FollowEvaluator: public PathEvaluator |
3541 | + { |
3542 | + public: |
3543 | + FollowEvaluator( gCycle const & cycle ); |
3544 | + ~FollowEvaluator(); |
3545 | + |
3546 | + //! return data of SolveTurn |
3547 | + struct SolveTurnData |
3548 | + { |
3549 | + REAL turnTime; //!< seconds to wait before we turn |
3550 | + REAL quality; //!< quality of the turn |
3551 | + eCoord turnDir; //!< direction to drive in |
3552 | + |
3553 | + SolveTurnData(): turnTime(0), quality(0){} |
3554 | + }; |
3555 | + |
3556 | + //! determine when we need to turn in order to catch the target. |
3557 | + void SolveTurn( int direction, eCoord const & targetVelocity, eCoord const & targetPosition, SolveTurnData & data ); |
3558 | + |
3559 | + //! set the target to follow |
3560 | + void SetTarget( eCoord const & target, eCoord const & velocity ); |
3561 | + |
3562 | + virtual void Evaluate( gAINavigator::Path const & path, gAINavigator::PathEvaluation & evaluation ) const; |
3563 | + |
3564 | + gCycle * GetBlocker() const { return blocker_; } |
3565 | + protected: |
3566 | + gCycle const & cycle_; //!< the owning cycle |
3567 | + gCycle * blocker_; //!< other cycle blocking the path to the target |
3568 | + bool blockedBySelf_; //!< flag indicating the path is blocked by the cycle itself |
3569 | + eCoord toTarget_; //!< direction to target, roughly normalized |
3570 | + REAL turnTime_; //!< time to make the next turn |
3571 | + }; |
3572 | + |
3573 | + |
3574 | + class EvaluationManager |
3575 | + { |
3576 | + public: |
3577 | + EvaluationManager( PathGroup & paths ); |
3578 | + |
3579 | + //! ways to combine new evaluation with previous results |
3580 | + enum BlendMode |
3581 | + { |
3582 | + BLEND_ADD, |
3583 | + BLEND_MULT, |
3584 | + BLEND_MAX, |
3585 | + BLEND_MIN |
3586 | + }; |
3587 | + |
3588 | + //! evaluate all paths using the evaluator |
3589 | + void Evaluate( PathEvaluator const & evaluator, BlendMode mode = BLEND_ADD, REAL scale = 1.0, REAL offset = 0.0 ); |
3590 | + |
3591 | + //! evaluate all paths using the evaluator, adding results |
3592 | + void Evaluate( PathEvaluator const & evaluator, REAL scale ) |
3593 | + { |
3594 | + Evaluate( evaluator, BLEND_ADD, scale ); |
3595 | + } |
3596 | + |
3597 | + //! reset scores, but don't forget veto |
3598 | + void Reset(); |
3599 | + |
3600 | + //! execute |
3601 | + REAL Finish( CycleController & controller, gCycle & cycle, REAL maxStep = 1E+30 ); |
3602 | + private: |
3603 | + PathGroup & paths_; //!< the path group |
3604 | + |
3605 | + int bestPath_; //!< best path |
3606 | + |
3607 | + //! list of evaluations fitting to those in the |
3608 | + PathEvaluation evaluations_[ PathGroup::PATH_COUNT ]; |
3609 | + }; |
3610 | + |
3611 | + PathGroup & GetPaths(); //!< returns the paths the navigator knows about |
3612 | + void UpdatePaths(); //!< updates the paths to the new cycle position |
3613 | + |
3614 | + gAINavigator( gCycle * owner ); |
3615 | + |
3616 | + // returns the controlled cycle |
3617 | + gCycle * Owner() const |
3618 | + { |
3619 | + return owner_; |
3620 | + } |
3621 | + |
3622 | + // determines the distance between two sensors; the size should give the likelyhood |
3623 | + // to survive if you pass through a gap between the two selected walls |
3624 | + REAL Distance( Sensor const & a, Sensor const & b ) const; |
3625 | + |
3626 | + bool CanMakeTurn( uActionPlayer * action ); |
3627 | + |
3628 | + //! does the main thinking at the current time, knowing the next thought can't be sooner than minstep |
3629 | + REAL Activate( REAL currentTime, REAL minstep, REAL penalty = 0, Wish * wish = 0 ); |
3630 | + |
3631 | +private: |
3632 | + short lastTurn_; //!< the last turn the chat AI made |
3633 | + REAL nextTurn_; //!< the next turn if one is planned |
3634 | + bool turnedRecently_; //!< whether the cycle was turned or almost turned recently |
3635 | + gCycle * owner_; //!< owner of chatbot |
3636 | + PathGroup paths_; //!< possible future paths |
3637 | +}; |
3638 | + |
3639 | +#endif |
3640 | |
3641 | === modified file 'src/tron/gCycle.cpp' |
3642 | --- src/tron/gCycle.cpp 2013-10-28 22:02:36 +0000 |
3643 | +++ src/tron/gCycle.cpp 2019-01-04 23:26:28 +0000 |
3644 | @@ -57,8 +57,12 @@ |
3645 | #include "gStatistics.h" |
3646 | |
3647 | #include "cCockpit.h" |
3648 | +<<<<<<< TREE |
3649 | #include "eWarmup.h" |
3650 | #include "eLadderLog.h" |
3651 | +======= |
3652 | +#include "gAINavigator.h" |
3653 | +>>>>>>> MERGE-SOURCE |
3654 | |
3655 | #include "tMath.h" |
3656 | #include <stdlib.h> |
3657 | @@ -116,8 +120,6 @@ |
3658 | static float sg_cycleSyncSmoothThreshold = .2f; |
3659 | static tSettingItem<float> conf_smoothThreshold ("CYCLE_SMOOTH_THRESHOLD", sg_cycleSyncSmoothThreshold); |
3660 | |
3661 | -static REAL sg_enemyChatbotTimePenalty = 30.0f; //!< penalty for victim in chatbot mode |
3662 | -static tSettingItem<REAL> sg_enemyChatbotTimePenaltyConf( "ENEMY_CHATBOT_PENALTY", sg_enemyChatbotTimePenalty ); |
3663 | extern REAL sg_suicideTimeout; |
3664 | |
3665 | REAL gCycle::wallsStayUpDelay=8.0f; // the time the cycle walls stay up ( negative values: they stay up forever ) |
3666 | @@ -288,11 +290,6 @@ |
3667 | #endif |
3668 | } |
3669 | |
3670 | -// from gCycleMovement.cpp |
3671 | -extern void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness ); |
3672 | -extern REAL sg_brakeCycle; |
3673 | -extern REAL sg_cycleBrakeDeplete; |
3674 | - |
3675 | // in release mode, default values should always be used. in debug mode, we want to experiment :) |
3676 | #ifdef DEBUG |
3677 | #ifndef DEDICATED |
3678 | @@ -331,284 +328,20 @@ |
3679 | static gChatBotSetting sg_chatBotDecayConf( "CHATBOT_DECAY", |
3680 | sg_chatBotDecay ); |
3681 | |
3682 | -class gCycleChatBot |
3683 | +class gCycleChatBot: public gAINavigator |
3684 | { |
3685 | - gCycleChatBot(); |
3686 | + friend class gCycle; |
3687 | + |
3688 | + REAL nextChatAI_; //!< the next time the chat AI can be active |
3689 | + REAL timeOnChatAI_; //!< the total time the player was on chat AI this round |
3690 | public: |
3691 | -class Sensor: public gSensor |
3692 | - { |
3693 | - public: |
3694 | - Sensor(gCycle *o,const eCoord &start,const eCoord &d) |
3695 | - : gSensor(o,start,d) |
3696 | - , hitOwner_( 0 ) |
3697 | - , hitTime_ ( 0 ) |
3698 | - , hitDistance_( o->MaxWallsLength() ) |
3699 | - , lrSuggestion_( 0 ) |
3700 | - , windingNumber_( 0 ) |
3701 | - { |
3702 | - if ( hitDistance_ <= 0 ) |
3703 | - hitDistance_ = o->GetDistance(); |
3704 | - } |
3705 | - |
3706 | - /* |
3707 | - // do detection and additional stuff |
3708 | - void detect( REAL range ) |
3709 | - { |
3710 | - gSensor::detect( range ); |
3711 | - } |
3712 | - */ |
3713 | - |
3714 | - virtual void PassEdge(const eWall *ww,REAL time,REAL a,int r) |
3715 | - { |
3716 | - try{ |
3717 | - gSensor::PassEdge(ww,time,a,r); |
3718 | - } |
3719 | - catch( eSensorFinished & e ) |
3720 | - { |
3721 | - if ( DoExtraDetectionStuff() ) |
3722 | - throw; |
3723 | - } |
3724 | - } |
3725 | - |
3726 | - bool DoExtraDetectionStuff() |
3727 | - { |
3728 | - // move towards the beginning of a wall |
3729 | - lrSuggestion_ = -lr; |
3730 | - |
3731 | - switch ( type ) |
3732 | - { |
3733 | - case gSENSOR_NONE: |
3734 | - case gSENSOR_RIM: |
3735 | - lrSuggestion_ = 0; |
3736 | - return true; |
3737 | - default: |
3738 | - // unless it is an enemy, follow his wall instead (uncomment for a nasty cowardy campbot) |
3739 | - // lrSuggestion *= -1; |
3740 | - case gSENSOR_SELF: |
3741 | - { |
3742 | - // determine whether we're hitting the front or back half of his wall |
3743 | - if ( !ehit ) |
3744 | - return true; |
3745 | - eWall * wall = ehit->GetWall(); |
3746 | - if ( !wall ) |
3747 | - return true; |
3748 | - gPlayerWall * playerWall = dynamic_cast< gPlayerWall * >( wall ); |
3749 | - if ( !playerWall ) |
3750 | - return true; |
3751 | - hitOwner_ = playerWall->Cycle(); |
3752 | - if ( !hitOwner_ ) |
3753 | - return true; |
3754 | - |
3755 | - // gCycleChatBot & enemyChatBot = Get( hitOwner_ ); |
3756 | - |
3757 | - REAL wallAlpha = playerWall->Edge()->Ratio( before_hit ); |
3758 | - // that's an unreliable source |
3759 | - if ( wallAlpha < 0 ) |
3760 | - wallAlpha = 0; |
3761 | - if ( wallAlpha > 1 ) |
3762 | - wallAlpha = 1; |
3763 | - hitDistance_ = hitOwner_->GetDistance() - playerWall->Pos( wallAlpha ); |
3764 | - hitTime_ = playerWall->Time( wallAlpha ); |
3765 | - windingNumber_ = playerWall->WindingNumber(); |
3766 | - |
3767 | - // don't see new walls |
3768 | - if ( hitTime_ > hitOwner_->LastTime() - sg_chatBotNewWallBlindness && hitOwner_ != owned ) |
3769 | - { |
3770 | - ehit = NULL; |
3771 | - hit = 1E+40; |
3772 | - return false; |
3773 | - } |
3774 | - |
3775 | - // REAL cycleDistance = hitOwner_->GetDistance(); |
3776 | - |
3777 | - // REAL wallStart = 0; |
3778 | - |
3779 | - /* |
3780 | - if ( gCycle::WallsLength() > 0 ) |
3781 | - { |
3782 | - wallStart = cyclePos - playerWall->Cycle()->ThisWallsLength(); |
3783 | - if ( wallStart < 0 ) |
3784 | - wallStart = 0; |
3785 | - } |
3786 | - */ |
3787 | - } |
3788 | - } |
3789 | - |
3790 | - return true; |
3791 | - } |
3792 | - |
3793 | - // check how far the hit wall extends straight into the given direction |
3794 | - REAL HitWallExtends( eCoord const & dir, eCoord const & origin ) |
3795 | - { |
3796 | - if ( !ehit || !ehit->Other() ) |
3797 | - { |
3798 | - return 1E+30; |
3799 | - } |
3800 | - |
3801 | - REAL ret = -1E+30; |
3802 | - eCoord ends[2] = { *ehit->Point(), *ehit->Other()->Point() }; |
3803 | - for ( int i = 1; i>=0; --i ) |
3804 | - { |
3805 | - REAL newRet = eCoord::F( dir, ends[i]-origin ); |
3806 | - if ( newRet > ret ) |
3807 | - ret = newRet; |
3808 | - } |
3809 | - |
3810 | - return ret; |
3811 | - } |
3812 | - |
3813 | - gCycle * hitOwner_; // the owner of the hit wall |
3814 | - REAL hitTime_; // the time the hit wall was built at |
3815 | - REAL hitDistance_; // the distance of the wall to the cycle that built it |
3816 | - short lrSuggestion_; // sensor's oppinon on whether moving to the left or right of the hit wall is recommended (-1 for left, +1 for right) |
3817 | - int windingNumber_; // the number of turns (with sign) the cycle has taken |
3818 | - }; |
3819 | - |
3820 | gCycleChatBot( gCycle * owner ) |
3821 | - : nextChatAI_( 0 ) |
3822 | + : gAINavigator( owner ) |
3823 | + , nextChatAI_( 0 ) |
3824 | , timeOnChatAI_( 0 ) |
3825 | - , lastTurn_( 0 ) |
3826 | - , nextTurn_ ( 0 ) |
3827 | - , turnedRecently_ ( 0 ) |
3828 | - , owner_ ( owner ) |
3829 | - { |
3830 | -#ifdef RLBOT |
3831 | - rlDir = 1; |
3832 | - rlLastTime = -100; |
3833 | -#endif |
3834 | - } |
3835 | - |
3836 | - // describes walls we like. We like enemy walls. We like to go between them. |
3837 | - class WallHug |
3838 | - { |
3839 | - public: |
3840 | - gCycle const * owner_; // the cycle the walls we like belong to |
3841 | - REAL lastTimeSeen_; // the last time we saw such a wall |
3842 | - |
3843 | - WallHug() |
3844 | - : owner_ ( NULL ) |
3845 | - , lastTimeSeen_ ( 0 ) |
3846 | - { |
3847 | - } |
3848 | - }; |
3849 | - |
3850 | - // promote seen walls to possible wallhug replacements |
3851 | - void FindHugReplacement( Sensor const & sensor ) |
3852 | - { |
3853 | - gCycle const * owner = sensor.hitOwner_; |
3854 | - if (!owner) |
3855 | - return; |
3856 | - |
3857 | - // store as possible replacement |
3858 | - if ( !hugReplacement_.owner_ && sensor.type != gSENSOR_SELF && |
3859 | - owner != hugLeft_.owner_ && |
3860 | - owner != hugRight_.owner_ ) |
3861 | - { |
3862 | - hugReplacement_.owner_ = sensor.hitOwner_; |
3863 | - hugReplacement_.lastTimeSeen_ = nextChatAI_; |
3864 | - } |
3865 | - |
3866 | - // update timestamps |
3867 | - if ( owner == hugLeft_.owner_ ) |
3868 | - hugLeft_.lastTimeSeen_ = nextChatAI_; |
3869 | - if ( owner == hugRight_.owner_ ) |
3870 | - hugRight_.lastTimeSeen_ = nextChatAI_; |
3871 | - } |
3872 | - |
3873 | - // determines the distance between two sensors; the size should give the likelyhood |
3874 | - // to survive if you pass through a gap between the two selected walls |
3875 | - REAL Distance( Sensor const & a, Sensor const & b ) |
3876 | - { |
3877 | - // make sure a is left from b |
3878 | - if ( a.Direction() * b.Direction() < 0 ) |
3879 | - return Distance( b, a ); |
3880 | - |
3881 | - bool self = a.type == gSENSOR_SELF || b.type == gSENSOR_SELF; |
3882 | - bool rim = a.type == gSENSOR_RIM || b.type == gSENSOR_RIM; |
3883 | - |
3884 | - // avoid. own. walls. |
3885 | - REAL selfHatred = 1; |
3886 | - if ( a.type == gSENSOR_SELF ) |
3887 | - { |
3888 | - selfHatred *= .5; |
3889 | - if ( a.lr > 0 ) |
3890 | - { |
3891 | - selfHatred *= .5; |
3892 | - if ( b.type == gSENSOR_RIM ) |
3893 | - selfHatred *= .25; |
3894 | - } |
3895 | - } |
3896 | - if ( b.type == gSENSOR_SELF ) |
3897 | - { |
3898 | - selfHatred *= .5; |
3899 | - if ( b.lr < 0 ) |
3900 | - { |
3901 | - selfHatred *= .5; |
3902 | - if ( a.type == gSENSOR_RIM ) |
3903 | - selfHatred *= .25; |
3904 | - } |
3905 | - } |
3906 | - |
3907 | - // some big distance to return if we don't know anything better |
3908 | - REAL bigDistance = owner_->MaxWallsLength(); |
3909 | - if ( bigDistance <= 0 ) |
3910 | - bigDistance = owner_->GetDistance(); |
3911 | - |
3912 | - if ( a.hitOwner_ != b.hitOwner_ ) |
3913 | - { |
3914 | - // different owners? Great, there has to be a way through! |
3915 | - REAL ret = |
3916 | - a.hitDistance_ + b.hitDistance_; |
3917 | - |
3918 | - if ( rim ) |
3919 | - { |
3920 | - ret = bigDistance * .001 + ret * .01 + ( a.before_hit - b.before_hit).Norm(); |
3921 | - |
3922 | - // we love going between the rim and enemies |
3923 | - if ( !self ) |
3924 | - ret = bigDistance * 2; |
3925 | - } |
3926 | - |
3927 | - // minimal factor should be 1, this path should never return something smaller than the |
3928 | - // paths where only one cycle's walls are hit |
3929 | - ret *= 16; |
3930 | - |
3931 | - // or empty space |
3932 | - if ( a.type == gSENSOR_NONE || b.type == gSENSOR_NONE ) |
3933 | - ret *= 2; |
3934 | - |
3935 | - return ret * selfHatred; |
3936 | - } |
3937 | - else if ( rim ) |
3938 | - { |
3939 | - // at least one rim wall? Take the distance between the hit positions. |
3940 | - return ( a.before_hit - b.before_hit).Norm() * selfHatred; |
3941 | - } |
3942 | - else if ( a.type == gSENSOR_NONE && b.type == gSENSOR_NONE ) |
3943 | - { |
3944 | - // empty space! Woo! |
3945 | - return owner_->GetDistance() * 256; |
3946 | - } |
3947 | - else if ( a.lr != b.lr ) |
3948 | - { |
3949 | - // different directions? Also great! |
3950 | - return ( fabsf( a.hitDistance_ - b.hitDistance_ ) + .25 * bigDistance ) * selfHatred; |
3951 | - } |
3952 | - /* |
3953 | - else if ( - 2 * a.lr * (a.windingNumber_ - b.windingNumber_ ) > owner_->Grid()->WindingNumber() ) |
3954 | - { |
3955 | - // this looks like a way out to me |
3956 | - return fabsf( a.hitDistance_ - b.hitDistance_ ) * 10 * selfHatred; |
3957 | - } |
3958 | - */ |
3959 | - else |
3960 | - { |
3961 | - // well, the longer the wall segment between the two points, the better. |
3962 | - return fabsf( a.hitDistance_ - b.hitDistance_ ) * selfHatred; |
3963 | - } |
3964 | - |
3965 | - // default: hit distance |
3966 | - return ( a.before_hit - b.before_hit).Norm() * selfHatred; |
3967 | + { |
3968 | + settings_.range = sg_chatBotRange; |
3969 | + settings_.newWallBlindness = sg_chatBotNewWallBlindness; |
3970 | } |
3971 | |
3972 | static gCycleChatBot & Get( gCycle * cycle ) |
3973 | @@ -622,63 +355,27 @@ |
3974 | return *cycle->chatBot_; |
3975 | } |
3976 | |
3977 | - bool CanMakeTurn( uActionPlayer * action ) |
3978 | + REAL Think( REAL minStep ) |
3979 | { |
3980 | - return owner_->CanMakeTurn( ( action == &gCycle::se_turnRight ) ? 1 : -1 ); |
3981 | + UpdatePaths(); |
3982 | + EvaluationManager manager( GetPaths() ); |
3983 | + manager.Evaluate( SuicideEvaluator( *Owner(), sg_chatBotMinTimestep*1.1 ), 1 ); |
3984 | + manager.Evaluate( SuicideEvaluator( *Owner(), minStep ), 1 ); |
3985 | + manager.Reset(); |
3986 | + manager.Evaluate( SpaceEvaluator( *Owner() ), 1 ); |
3987 | + manager.Evaluate( PlanEvaluator(), .1 ); |
3988 | + CycleControllerAction controller; |
3989 | + return manager.Finish( controller, *Owner(), minStep ); |
3990 | } |
3991 | |
3992 | -#ifdef RLBOT |
3993 | - int rlDir; |
3994 | - REAL rlLastTime; |
3995 | -#endif |
3996 | - |
3997 | - // does the main thinking |
3998 | void Activate( REAL currentTime ) |
3999 | { |
4000 | -#ifdef RLBOT |
4001 | - // hack chatbot for crazy turning |
4002 | - { |
4003 | - if (!owner_->Alive() || !owner_->Vulnerable() ) |
4004 | - { |
4005 | - return; |
4006 | - } |
4007 | - if( fabs( rlLastTime - currentTime) > 1 ) |
4008 | - { |
4009 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4010 | - rlDir = -1; |
4011 | - } |
4012 | - else if ( rlDir > 0 ) |
4013 | - { |
4014 | - if( CanMakeTurn( &gCycle::se_turnRight ) ) |
4015 | - { |
4016 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4017 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4018 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4019 | - rlDir = -1; |
4020 | - } |
4021 | - } |
4022 | - else |
4023 | - { |
4024 | - if( CanMakeTurn( &gCycle::se_turnLeft ) ) |
4025 | - { |
4026 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4027 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4028 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4029 | - rlDir = 1; |
4030 | - } |
4031 | - } |
4032 | - rlLastTime = currentTime; |
4033 | - return; |
4034 | - } |
4035 | -#endif |
4036 | - |
4037 | // is it already time for activation? |
4038 | if ( currentTime < nextChatAI_ ) |
4039 | return; |
4040 | |
4041 | - REAL lookahead = sg_chatBotRange; // seconds to plan ahead |
4042 | REAL minstep = sg_chatBotMinTimestep; // minimum timestep between thoughts in seconds |
4043 | - REAL maxstep = sg_chatBotMinTimestep * 4 * ( 1 + .1 * tReproducibleRandomizer::GetInstance().Get() ); // maximum timestep between thoughts in seconds |
4044 | + REAL maxstep = sg_chatBotMinTimestep * ( 1 + .5 * tReproducibleRandomizer::GetInstance().Get() ); // maximum timestep between thoughts in seconds |
4045 | |
4046 | // chat AI wasn't active yet, so don't start immediately |
4047 | if ( nextChatAI_ <= EPS ) |
4048 | @@ -689,11 +386,6 @@ |
4049 | |
4050 | timeOnChatAI_ += currentTime - nextChatAI_; |
4051 | |
4052 | - // cylce data |
4053 | - REAL speed = owner_->Speed(); |
4054 | - eCoord dir = owner_->Direction(); |
4055 | - eCoord pos = owner_->Position(); |
4056 | - |
4057 | // make chat AI worse over time |
4058 | if ( sn_GetNetState() != nSTANDALONE ) |
4059 | { |
4060 | @@ -705,408 +397,26 @@ |
4061 | } |
4062 | } |
4063 | |
4064 | - REAL range= speed * lookahead; |
4065 | - eCoord scanDir = dir * range; |
4066 | - |
4067 | - REAL frontFactor = .5; |
4068 | - |
4069 | - Sensor front(owner_,pos,scanDir); |
4070 | - front.detect(frontFactor); |
4071 | - owner_->enemyInfluence.AddSensor( front, sg_enemyChatbotTimePenalty, owner_ ); |
4072 | - |
4073 | - REAL minMoveOn = 0, maxMoveOn = 0, moveOn = 0; |
4074 | - |
4075 | - // get extra time we get through rubber usage |
4076 | - REAL rubberGranted, rubberEffectiveness; |
4077 | - sg_RubberValues( owner_->player, speed, rubberGranted, rubberEffectiveness ); |
4078 | - REAL rubberTime = ( rubberGranted - owner_->GetRubber() )*rubberEffectiveness/speed; |
4079 | - REAL rubberRatio = owner_->GetRubber()/rubberGranted; |
4080 | - |
4081 | - if ( front.ehit ) |
4082 | - { |
4083 | - turnedRecently_ = false; |
4084 | - |
4085 | - // these checks can hit our last wall and fail. Temporarily set it to NULL. |
4086 | - tJUST_CONTROLLED_PTR< gNetPlayerWall > lastWall = owner_->lastWall; |
4087 | - owner_->lastWall = NULL; |
4088 | - |
4089 | - REAL narrowFront = 1; |
4090 | - |
4091 | - // cast four diagonal rays |
4092 | - Sensor forwardLeft ( owner_, pos, scanDir.Turn(+1,+1 ) ); |
4093 | - Sensor backwardLeft( owner_, pos, scanDir.Turn(-1,+narrowFront) ); |
4094 | - forwardLeft.detect(1); |
4095 | - backwardLeft.detect(1); |
4096 | - Sensor forwardRight ( owner_, pos, scanDir.Turn(+1,-1 ) ); |
4097 | - Sensor backwardRight( owner_, pos, scanDir.Turn(-1,-narrowFront) ); |
4098 | - forwardRight.detect(1); |
4099 | - backwardRight.detect(1); |
4100 | - |
4101 | - // do we have a hug replacement candiate? If so, take it. |
4102 | - if ( hugReplacement_.owner_ && !hugLeft_.owner_ && !hugRight_.owner_ ) |
4103 | - { |
4104 | - // first time hugging? let the status quo decide. |
4105 | - int lr = 0; |
4106 | - if ( backwardLeft.hitOwner_ == hugReplacement_.owner_ ) |
4107 | - lr--; |
4108 | - if ( forwardLeft.hitOwner_ == hugReplacement_.owner_ ) |
4109 | - lr--; |
4110 | - if ( backwardRight.hitOwner_ == hugReplacement_.owner_ ) |
4111 | - lr++; |
4112 | - if ( forwardRight.hitOwner_ == hugReplacement_.owner_ ) |
4113 | - lr++; |
4114 | - |
4115 | - if ( lr > 0 ) |
4116 | - hugRight_ = hugReplacement_; |
4117 | - if ( lr < 0 ) |
4118 | - hugLeft_ = hugReplacement_; |
4119 | - |
4120 | - hugReplacement_.owner_ = 0; |
4121 | - } |
4122 | - |
4123 | - if ( hugReplacement_.owner_ ) |
4124 | - { |
4125 | - if( hugLeft_.lastTimeSeen_ < hugRight_.lastTimeSeen_ ) |
4126 | - { |
4127 | - if ( hugReplacement_.lastTimeSeen_ > hugLeft_.lastTimeSeen_ ) |
4128 | - hugLeft_ = hugReplacement_; |
4129 | - } |
4130 | - else |
4131 | - { |
4132 | - if ( hugReplacement_.lastTimeSeen_ > hugRight_.lastTimeSeen_ ) |
4133 | - hugRight_ = hugReplacement_; |
4134 | - } |
4135 | - hugReplacement_.owner_ = 0; |
4136 | - } |
4137 | - |
4138 | - FindHugReplacement( front ); |
4139 | - FindHugReplacement( forwardLeft ); |
4140 | - FindHugReplacement( forwardRight ); |
4141 | - FindHugReplacement( backwardLeft ); |
4142 | - FindHugReplacement( backwardRight ); |
4143 | - |
4144 | - // determine survival chances in the four directions |
4145 | - REAL frontOpen = Distance ( forwardLeft, forwardRight ); |
4146 | - REAL leftOpen = Distance ( forwardLeft, backwardLeft ); |
4147 | - REAL rightOpen = Distance ( forwardRight, backwardRight ); |
4148 | - REAL rearOpen = Distance ( backwardLeft, backwardRight ); |
4149 | - |
4150 | - Sensor self( owner_, pos, scanDir.Turn(-1, 0) ); |
4151 | - // fake entries |
4152 | - self.before_hit = pos; |
4153 | - self.windingNumber_ = owner_->windingNumber_; |
4154 | - self.type = gSENSOR_SELF; |
4155 | - self.hitDistance_ = 0; |
4156 | - self.hitOwner_ = owner_; |
4157 | - self.hitTime_ = currentTime; |
4158 | - self.lr = -1; |
4159 | - REAL rearLeftOpen = Distance( backwardLeft, self ); |
4160 | - self.lr = 1; |
4161 | - REAL rearRightOpen = Distance( backwardRight, self ); |
4162 | - |
4163 | - /* |
4164 | - // override: don't camp (too much) |
4165 | - if ( forwardRight.type == gSENSOR_SELF && |
4166 | - forwardLeft.type == gSENSOR_SELF && |
4167 | - backwardRight.type == gSENSOR_SELF && |
4168 | - backwardLeft.type == gSENSOR_SELF && |
4169 | - front.type == gSENSOR_SELF && |
4170 | - forwardRight.lr == front.lr && |
4171 | - forwardLeft.lr == front.lr && |
4172 | - backwardRight.lr == front.lr && |
4173 | - backwardLeft.lr == front.lr && |
4174 | - frontOpen + leftOpen + rightOpen < owner_->GetDistance() * .5 ) |
4175 | - { |
4176 | - turnedRecently_ = true; |
4177 | - if ( front.lr > 0 ) |
4178 | - { |
4179 | - if ( leftOpen > minstep * speed ) |
4180 | - // force a turn to the left |
4181 | - rightOpen = 0; |
4182 | - else if ( front.hit * range < 2 * minstep ) |
4183 | - // force a preliminary turn to the right that will allow us to reverse |
4184 | - frontOpen = 0; |
4185 | - } |
4186 | - else |
4187 | - { |
4188 | - if ( rightOpen > minstep * speed ) |
4189 | - // force a turn to the right |
4190 | - leftOpen = 0; |
4191 | - else if ( front.hit * range < 2 * minstep ) |
4192 | - // force a preliminary turn to the left that will allow us to reverse |
4193 | - frontOpen = 0; |
4194 | - } |
4195 | - } |
4196 | - */ |
4197 | - |
4198 | - // override rim hugging |
4199 | - if ( forwardRight.type == gSENSOR_SELF && |
4200 | - forwardLeft.type == gSENSOR_RIM && |
4201 | - backwardRight.type == gSENSOR_SELF && |
4202 | - backwardLeft.type == gSENSOR_RIM && |
4203 | - // backwardLeft.hit < .1 && |
4204 | - forwardRight.lr == -1 && |
4205 | - backwardRight.lr == -1 ) |
4206 | - { |
4207 | - turnedRecently_ = true; |
4208 | - if ( rightOpen > speed * ( owner_->GetTurnDelay() - rubberTime * .8 ) ) |
4209 | - { |
4210 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4211 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4212 | - } |
4213 | - else |
4214 | - { |
4215 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4216 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4217 | - } |
4218 | - } |
4219 | - |
4220 | - if ( forwardLeft.type == gSENSOR_SELF && |
4221 | - forwardRight.type == gSENSOR_RIM && |
4222 | - backwardLeft.type == gSENSOR_SELF && |
4223 | - backwardRight.type == gSENSOR_RIM && |
4224 | - // backwardRight.hit < .1 && |
4225 | - forwardLeft.lr == 1 && |
4226 | - backwardLeft.lr == 1 ) |
4227 | - { |
4228 | - turnedRecently_ = true; |
4229 | - if ( leftOpen > speed * ( owner_->GetTurnDelay() - rubberTime * .8 ) ) |
4230 | - { |
4231 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4232 | - owner_->Act( &gCycle::se_turnLeft, 1 ); |
4233 | - } |
4234 | - else |
4235 | - { |
4236 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4237 | - owner_->Act( &gCycle::se_turnRight, 1 ); |
4238 | - } |
4239 | - } |
4240 | - |
4241 | - // get the best turn direction |
4242 | - uActionPlayer * bestAction = ( leftOpen > rightOpen ) ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
4243 | - int bestDir = ( leftOpen > rightOpen ) ? 1 : -1; |
4244 | - REAL bestOpen = ( leftOpen > rightOpen ) ? leftOpen : rightOpen; |
4245 | - Sensor & bestForward = ( leftOpen > rightOpen ) ? forwardLeft : forwardRight; |
4246 | - Sensor & bestBackward = ( leftOpen > rightOpen ) ? backwardLeft : backwardRight; |
4247 | - |
4248 | - Sensor direct ( owner_, pos, scanDir.Turn( 0, bestDir) ); |
4249 | - direct.detect( 1 ); |
4250 | - |
4251 | - // restore last wall |
4252 | - owner_->lastWall = lastWall; |
4253 | - |
4254 | - // only turn if the hole has a shape that allows better entry after we do a zig-zag, or if we're past the good turning point |
4255 | - // see how the survival chance is distributed between forward and backward half |
4256 | - REAL forwardHalf = Distance ( direct, bestForward ); |
4257 | - REAL backwardHalf = Distance ( direct, bestBackward ); |
4258 | - |
4259 | - REAL forwardOverhang = bestForward.HitWallExtends( bestForward.Direction(), pos ); |
4260 | - REAL backwardOverhang = bestBackward.HitWallExtends( bestForward.Direction(), pos ); |
4261 | - |
4262 | - // we have to move forward this much before we can hope to turn |
4263 | - minMoveOn = bestBackward.HitWallExtends( dir, pos ); |
4264 | - |
4265 | - // maybe the direct to the side sensor is better? |
4266 | - REAL minMoveOnOther = direct.HitWallExtends( dir, pos ); |
4267 | - |
4268 | - // determine how far we can drive on |
4269 | - maxMoveOn = bestForward.HitWallExtends( dir, pos ); |
4270 | - REAL maxMoveOnOther = front.HitWallExtends( dir, pos ); |
4271 | - if ( maxMoveOn > maxMoveOnOther ) |
4272 | - maxMoveOn = maxMoveOnOther; |
4273 | - |
4274 | - if ( maxMoveOn > minMoveOnOther && forwardHalf > backwardHalf && direct.hitOwner_ == bestBackward.hitOwner_ ) |
4275 | - { |
4276 | - backwardOverhang = direct.HitWallExtends( bestForward.Direction(), pos ); |
4277 | - minMoveOn = minMoveOnOther; |
4278 | - } |
4279 | - |
4280 | - // best place to turn |
4281 | - moveOn = .5 * ( minMoveOn * ( 1 + rubberRatio ) + maxMoveOn * ( 1 - rubberRatio ) ); |
4282 | - |
4283 | - // hit the brakes before you hit anything and if it's worth it |
4284 | - bool brake = sg_brakeCycle > 0 && |
4285 | - front.hit * lookahead * sg_cycleBrakeDeplete < owner_->GetBrakingReservoir() && |
4286 | - sg_brakeCycle * front.hit * lookahead < 2 * speed * owner_->GetBrakingReservoir() && |
4287 | - ( maxMoveOn - minMoveOn ) > 0 && |
4288 | - owner_->GetBrakingReservoir() * ( maxMoveOn - minMoveOn ) < speed * owner_->GetTurnDelay(); |
4289 | - if ( frontOpen < bestOpen && |
4290 | - ( forwardOverhang <= backwardOverhang || ( minMoveOn < 0 && moveOn < minstep * speed ) ) ) |
4291 | - { |
4292 | - // FindHugReplacement( direct ); |
4293 | - // REAL expectedBackwardHalf = ( direct.before_hit - bestBackward.before_hit ).Norm(); |
4294 | - |
4295 | - // if ( ( ( forwardHalf + backwardHalf > bestOpen * 2 || backwardHalf > frontOpen * 10 || backwardHalf > expectedBackwardHalf * 1.01 ) && frontOpen < bestOpen ) || |
4296 | - // rubberTime * .5 + minspace * lookahead < minstep ) |
4297 | - // { |
4298 | - turnedRecently_ = true; |
4299 | - |
4300 | - minMoveOn = maxMoveOn = moveOn = 0; |
4301 | - |
4302 | - /* |
4303 | - if ( |
4304 | - ( ( ( bestBackward.type == gSENSOR_ENEMY || bestBackward.type == gSENSOR_TEAMMATE ) && bestBackward.hitDistance_ < bestBackward.hit * lookahead * speed ) || |
4305 | - direct.hit * lookahead + rubberTime < owner_->GetTurnDelay() ) && |
4306 | - ( bestBackward.hit * lookahead + rubberTime < owner_->GetTurnDelay() || |
4307 | - bestForward.hit * lookahead + rubberTime < owner_->GetTurnDelay() ) |
4308 | - ) |
4309 | - { |
4310 | - // override: stupid turn into certain death, turn it around if that makes it less stupid |
4311 | - uActionPlayer * newBestAction = ( leftOpen > rightOpen ) ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
4312 | - Sensor newDirect ( owner_, pos, scanDir.Turn( 0, -bestDir) ); |
4313 | - newDirect.detect( 1 ); |
4314 | - if ( newDirect.hit > direct.hit || |
4315 | - newDirect.hit * lookahead + rubberTime > owner_->GetTurnDelay() ) |
4316 | - owner_->Act( newBestAction, 1 ); |
4317 | - } |
4318 | - else |
4319 | - */ |
4320 | - { |
4321 | - if ( !CanMakeTurn( bestAction ) ) |
4322 | - { |
4323 | - nextChatAI_ = currentTime; |
4324 | - return; |
4325 | - } |
4326 | - |
4327 | - owner_->Act( bestAction, 1 ); |
4328 | - } |
4329 | - |
4330 | - brake = false; |
4331 | - } |
4332 | - else |
4333 | - { |
4334 | - // the best |
4335 | - REAL bestSoFar = frontOpen > bestOpen ? frontOpen : bestOpen; |
4336 | - bestSoFar *= ( 10 * ( 1 - rubberRatio ) + 1 ); |
4337 | - |
4338 | - if ( rearOpen > bestSoFar && ( rearLeftOpen > bestSoFar || rearRightOpen > bestSoFar ) ) |
4339 | - { |
4340 | - brake = false; |
4341 | - turnedRecently_ = true; |
4342 | - |
4343 | - bool goLeft = rearLeftOpen > rearRightOpen; |
4344 | - |
4345 | - // dead end. reverse into the opposite direction of the front wall |
4346 | - uActionPlayer * bestAction = goLeft ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
4347 | - uActionPlayer * otherAction = !goLeft ? &gCycle::se_turnLeft : &gCycle::se_turnRight; |
4348 | - Sensor & bestForward = goLeft ? forwardLeft : forwardRight; |
4349 | - Sensor & bestBackward = goLeft ? backwardLeft : backwardRight; |
4350 | - Sensor & otherForward = !goLeft ? forwardLeft : forwardRight; |
4351 | - Sensor & otherBackward = !goLeft ? backwardLeft : backwardRight; |
4352 | - |
4353 | - // space in the two directions available for turns |
4354 | - REAL bestHit = bestForward.hit > bestBackward.hit ? bestBackward.hit : bestForward.hit; |
4355 | - REAL otherHit = otherForward.hit > otherBackward.hit ? otherBackward.hit : otherForward.hit; |
4356 | - |
4357 | - bool wait = false; |
4358 | - |
4359 | - if ( !CanMakeTurn( bestAction ) ) |
4360 | - { |
4361 | - nextChatAI_ = currentTime; |
4362 | - return; |
4363 | - } |
4364 | - |
4365 | - // well, after a short turn to the right if space is tight |
4366 | - if ( bestHit * lookahead < owner_->GetTurnDelay() + rubberTime ) |
4367 | - { |
4368 | - if ( otherHit < bestForward.hit * 2 && front.hit * lookahead > owner_->GetTurnDelay() * 2 ) |
4369 | - { |
4370 | - // wait a bit, perhaps there will be a better spot |
4371 | - wait = true; |
4372 | - } |
4373 | - else |
4374 | - { |
4375 | - if ( !CanMakeTurn( otherAction ) ) |
4376 | - { |
4377 | - nextChatAI_ = currentTime; |
4378 | - return; |
4379 | - } |
4380 | - |
4381 | - owner_->Act( otherAction, 1 ); |
4382 | - |
4383 | - // there needs to be space ahead to finish the maneuver correctly |
4384 | - if ( maxMoveOn < speed * owner_->GetTurnDelay() ) |
4385 | - { |
4386 | - // there isn't. oh well, turn into the wrong direction completely, see if I care |
4387 | - owner_->Act( otherAction, 1 ); |
4388 | - wait = true; |
4389 | - } |
4390 | - } |
4391 | - } |
4392 | - |
4393 | - if ( !wait ) |
4394 | - { |
4395 | - owner_->Act( bestAction, 1 ); |
4396 | - owner_->Act( bestAction, 1 ); |
4397 | - } |
4398 | - |
4399 | - minMoveOn = maxMoveOn = moveOn = 0; |
4400 | - } |
4401 | - } |
4402 | - |
4403 | - // execute brake command |
4404 | - owner_->Act( &gCycle::s_brake, brake ? 1 : -1 ); |
4405 | - |
4406 | - // swap hugged walls if we're in fact grinding them the other way round |
4407 | - if ( hugLeft_.owner_ == backwardRight.hitOwner_ || |
4408 | - hugRight_.owner_ == backwardLeft.hitOwner_ ) |
4409 | - { |
4410 | - WallHug swap = hugRight_; |
4411 | - hugRight_ = hugLeft_; |
4412 | - hugLeft_ = swap; |
4413 | - } |
4414 | - } |
4415 | - |
4416 | - // REAL mintime = minspace * lookahead; |
4417 | - |
4418 | - // try again soon |
4419 | - // REAL newmintime = mintime * .5 - minstep * .2 * tReproducibleRandomizer::GetInstance().Get(); |
4420 | - |
4421 | - // clamp |
4422 | - // if ( newmintime < minstep ) |
4423 | - // newmintime = minstep; |
4424 | - |
4425 | - // add slack, acceleration and rubber |
4426 | - // if ( owner_->acceleration > 0 ) |
4427 | - // mintime -= owner_->acceleration * mintime * mintime / speed; |
4428 | - // mintime -= .1 * minstep - rubberTime * .3; |
4429 | - |
4430 | - // if the next step gets us too close to the wall to do anything useful, |
4431 | - // bring us really close right away. |
4432 | - // if ( mintime - newmintime > minstep ) |
4433 | - // { |
4434 | - // mintime = newmintime; |
4435 | - // } |
4436 | - |
4437 | - REAL space = moveOn; |
4438 | - REAL minTime = space/speed; |
4439 | - |
4440 | - if ( turnedRecently_ ) |
4441 | - minTime = owner_->GetTurnDelay(); |
4442 | - |
4443 | - if ( minTime < minstep ) |
4444 | - minTime = minstep; |
4445 | - if ( minTime > maxstep + minstep * 1.5 ) |
4446 | - { |
4447 | - minTime = maxstep; |
4448 | - } |
4449 | - // minTime = 0; |
4450 | - |
4451 | - nextChatAI_ = currentTime + minTime; |
4452 | - timeOnChatAI_ += minTime; |
4453 | + REAL minTime = Think( maxstep*2 ); |
4454 | + |
4455 | + if( minTime < 0 ) |
4456 | + { |
4457 | + // try right again |
4458 | + nextChatAI_ = currentTime; |
4459 | + } |
4460 | + else |
4461 | + { |
4462 | + if ( minTime < minstep ) |
4463 | + minTime = minstep; |
4464 | + if ( minTime > maxstep + minstep * 1.5 ) |
4465 | + { |
4466 | + minTime = maxstep; |
4467 | + } |
4468 | + |
4469 | + nextChatAI_ = currentTime + minTime; |
4470 | + timeOnChatAI_ += minTime; |
4471 | + } |
4472 | } |
4473 | - |
4474 | - REAL nextChatAI_; //!< the next time the chat AI can be active |
4475 | -private: |
4476 | - REAL timeOnChatAI_; //!< the total time the player was on chat AI this round |
4477 | - short lastTurn_; //!< the last turn the chat AI made |
4478 | - REAL nextTurn_; //!< the next turn if one is planned |
4479 | - bool turnedRecently_; //!< whether the cycle was turned or almost turned recently |
4480 | - gCycle * owner_; //!< owner of chatbot |
4481 | - |
4482 | - WallHug hugLeft_; //!< the wall we like to have on our left side |
4483 | - WallHug hugRight_; //!< the wall we like to have on our right side |
4484 | - WallHug hugReplacement_; //!< a possible replacement candidate for one of the hugged walls |
4485 | }; |
4486 | |
4487 | // ***************************************************************** |
4488 | @@ -1704,7 +1014,7 @@ |
4489 | sg_cycleRubberSync); |
4490 | |
4491 | // how much rubber usage shortens the walls |
4492 | -static REAL sg_cycleRubberWallShrink = 0; |
4493 | +REAL sg_cycleRubberWallShrink = 0; |
4494 | static nSettingItemWatched<REAL> |
4495 | sg_cycleRubberWallShrinkConf("CYCLE_RUBBER_WALL_SHRINK", |
4496 | sg_cycleRubberWallShrink, |
4497 | @@ -4119,15 +3429,19 @@ |
4498 | dir_eWall.Select(); |
4499 | } |
4500 | } |
4501 | +#endif |
4502 | |
4503 | gCycleWallsDisplayListManager::gCycleWallsDisplayListManager() |
4504 | : wallList_(0) |
4505 | , wallsWithDisplayList_(0) |
4506 | +#ifndef DEDICATED |
4507 | , wallsWithDisplayListMinDistance_(0) |
4508 | , wallsInDisplayList_(0) |
4509 | +#endif |
4510 | { |
4511 | } |
4512 | |
4513 | +#ifndef DEDICATED |
4514 | bool gCycleWallsDisplayListManager::CannotHaveList( REAL distance, gCycle const * cycle ) |
4515 | { |
4516 | return |
4517 | @@ -6173,7 +5487,7 @@ |
4518 | |
4519 | void gCycle::RightBeforeDeath( int numTries ) |
4520 | { |
4521 | - if ( player ) |
4522 | + if ( player && pendingTurns.size() == 0 ) |
4523 | { |
4524 | player->RightBeforeDeath( numTries ); |
4525 | } |
4526 | @@ -6257,3 +5571,97 @@ |
4527 | { |
4528 | return Alive() && lastTime > spawnTime_ + sg_cycleInvulnerableTime; |
4529 | } |
4530 | + |
4531 | +// ************* |
4532 | +// * wall info * |
4533 | +// ************* |
4534 | + |
4535 | +struct gWallInfoTemp |
4536 | +{ |
4537 | + // center of mass data |
4538 | + eCoord com; |
4539 | + REAL weight; |
4540 | + |
4541 | + // tail end |
4542 | + eCoord end; |
4543 | + eCoord endDir; |
4544 | + |
4545 | + REAL smallestBlend; |
4546 | + |
4547 | + gWallInfoTemp() |
4548 | + : weight( 0 ), smallestBlend( HUGE ){} |
4549 | + |
4550 | + void AddWall( gNetPlayerWall * wall, gCycle const & cycle, REAL totalLength ) |
4551 | + { |
4552 | + if ( !wall || !wall->IsDangerousAnywhere( cycle.LastTime() ) ) |
4553 | + { |
4554 | + return; |
4555 | + } |
4556 | + |
4557 | + REAL len = wall->Pos(1) - wall->Pos(0); |
4558 | + com += (wall->EndPoint(0) + wall->EndPoint(1))*len; |
4559 | + weight += len*2; |
4560 | + if( len > 0 ) |
4561 | + { |
4562 | + REAL blend = ( ( cycle.GetDistance() - totalLength - wall->Pos(0) )/len ); |
4563 | + if( blend >= 0 && blend < smallestBlend ) |
4564 | + { |
4565 | + end = wall->EndPoint(0) + wall->Vec() * blend; |
4566 | + endDir = wall->Vec(); |
4567 | + smallestBlend = blend; |
4568 | + } |
4569 | + } |
4570 | + } |
4571 | +}; |
4572 | + |
4573 | +// @param info the info to fill |
4574 | +// @param totalLenght total length of wall to assume |
4575 | +void gCycle::FillWallInfoFlexible( WallInfo & info, REAL totalLength ) const |
4576 | +{ |
4577 | + gWallInfoTemp temp; |
4578 | + |
4579 | + gNetPlayerWall * run = displayList_.wallList_; |
4580 | + while( run ) |
4581 | + { |
4582 | + temp.AddWall( run, *this, totalLength ); |
4583 | + run = run->Next(); |
4584 | + } |
4585 | + |
4586 | + run = displayList_.wallsWithDisplayList_; |
4587 | + while( run ) |
4588 | + { |
4589 | + temp.AddWall( run, *this, totalLength ); |
4590 | + run = run->Next(); |
4591 | + } |
4592 | + |
4593 | + info.tailPos = temp.end; |
4594 | + info.tailDir = temp.endDir; |
4595 | + REAL norm = info.tailDir.Norm(); |
4596 | + if( norm > 0 ) |
4597 | + info.tailDir *= 1/norm; |
4598 | + if( temp.weight > 0 ) |
4599 | + { |
4600 | + info.centerOfMass = temp.com*(1/temp.weight); |
4601 | + } |
4602 | +} |
4603 | + |
4604 | +// @param info the info to fill |
4605 | +// @param rubberRatio rubber usage ratio to assume |
4606 | +// @param offset offset value to add to the wall length |
4607 | +void gCycle::FillWallInfo( WallInfo & info, REAL rubberRatio, REAL offset ) const |
4608 | +{ |
4609 | + REAL max, effectiveness; |
4610 | + sg_RubberValues( Player(), verletSpeed_, max, effectiveness ); |
4611 | + REAL totalLength = sg_CycleWallLengthFromDist( GetDistance() ); |
4612 | + if ( totalLength > 0 ) |
4613 | + { |
4614 | + totalLength -= rubberRatio * max; |
4615 | + } |
4616 | + FillWallInfoFlexible( info, totalLength + offset ); |
4617 | +} |
4618 | + |
4619 | +// @param info the info to fill |
4620 | +void gCycle::FillWallInfo( WallInfo & info ) const |
4621 | +{ |
4622 | + FillWallInfoFlexible( info, ThisWallsLength() ); |
4623 | +} |
4624 | |
4625 | === modified file 'src/tron/gCycle.h' |
4626 | --- src/tron/gCycle.h 2011-09-03 16:16:22 +0000 |
4627 | +++ src/tron/gCycle.h 2019-01-04 23:26:28 +0000 |
4628 | @@ -117,16 +117,22 @@ |
4629 | const gCycleMovement* parent_; // the cycle that is extrapolated |
4630 | }; |
4631 | |
4632 | +class gAINavigator; |
4633 | class gCycleChatBot; |
4634 | |
4635 | -#ifndef DEDICATED |
4636 | class gCycleWallsDisplayListManager |
4637 | { |
4638 | friend class gNetPlayerWall; |
4639 | + friend class gCycle; |
4640 | |
4641 | public: |
4642 | gCycleWallsDisplayListManager(); |
4643 | |
4644 | + bool Walls() const |
4645 | + { |
4646 | + return wallList_ || wallsWithDisplayList_; |
4647 | + } |
4648 | +#ifndef DEDICATED |
4649 | //! checks whether a wall at a certain distance can have a display list |
4650 | static bool CannotHaveList( REAL distance, gCycle const * cycle ); |
4651 | |
4652 | @@ -138,23 +144,22 @@ |
4653 | |
4654 | //! render all walls |
4655 | void RenderAll( eCamera const * camera, gCycle * cycle ); |
4656 | - bool Walls() const |
4657 | - { |
4658 | - return wallList_ || wallsWithDisplayList_; |
4659 | - } |
4660 | |
4661 | void Clear( int inhibit = 0 ) |
4662 | { |
4663 | displayList_.Clear( inhibit ); |
4664 | } |
4665 | +#endif |
4666 | private: |
4667 | gNetPlayerWall * wallList_; //!< linked list of all walls |
4668 | gNetPlayerWall * wallsWithDisplayList_; //!< linked list of all walls with display list |
4669 | + |
4670 | +#ifndef DEDICATED |
4671 | rDisplayList displayList_; //!< combined display list |
4672 | REAL wallsWithDisplayListMinDistance_; //!< minimal distance of the walls with display list |
4673 | int wallsInDisplayList_; //!< number of walls in the current display list |
4674 | +#endif |
4675 | }; |
4676 | -#endif |
4677 | |
4678 | // a complete lightcycle |
4679 | class gCycle: public gCycleMovement |
4680 | @@ -172,6 +177,7 @@ |
4681 | REAL timeCameIntoView; |
4682 | |
4683 | friend class gCycleChatBot; |
4684 | + friend class gAINavigator; |
4685 | std::auto_ptr< gCycleChatBot > chatBot_; |
4686 | |
4687 | bool dropWallRequested_; //!< flag indicating that someone requested a wall drop |
4688 | @@ -211,9 +217,7 @@ |
4689 | private: |
4690 | void TransferPositionCorrectionToDistanceCorrection(); |
4691 | |
4692 | -#ifndef DEDICATED |
4693 | gCycleWallsDisplayListManager displayList_; //!< display list manager |
4694 | -#endif |
4695 | |
4696 | tCHECKED_PTR(gNetPlayerWall) currentWall; //!< the wall that currenly is attached to the cycle |
4697 | tCHECKED_PTR(gNetPlayerWall) lastWall; //!< the last wall that was attached to this cycle |
4698 | @@ -354,6 +358,22 @@ |
4699 | // virtual void AddRef(); |
4700 | // virtual void Release(); |
4701 | |
4702 | + //! information about walls |
4703 | + struct WallInfo |
4704 | + { |
4705 | + tCoord tailPos; //!< the position of the end of the walls |
4706 | + tCoord tailDir; //!< direction the tail is moving in |
4707 | + tCoord centerOfMass; //!< the center of the total walls |
4708 | + }; |
4709 | + |
4710 | + //! fills in tail info, assuming the total wall lenght is the one given |
4711 | + void FillWallInfoFlexible( WallInfo & info, REAL totalLength ) const; |
4712 | + |
4713 | + //! fills in tail info using the real wall length assuming the given rubber usage ratio |
4714 | + void FillWallInfo( WallInfo & info, REAL rubberRatio, REAL offset = 0 ) const; |
4715 | + |
4716 | + //! fills in tail info using the real wall length |
4717 | + void FillWallInfo( WallInfo & info ) const; |
4718 | private: |
4719 | static REAL wallsStayUpDelay; //!< the time the cycle walls stay up ( negative values: they stay up forever ) |
4720 | static REAL wallsLength; //!< the maximum total length of the walls |
4721 | |
4722 | === modified file 'src/tron/gCycleMovement.cpp' |
4723 | --- src/tron/gCycleMovement.cpp 2014-01-03 23:23:09 +0000 |
4724 | +++ src/tron/gCycleMovement.cpp 2019-01-04 23:26:28 +0000 |
4725 | @@ -3719,7 +3719,6 @@ |
4726 | REAL step=verletSpeed_*ts; |
4727 | tASSERT(isfinite(step)); |
4728 | |
4729 | - int numTries = 0; |
4730 | bool emergency = false; |
4731 | |
4732 | rubberSpeedFactor = 1; |
4733 | @@ -3829,7 +3828,15 @@ |
4734 | |
4735 | verletSpeed_=lastSpeed; |
4736 | acceleration=lastAcceleration; |
4737 | - return TimestepCore( rubberGetsActiveTime, false ) || TimestepCore( currentTime ); |
4738 | + if ( TimestepCore( rubberGetsActiveTime, false ) ) |
4739 | + { |
4740 | + return true; |
4741 | + } |
4742 | + |
4743 | + // inform AI of its impending doom |
4744 | + RightBeforeDeath(1); |
4745 | + |
4746 | + return TimestepCore( currentTime ); |
4747 | } |
4748 | } |
4749 | #ifdef DEDICATED |
4750 | @@ -3932,9 +3939,6 @@ |
4751 | } |
4752 | */ |
4753 | |
4754 | - // notify AIs of it |
4755 | - emergency = true; |
4756 | - |
4757 | // calculate the step the rubber code should do based on the decay factor |
4758 | // calculated earler |
4759 | REAL rubberStep = space * rubberFactor; |
4760 | @@ -3952,6 +3956,7 @@ |
4761 | |
4762 | // clamp rubberneeded to the amout of rubber available |
4763 | REAL rubberAvailable = ( rubber_granted - rubber ) * rubberEffectiveness; |
4764 | + |
4765 | if ( sn_GetNetState() != nCLIENT && rubberneeded > rubberAvailable && Vulnerable() ) |
4766 | { |
4767 | // rubber will run out this frame. |
4768 | @@ -3969,7 +3974,18 @@ |
4769 | // need many attempts |
4770 | verletSpeed_=lastSpeed; |
4771 | acceleration=lastAcceleration; |
4772 | - return TimestepCore( runOutTime, false ) || TimestepCore( currentTime ); |
4773 | + |
4774 | + // simulate until rubber has run out |
4775 | + if ( TimestepCore( runOutTime, false ) ) |
4776 | + { |
4777 | + return true; |
4778 | + } |
4779 | + |
4780 | + // inform AI of its impending doom |
4781 | + RightBeforeDeath(0); |
4782 | + |
4783 | + // simulate rest of timestep |
4784 | + return TimestepCore( currentTime ); |
4785 | } |
4786 | } |
4787 | } |
4788 | @@ -3981,11 +3997,6 @@ |
4789 | // update rubber usage |
4790 | rubber += rubberneeded / rubberEffectiveness; |
4791 | |
4792 | - numTries = int((sg_rubberCycleTime * ( rubber_granted - rubber ) - 1 )/(sg_rubberCycleTime * step*1.5 + 1)); |
4793 | - int numTriesSpace = int(space*10/verletSpeed_); |
4794 | - if ( numTriesSpace < numTries ) |
4795 | - numTriesSpace = 0; |
4796 | - |
4797 | if ( step > 0 ) |
4798 | rubberSpeedFactor = 1 - rubberneeded/step; |
4799 | else |
4800 | @@ -4063,7 +4074,6 @@ |
4801 | verletSpeed_ = lastSpeed; |
4802 | acceleration = lastAcceleration; |
4803 | currentFace = lastFace; |
4804 | - numTries = 0; |
4805 | |
4806 | // don't simulate further |
4807 | return false; |
4808 | @@ -4108,7 +4118,6 @@ |
4809 | verletSpeed_ = lastSpeed; |
4810 | acceleration = lastAcceleration; |
4811 | currentFace = lastFace; |
4812 | - numTries = 0; |
4813 | emergency = true; |
4814 | |
4815 | // don't simulate further |
4816 | @@ -4135,7 +4144,6 @@ |
4817 | if ( rubber < 0 ) |
4818 | rubber = 0; |
4819 | |
4820 | - numTries = 0; |
4821 | emergency = true; |
4822 | } |
4823 | } |
4824 | @@ -4209,7 +4217,7 @@ |
4825 | // give the AI a chance to evade just in time |
4826 | if (emergency) |
4827 | { |
4828 | - RightBeforeDeath(numTries); |
4829 | + RightBeforeDeath(0); |
4830 | } |
4831 | |
4832 | #ifdef DEBUGOUTPUT |
4833 | |
4834 | === modified file 'src/tron/gCycleMovement.h' |
4835 | --- src/tron/gCycleMovement.h 2011-07-10 18:22:34 +0000 |
4836 | +++ src/tron/gCycleMovement.h 2019-01-04 23:26:28 +0000 |
4837 | @@ -42,6 +42,10 @@ |
4838 | class gSensor; |
4839 | struct gMaxSpaceAheadHitInfo; |
4840 | |
4841 | +extern void sg_RubberValues( ePlayerNetID const * player, REAL speed, REAL & max, REAL & effectiveness ); |
4842 | +extern REAL sg_brakeCycle; |
4843 | +extern REAL sg_cycleBrakeDeplete; |
4844 | + |
4845 | //! used to clear out dangerous information from hit info after simulation is done |
4846 | class gMaxSpaceAheadHitInfoClearer |
4847 | { |
4848 | |
4849 | === modified file 'src/tron/gGame.cpp' |
4850 | --- src/tron/gGame.cpp 2013-11-23 23:37:48 +0000 |
4851 | +++ src/tron/gGame.cpp 2019-01-04 23:26:28 +0000 |
4852 | @@ -3175,8 +3175,8 @@ |
4853 | tJUST_CONTROLLED_PTR< eGameObject > e = gameObjects(i); |
4854 | if ( e ) |
4855 | { |
4856 | + e->EnsureBorn(); |
4857 | e->OnRoundBegin(); |
4858 | - e->EnsureBorn(); |
4859 | } |
4860 | } |
4861 | } |
4862 | |
4863 | === modified file 'src/tron/gSensor.h' |
4864 | --- src/tron/gSensor.h 2006-07-31 05:05:12 +0000 |
4865 | +++ src/tron/gSensor.h 2019-01-04 23:26:28 +0000 |
4866 | @@ -40,7 +40,7 @@ |
4867 | public: |
4868 | gSensorWallType type; |
4869 | |
4870 | - gSensor(eGameObject *o,const eCoord &start,const eCoord &d) |
4871 | + gSensor(eGameObject const * o,const eCoord &start,const eCoord &d) |
4872 | :eSensor(o,start,d), type(gSENSOR_NONE){} |
4873 | |
4874 | virtual void PassEdge(const eWall *w,REAL time,REAL,int =1); |
4875 | |
4876 | === modified file 'src/tron/gWall.cpp' |
4877 | --- src/tron/gWall.cpp 2013-10-28 22:02:36 +0000 |
4878 | +++ src/tron/gWall.cpp 2019-01-04 23:26:28 +0000 |
4879 | @@ -1591,13 +1591,11 @@ |
4880 | |
4881 | void gNetPlayerWall::MyInitAfterCreation() |
4882 | { |
4883 | -#ifndef DEDICATED |
4884 | // put yourself into rendering list |
4885 | if ( cycle_ ) |
4886 | { |
4887 | Insert( cycle_->displayList_.wallList_ ); |
4888 | } |
4889 | -#endif |
4890 | |
4891 | //w= |
4892 | #ifdef DEBUG |
4893 | @@ -2027,8 +2025,10 @@ |
4894 | |
4895 | eCoord gNetPlayerWall::Vec() |
4896 | { |
4897 | - if ( edge_ ) return edge_->Vec(); |
4898 | - else return eCoord(); |
4899 | + return end - beg; |
4900 | + // Odd, this code would never return something useful, but was here. |
4901 | + // if ( edge_ ) return edge_->Vec(); |
4902 | + // else return eCoord(); |
4903 | } |
4904 | |
4905 | gPlayerWall *gNetPlayerWall::Wall(){ |
4906 | |
4907 | === added file 'src/tron/zone/zAI.cpp' |
4908 | --- src/tron/zone/zAI.cpp 1970-01-01 00:00:00 +0000 |
4909 | +++ src/tron/zone/zAI.cpp 2019-01-04 23:26:28 +0000 |
4910 | @@ -0,0 +1,290 @@ |
4911 | +/* |
4912 | + |
4913 | +************************************************************************* |
4914 | + |
4915 | +ArmageTron -- Just another Tron Lightcycle Game in 3D. |
4916 | +Copyright (C) 2005 by |
4917 | +and the AA DevTeam (see the file AUTHORS(.txt) in the main source directory) |
4918 | + |
4919 | +************************************************************************** |
4920 | + |
4921 | +This program is free software; you can redistribute it and/or |
4922 | +modify it under the terms of the GNU General Public License |
4923 | +as published by the Free Software Foundation; either version 2 |
4924 | +of the License, or (at your option) any later version. |
4925 | + |
4926 | +This program is distributed in the hope that it will be useful, |
4927 | +but WITHOUT ANY WARRANTY; without even the implied warranty of |
4928 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4929 | +GNU General Public License for more details. |
4930 | + |
4931 | +You should have received a copy of the GNU General Public License |
4932 | +along with this program; if not, write to the Free Software |
4933 | +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
4934 | + |
4935 | +*************************************************************************** |
4936 | + |
4937 | +*/ |
4938 | + |
4939 | +#include "zAI.h" |
4940 | + |
4941 | +#include "zZone.h" |
4942 | +#include "zShape.h" |
4943 | +#include "gCycle.h" |
4944 | + |
4945 | +#include "eDebugLine.h" |
4946 | + |
4947 | +// ************************* |
4948 | +// * Zone attack/defense * |
4949 | +// ************************* |
4950 | + |
4951 | +//!@param cycle the cycle to control |
4952 | +//!@param zone the zone to protect/attack |
4953 | +//!@param random persistent random coordinates |
4954 | +//!@papam maxstep the maximal delay until the next thought |
4955 | +zZoneEvaluator::zZoneEvaluator( gCycle const & cycle, zZone const & zone, eCoord & random, REAL maxStep, gCycle * lastBlocker ): gAINavigator::FollowEvaluator( cycle ) |
4956 | +{ |
4957 | + Init( cycle, zone, random, maxStep, lastBlocker ); |
4958 | +} |
4959 | + |
4960 | +zZoneEvaluator::~zZoneEvaluator(){} |
4961 | + |
4962 | +void zZoneEvaluator::Init( gCycle const & cycle, zZone const & zone, eCoord & random, REAL maxStep, gCycle * lastBlocker ) |
4963 | +{ |
4964 | + zShape * shape = zone.getShape(); |
4965 | + if ( !shape ) |
4966 | + { |
4967 | + return; |
4968 | + } |
4969 | + |
4970 | + eCoord pos = cycle_.Position(); |
4971 | + eCoord center = shape->findCenter(); |
4972 | + |
4973 | + // REAL insideness = (pos-center).NormSquared()/(edge-center).NormSquared(); |
4974 | + |
4975 | + eCoord tailToChase, tailToChaseVelocity; |
4976 | + |
4977 | + if( zone.Team() == cycle.Team() ) |
4978 | + { |
4979 | + gCycle::WallInfo info; |
4980 | + |
4981 | + // don't get the real tail. Instead, get a bit in front and extrapolate back. |
4982 | + REAL endOffset = cycle.GetTurnDelay(); |
4983 | + if( maxStep > endOffset ) |
4984 | + { |
4985 | + endOffset = maxStep; |
4986 | + } |
4987 | + |
4988 | + cycle.FillWallInfo( info, .5, -endOffset*cycle.Speed() ); |
4989 | + |
4990 | + tailToChaseVelocity = info.tailDir * cycle.Speed(); |
4991 | + tailToChase = info.tailPos - tailToChaseVelocity * endOffset; |
4992 | + |
4993 | + { |
4994 | + // blend in standard circular path |
4995 | + REAL blend = 2 - cycle.GetDistance()/cycle.ThisWallsLength(); |
4996 | + if ( blend > 0 ) |
4997 | + { |
4998 | + if( blend > 1 ) |
4999 | + { |
5000 | + blend = 1; |
The diff has been truncated for viewing.
This definitely needs more work, non-fortress games are totally broken :)