1 (*
2 * Hedgewars, a free turn based strategy game
3 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *)
18
19 {$INCLUDE "options.inc"}
20
21 unit uAI;
22 interface
23 uses uFloat;
24
25 procedure initModule;
26 procedure freeModule;
27
28 procedure ProcessBot;
29 procedure FreeActionsList;
30
31 implementation
32 uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions,
33 uAmmos, uTypes,
34 uVariables, uCommands, uUtils, uDebug, uAILandMarks,
35 uGearsUtils;
36
37 var BestActions: TActions;
38 CanUseAmmo: array [TAmmoType] of boolean;
39 StopThinking: boolean;
40 StartTicks: Longword;
41 ThreadSem: PSDL_Sem;
42
43 procedure FreeActionsList;
44 begin
45 AddFileLog('FreeActionsList called');
46
47 StopThinking:= true;
48 SDL_SemWait(ThreadSem);
49 SDL_SemPost(ThreadSem);
50
51 if CurrentHedgehog <> nil then
52 with CurrentHedgehog^ do
53 if Gear <> nil then
54 if BotLevel <> 0 then
55 StopMessages(Gear^.Message);
56
57 BestActions.Count:= 0;
58 BestActions.Pos:= 0
59 end;
60
61
62 const cBranchStackSize = 12;
63 type TStackEntry = record
64 MadeActions: TActions;
65 Hedgehog: TGear;
66 end;
67
68 var Stack: record
69 Count: Longword;
70 States: array[0..Pred(cBranchStackSize)] of TStackEntry;
71 end;
72
Pushnull73 function Push(const Actions: TActions; const Me: TGear; Dir: integer): boolean;
74 var bRes: boolean;
75 begin
76 bRes:= (Stack.Count < cBranchStackSize) and (Actions.Count < MAXACTIONS - 5);
77 if bRes then
78 with Stack.States[Stack.Count] do
79 begin
80 MadeActions:= Actions;
81 Hedgehog:= Me;
82 Hedgehog.Message:= Dir;
83 inc(Stack.Count)
84 end;
85 Push:= bRes
86 end;
87
88 procedure Pop(var Actions: TActions; var Me: TGear);
89 begin
90 dec(Stack.Count);
91 with Stack.States[Stack.Count] do
92 begin
93 Actions:= MadeActions;
94 Me:= Hedgehog
95 end
96 end;
97
98
99
100 procedure TestAmmos(var Actions: TActions; Me: PGear; rareChecks: boolean);
101 var BotLevel: Byte;
102 ap: TAttackParams;
103 Score, i, t, n, dAngle: LongInt;
104 a, aa: TAmmoType;
105 useThisActions: boolean;
106 begin
107 BotLevel:= Me^.Hedgehog^.BotLevel;
108 windSpeed:= hwFloat2Float(cWindSpeed);
109 useThisActions:= false;
110 Me^.AIHints:= Me^.AIHints and (not aihAmmosChanged);
111
112 for i:= 0 to Pred(Targets.Count) do
113 if (Targets.ar[i].Score >= 0) and (not StopThinking) then
114 begin
115 with Me^.Hedgehog^ do
116 a:= CurAmmoType;
117 aa:= a;
118 SDL_delay(0); // hint to let the context switch run
119 repeat
120 if (CanUseAmmo[a])
121 and ((not rareChecks) or ((AmmoTests[a].flags and amtest_Rare) = 0))
122 and ((i = 0) or ((AmmoTests[a].flags and amtest_NoTarget) = 0))
123 then
124 begin
125 {$HINTS OFF}
126 Score:= AmmoTests[a].proc(Me, Targets.ar[i], BotLevel, ap);
127 {$HINTS ON}
128 if (Score > BadTurn) and (Actions.Score + Score > BestActions.Score) then
129 if (BestActions.Score < 0) or (Actions.Score + Score > BestActions.Score + Byte(BotLevel - 1) * 2048) then
130 begin
131 if useThisActions then
132 begin
133 BestActions.Count:= Actions.Count
134 end
135 else
136 begin
137 BestActions:= Actions;
138 BestActions.isWalkingToABetterPlace:= false;
139 useThisActions:= true
140 end;
141
142 BestActions.Score:= Actions.Score + Score;
143
144 // if not between shots, activate invulnerability/vampirism if available
145 if CurrentHedgehog^.MultiShootAttacks = 0 then
146 begin
147 if (HHHasAmmo(Me^.Hedgehog^, amInvulnerable) > 0) and (Me^.Hedgehog^.Effects[heInvulnerable] = 0) then
148 begin
149 AddAction(BestActions, aia_Weapon, Longword(amInvulnerable), 80, 0, 0);
150 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0);
151 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0);
152 end;
153
154 if (HHHasAmmo(Me^.Hedgehog^, amExtraDamage) > 0) and (cDamageModifier <> _1_5) then
155 begin
156 AddAction(BestActions, aia_Weapon, Longword(amExtraDamage), 80, 0, 0);
157 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0);
158 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0);
159 end;
160 if (HHHasAmmo(Me^.Hedgehog^, amVampiric) > 0) and (not cVampiric) then
161 begin
162 AddAction(BestActions, aia_Weapon, Longword(amVampiric), 80, 0, 0);
163 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0);
164 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0);
165 end;
166 end;
167
168 AddAction(BestActions, aia_Weapon, Longword(a), 300 + random(400), 0, 0);
169
170 if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
171 begin
172 AddAction(BestActions, aia_Put, 0, 8, ap.AttackPutX, ap.AttackPutY)
173 end;
174
175 if (ap.Angle > 0) then
176 AddAction(BestActions, aia_LookRight, 0, 200, 0, 0)
177 else if (ap.Angle < 0) then
178 AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0);
179
180 if (Ammoz[a].Ammo.Propz and ammoprop_Timerable) <> 0 then
181 AddAction(BestActions, aia_Timer, ap.Time div 1000, 400, 0, 0);
182
183 if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then
184 begin
185 dAngle:= LongInt(Me^.Angle) - Abs(ap.Angle);
186 if dAngle > 0 then
187 begin
188 AddAction(BestActions, aia_Up, aim_push, 300 + random(250), 0, 0);
189 AddAction(BestActions, aia_Up, aim_release, dAngle, 0, 0)
190 end
191 else if dAngle < 0 then
192 begin
193 AddAction(BestActions, aia_Down, aim_push, 300 + random(250), 0, 0);
194 AddAction(BestActions, aia_Down, aim_release, -dAngle, 0, 0)
195 end
196 end;
197
198 if (Ammoz[a].Ammo.Propz and ammoprop_OscAim) <> 0 then
199 begin
200 AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0);
201 AddAction(BestActions, aia_attack, aim_release, 1, 0, 0);
202
203 if abs(ap.Angle) > 32 then
204 begin
205 AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0);
206 AddAction(BestActions, aia_Down, aim_release, 32, 0, 0);
207 end;
208
209 AddAction(BestActions, aia_waitAngle, ap.Angle, 250, 0, 0);
210 AddAction(BestActions, aia_attack, aim_push, 1, 0, 0);
211 AddAction(BestActions, aia_attack, aim_release, 1, 0, 0);
212 end else
213 if (Ammoz[a].Ammo.Propz and ammoprop_AttackingPut) = 0 then
214 begin
215 if (AmmoTests[a].flags and amtest_MultipleAttacks) = 0 then
216 n:= 1 else n:= ap.AttacksNum;
217
218 AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0);
219 for t:= 2 to n do
220 begin
221 AddAction(BestActions, aia_attack, aim_push, 150, 0, 0);
222 AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
223 end;
224 AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0);
225 end;
226
227 if (Ammoz[a].Ammo.Propz and ammoprop_Track) <> 0 then
228 begin
229 AddAction(BestActions, aia_waitAmmoXY, 0, 12, ap.ExplX, ap.ExplY);
230 AddAction(BestActions, aia_attack, aim_push, 1, 0, 0);
231 AddAction(BestActions, aia_attack, aim_release, 7, 0, 0);
232 end;
233
234 if ap.ExplR > 0 then
235 AddAction(BestActions, aia_AwareExpl, ap.ExplR, 10, ap.ExplX, ap.ExplY);
236 end
237 end;
238 if a = High(TAmmoType) then
239 a:= Low(TAmmoType)
240 else inc(a)
241 until (a = aa) or (CurrentHedgehog^.MultiShootAttacks > 0) {shooting same weapon}
242 or StopThinking
243 end
244 end;
245
246 procedure Walk(Me: PGear; var Actions: TActions);
247 const FallPixForBranching = cHHRadius;
248 var
249 maxticks, oldticks, steps, tmp: Longword;
250 BaseRate, BestRate, Rate: LongInt;
251 GoInfo: TGoInfo;
252 CanGo: boolean;
253 AltMe: TGear;
254 BotLevel: Byte;
255 a: TAmmoType;
256 isAfterAttack: boolean;
257 begin
258 Actions.ticks:= 0;
259 oldticks:= 0; // avoid compiler hint
260 Stack.Count:= 0;
261
262 clearAllMarks;
263
264 for a:= Low(TAmmoType) to High(TAmmoType) do
265 CanUseAmmo[a]:= Assigned(AmmoTests[a].proc) and (HHHasAmmo(Me^.Hedgehog^, a) > 0);
266
267 BotLevel:= Me^.Hedgehog^.BotLevel;
268
269 isAfterAttack:= ((Me^.State and gstAttacked) <> 0) and ((GameFlags and gfInfAttack) = 0);
270 if isAfterAttack then
271 maxticks:= Max(0, TurnTimeLeft - 500)
272 else
273 maxticks:= Max(0, TurnTimeLeft - 5000 - LongWord(4000 * BotLevel));
274
275 if not isAfterAttack then
276 TestAmmos(Actions, Me, false);
277
278 BestRate:= RatePlace(Me);
279 BaseRate:= Max(BestRate, 0);
280
281 // switch to 'skip' if we cannot move because of mouse cursor being shown
282 if (Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
283 AddAction(Actions, aia_Weapon, Longword(amSkip), 100 + random(200), 0, 0);
284
285 if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0))
286 and (CurrentHedgehog^.Effects[heArtillery] = 0) and (cGravityf <> 0) then
287 begin
288 tmp:= random(2) + 1;
289 Push(Actions, Me^, tmp);
290 Push(Actions, Me^, tmp xor 3);
291
292 while (Stack.Count > 0) and (not StopThinking) do
293 begin
294 Pop(Actions, Me^);
295
296 AddAction(Actions, Me^.Message, aim_push, 250, 0, 0);
297 if (Me^.Message and gmLeft) <> 0 then
298 AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0)
299 else
300 AddAction(Actions, aia_WaitXR, hwRound(Me^.X), 0, 0, 0);
301
302 steps:= 0;
303
304 while (not StopThinking) do
305 begin
306 {$HINTS OFF}
307 CanGo:= HHGo(Me, @AltMe, GoInfo);
308 {$HINTS ON}
309 oldticks:= Actions.ticks;
310 inc(Actions.ticks, GoInfo.Ticks);
311 if (Actions.ticks > maxticks) or (TurnTimeLeft < BestActions.ticks + 5000) then
312 begin
313 if (BotLevel < 5)
314 and (not isAfterAttack)
315 and (BestActions.Score > 0) // we have a good move
316 and (TurnTimeLeft < BestActions.ticks + 5000) // we won't have a lot of time after attack
317 and (HHHasAmmo(Me^.Hedgehog^, amExtraTime) > 0) // but can use extra time
318 then
319 begin
320 BestActions.Count:= 0;
321 AddAction(BestActions, aia_Weapon, Longword(amExtraTime), 80, 0, 0);
322 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0);
323 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0);
324 end;
325
326 break;
327 end;
328
329 if (BotLevel < 5)
330 and (GoInfo.JumpType = jmpHJump)
331 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped))
332 then // hjump support
333 begin
334 // check if we could go backwards and maybe ljump over a gap after this hjump
335 addMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped);
336 if Push(Actions, AltMe, Me^.Message xor 3) then
337 begin
338 with Stack.States[Pred(Stack.Count)] do
339 begin
340 if (Me^.Message and gmLeft) <> 0 then
341 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0)
342 else
343 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0);
344
345 AddAction(MadeActions, aia_HJump, 0, 305 + random(50), 0, 0);
346 AddAction(MadeActions, aia_HJump, 0, 350, 0, 0);
347 end;
348 // but first check walking forward
349 Push(Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message)
350 end;
351 end;
352 if (BotLevel < 3)
353 and (GoInfo.JumpType = jmpLJump)
354 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped))
355 then // ljump support
356 begin
357 addMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped);
358 // at final check where we go after jump walking backward
359 if Push(Actions, AltMe, Me^.Message xor 3) then
360 with Stack.States[Pred(Stack.Count)] do
361 begin
362 if (Me^.Message and gmLeft) <> 0 then
363 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0)
364 else
365 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0);
366
367 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0);
368 end;
369
370 // push current position so we proceed from it after checking jump+forward walk opportunities
371 if CanGo then Push(Actions, Me^, Me^.Message);
372
373 // first check where we go after jump walking forward
374 if Push(Actions, AltMe, Me^.Message) then
375 with Stack.States[Pred(Stack.Count)] do
376 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0);
377
378 break
379 end;
380
381 // 'not CanGO' means we cannot go straight, possible jumps are checked above
382 if not CanGo then
383 break;
384
385 inc(steps);
386 Actions.actions[Pred(Actions.Count)].Param:= hwRound(Me^.X);
387 Rate:= RatePlace(Me);
388 if Rate > BestRate then
389 begin
390 BestActions:= Actions;
391 BestActions.isWalkingToABetterPlace:= true;
392 BestRate:= Rate;
393 isAfterAttack:= true // we have better place, go there and do not use ammo
394 end
395 else if Rate < BestRate then
396 break;
397
398 if (not isAfterAttack) and ((steps mod 4) = 0) then
399 begin
400 if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere) then
401 break;
402 addMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere);
403
404 TestAmmos(Actions, Me, Actions.ticks shr 12 = oldticks shr 12);
405 end;
406
407 if GoInfo.FallPix >= FallPixForBranching then
408 Push(Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right
409 end {while};
410
411 if BestRate > BaseRate then
412 exit
413 end {while}
414 end {if}
415 end;
416
417 function Think(Me: PGear): LongInt; cdecl; export;
418 var BackMe, WalkMe: TGear;
419 switchCount: LongInt;
420 currHedgehogIndex, itHedgehog, switchesNum, i: Longword;
421 switchImmediatelyAvailable: boolean;
422 Actions: TActions;
423 begin
424 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent;
425 StartTicks:= GameTicks;
426
427 currHedgehogIndex:= CurrentTeam^.CurrHedgehog;
428 itHedgehog:= currHedgehogIndex;
429 switchesNum:= 0;
430
431 switchImmediatelyAvailable:= (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtSwitcher);
432 if Me^.Hedgehog^.BotLevel <> 5 then
433 switchCount:= HHHasAmmo(PGear(Me)^.Hedgehog^, amSwitch)
434 else switchCount:= 0;
435
436 if ((Me^.State and gstAttacked) = 0) or isInMultiShoot or bonuses.activity or ((Me^.AIHints and aihAmmosChanged) <> 0) then
437 if Targets.Count > 0 then
438 begin
439 // iterate over current team hedgehogs
440 repeat
441 WalkMe:= CurrentTeam^.Hedgehogs[itHedgehog].Gear^;
442
443 Actions.Count:= 0;
444 Actions.Pos:= 0;
445 Actions.Score:= 0;
446 if switchesNum > 0 then
447 begin
448 if (not switchImmediatelyAvailable) then
449 begin
450 // when AI has to use switcher, make it cost smth unless they have a lot of switches
451 if (switchCount < 10) then Actions.Score:= (-27+switchCount*3)*4000;
452 AddAction(Actions, aia_Weapon, Longword(amSwitch), 300 + random(200), 0, 0);
453 AddAction(Actions, aia_attack, aim_push, 300 + random(300), 0, 0);
454 AddAction(Actions, aia_attack, aim_release, 1, 0, 0);
455 end;
456 for i:= 1 to switchesNum do
457 AddAction(Actions, aia_Switch, 0, 300 + random(200), 0, 0);
458 end;
459 Walk(@WalkMe, Actions);
460
461 // find another hog in team
462 repeat
463 itHedgehog:= Succ(itHedgehog) mod CurrentTeam^.HedgehogsNumber;
464 until (itHedgehog = currHedgehogIndex) or ((CurrentTeam^.Hedgehogs[itHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[itHedgehog].Effects[heFrozen]=0));
465
466 inc(switchesNum);
467 until (not (switchImmediatelyAvailable or (switchCount > 0)))
468 or StopThinking
469 or (itHedgehog = currHedgehogIndex)
470 or BestActions.isWalkingToABetterPlace;
471
472 if (StartTicks > GameTicks - 1500) and (not StopThinking) then
473 SDL_Delay(700);
474
475 if (BestActions.Score < -1023) and (not BestActions.isWalkingToABetterPlace) then
476 begin
477 BestActions.Count:= 0;
478
479 FillBonuses(false);
480
481 // Hog has no idea what to do. Use tardis or skip
482 if (not bonuses.activity) and ((Me^.AIHints and aihAmmosChanged) = 0) then
483 if (((GameFlags and gfInfAttack) <> 0) or (CurrentHedgehog^.MultiShootAttacks = 0)) and (HHHasAmmo(Me^.Hedgehog^, amTardis) > 0) and (CanUseTardis(Me^.Hedgehog^.Gear)) and (random(4) < 3) then
484 // Tardis brings hog to a random place. Perfect for clueless AI
485 begin
486 AddAction(BestActions, aia_Weapon, Longword(amTardis), 80, 0, 0);
487 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0);
488 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0);
489 end
490 else
491 AddAction(BestActions, aia_Skip, 0, 250, 0, 0);
492 Me^.AIHints := ME^.AIHints and (not aihAmmosChanged);
493 end;
494
495 end else SDL_Delay(100)
496 else
497 begin
498 BackMe:= Me^;
499 i:= 4;
500 while (not StopThinking) and (BestActions.Count = 0) and (i > 0) do
501 begin
502
503 (*
504 // Maybe this would get a bit of movement out of them? Hopefully not *toward* water. Need to check how often he'd choose that strategy
505 if SuddenDeathDmg and ((hwRound(BackMe.Y)+cWaterRise*2) > cWaterLine) then
506 AddBonus(hwRound(BackMe.X), hwRound(BackMe.Y), 250, -40);
507 *)
508
509 FillBonuses(true);
510 WalkMe:= BackMe;
511 Actions.Count:= 0;
512 Actions.Pos:= 0;
513 Actions.Score:= 0;
514 Walk(@WalkMe, Actions);
515 if not bonuses.activity then dec(i);
516 if not StopThinking then
517 SDL_Delay(100)
518 end
519 end;
520
521 Me^.State:= Me^.State and (not gstHHThinking);
522 Think:= 0;
523 SDL_SemPost(ThreadSem);
524 end;
525
526 procedure StartThink(Me: PGear);
527 var ThinkThread: PSDL_Thread;
528 begin
529 if ((Me^.State and (gstAttacking or gstHHJumping or gstMoving)) <> 0)
530 or isInMultiShoot then
531 exit;
532
533 SDL_SemWait(ThreadSem);
534 //DeleteCI(Me); // this will break demo/netplay
535
536 Me^.State:= Me^.State or gstHHThinking;
537 Me^.Message:= 0;
538
539 BestActions.Count:= 0;
540 BestActions.Pos:= 0;
541 BestActions.Score:= Low(LongInt);
542 BestActions.isWalkingToABetterPlace:= false;
543
544 StopThinking:= false;
545 ThinkingHH:= Me;
546
547 FillTargets;
548 if Targets.Count = 0 then
549 begin
550 OutError('AI: no targets!?', false);
551 exit
552 end;
553
554 FillBonuses(((Me^.State and gstAttacked) <> 0) and (not isInMultiShoot) and ((GameFlags and gfInfAttack) = 0));
555
556 ThinkThread:= SDL_CreateThread(@Think, PChar('think'), Me);
557 SDL_DetachThread(ThinkThread);
558 end;
559
560 {$IFDEF DEBUGAI}
561 var scoreShown: boolean = false;
562 {$ENDIF}
563
564 procedure ProcessBot;
565 const cStopThinkTime = 40;
566 begin
567 with CurrentHedgehog^ do
568 if (Gear <> nil)
569 and ((Gear^.State and gstHHDriven) <> 0)
570 and ((TurnTimeLeft < cHedgehogTurnTime - 50) or (TurnTimeLeft > cHedgehogTurnTime)) then
571 if ((Gear^.State and gstHHThinking) = 0) then
572 if (BestActions.Pos >= BestActions.Count)
573 and (TurnTimeLeft > cStopThinkTime) then
574 begin
575 if Gear^.Message <> 0 then
576 begin
577 StopMessages(Gear^.Message);
578 if checkFails((Gear^.Message and gmAllStoppable) = 0, 'Engine bug: AI may break demos playing', true) then exit;
579 end;
580
581 if Gear^.Message <> 0 then
582 exit;
583
584 {$IFDEF DEBUGAI}
585 scoreShown:= false;
586 {$ENDIF}
587 StartThink(Gear);
588 StartTicks:= GameTicks
589
590 end else
591 begin
592 {$IFDEF DEBUGAI}
593 if not scoreShown then
594 begin
595 if BestActions.Score > 0 then ParseCommand('/say Expected score = ' + inttostr(BestActions.Score div 1024), true);
596 scoreShown:= true
597 end;
598 {$ENDIF}
599 ProcessAction(BestActions, Gear)
600 end
601 else if ((GameTicks - StartTicks) > cMaxAIThinkTime)
602 or (TurnTimeLeft <= cStopThinkTime) then
603 StopThinking:= true
604 end;
605
606 procedure initModule;
607 begin
608 StartTicks:= 0;
609 ThreadSem:= SDL_CreateSemaphore(1);
610 end;
611
612 procedure freeModule;
613 begin
614 FreeActionsList();
615 SDL_DestroySemaphore(ThreadSem);
616 end;
617
618 end.
619