Merge lp:~armagetronad-dev/armagetronad/trunk-armagetronad-fortress_ai into lp:~armagetronad-dev/armagetronad/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
Reviewer Review Type Date Requested Status
Manuel Moos Needs Fixing
Armagetron Advanced SQUAT user feedback Pending
Review via email: mp+11975@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Manuel Moos (z-man) wrote :

This definitely needs more work, non-fortress games are totally broken :)

review: Needs Fixing
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.

Subscribers

People subscribed via source and target branches

to status/vote changes: