64 end; |
64 end; |
65 |
65 |
66 |
66 |
67 const cBranchStackSize = 12; |
67 const cBranchStackSize = 12; |
68 type TStackEntry = record |
68 type TStackEntry = record |
69 WastedTicks: Longword; |
|
70 MadeActions: TActions; |
69 MadeActions: TActions; |
71 Hedgehog: TGear; |
70 Hedgehog: TGear; |
72 end; |
71 end; |
73 |
72 |
74 var Stack: record |
73 var Stack: record |
75 Count: Longword; |
74 Count: Longword; |
76 States: array[0..Pred(cBranchStackSize)] of TStackEntry; |
75 States: array[0..Pred(cBranchStackSize)] of TStackEntry; |
77 end; |
76 end; |
78 |
77 |
79 function Push(Ticks: Longword; const Actions: TActions; const Me: TGear; Dir: integer): boolean; |
78 function Push(const Actions: TActions; const Me: TGear; Dir: integer): boolean; |
80 var bRes: boolean; |
79 var bRes: boolean; |
81 begin |
80 begin |
82 bRes:= (Stack.Count < cBranchStackSize) and (Actions.Count < MAXACTIONS - 5); |
81 bRes:= (Stack.Count < cBranchStackSize) and (Actions.Count < MAXACTIONS - 5); |
83 if bRes then |
82 if bRes then |
84 with Stack.States[Stack.Count] do |
83 with Stack.States[Stack.Count] do |
85 begin |
84 begin |
86 WastedTicks:= Ticks; |
|
87 MadeActions:= Actions; |
85 MadeActions:= Actions; |
88 Hedgehog:= Me; |
86 Hedgehog:= Me; |
89 Hedgehog.Message:= Dir; |
87 Hedgehog.Message:= Dir; |
90 inc(Stack.Count) |
88 inc(Stack.Count) |
91 end; |
89 end; |
92 Push:= bRes |
90 Push:= bRes |
93 end; |
91 end; |
94 |
92 |
95 procedure Pop(var Ticks: Longword; var Actions: TActions; var Me: TGear); |
93 procedure Pop(var Actions: TActions; var Me: TGear); |
96 begin |
94 begin |
97 dec(Stack.Count); |
95 dec(Stack.Count); |
98 with Stack.States[Stack.Count] do |
96 with Stack.States[Stack.Count] do |
99 begin |
97 begin |
100 Ticks:= WastedTicks; |
|
101 Actions:= MadeActions; |
98 Actions:= MadeActions; |
102 Me:= Hedgehog |
99 Me:= Hedgehog |
103 end |
100 end |
104 end; |
101 end; |
105 |
102 |
251 end; |
248 end; |
252 |
249 |
253 procedure Walk(Me: PGear; var Actions: TActions); |
250 procedure Walk(Me: PGear; var Actions: TActions); |
254 const FallPixForBranching = cHHRadius; |
251 const FallPixForBranching = cHHRadius; |
255 var |
252 var |
256 ticks, maxticks, oldticks, steps, tmp: Longword; |
253 maxticks, oldticks, steps, tmp: Longword; |
257 BaseRate, BestRate, Rate: integer; |
254 BaseRate, BestRate, Rate: LongInt; |
258 GoInfo: TGoInfo; |
255 GoInfo: TGoInfo; |
259 CanGo: boolean; |
256 CanGo: boolean; |
260 AltMe: TGear; |
257 AltMe: TGear; |
261 BotLevel: Byte; |
258 BotLevel: Byte; |
262 a: TAmmoType; |
259 a: TAmmoType; |
263 begin |
260 isAfterAttack: boolean; |
264 ticks:= 0; |
261 begin |
|
262 Actions.ticks:= 0; |
265 oldticks:= 0; // avoid compiler hint |
263 oldticks:= 0; // avoid compiler hint |
266 Stack.Count:= 0; |
264 Stack.Count:= 0; |
267 |
265 |
268 clearAllMarks; |
266 clearAllMarks; |
269 |
267 |
270 for a:= Low(TAmmoType) to High(TAmmoType) do |
268 for a:= Low(TAmmoType) to High(TAmmoType) do |
271 CanUseAmmo[a]:= Assigned(AmmoTests[a].proc) and (HHHasAmmo(Me^.Hedgehog^, a) > 0); |
269 CanUseAmmo[a]:= Assigned(AmmoTests[a].proc) and (HHHasAmmo(Me^.Hedgehog^, a) > 0); |
272 |
270 |
273 BotLevel:= Me^.Hedgehog^.BotLevel; |
271 BotLevel:= Me^.Hedgehog^.BotLevel; |
274 |
272 |
275 if (Me^.State and gstAttacked) = 0 then |
273 isAfterAttack:= ((Me^.State and gstAttacked) <> 0) and ((GameFlags and gfInfAttack) = 0); |
276 maxticks:= Max(0, TurnTimeLeft - 5000 - LongWord(4000 * BotLevel)) |
274 if isAfterAttack then |
|
275 maxticks:= Max(0, TurnTimeLeft - 500) |
277 else |
276 else |
278 maxticks:= TurnTimeLeft; |
277 maxticks:= Max(0, TurnTimeLeft - 5000 - LongWord(4000 * BotLevel)); |
279 |
278 |
280 if (Me^.State and gstAttacked) = 0 then |
279 if not isAfterAttack then |
281 TestAmmos(Actions, Me, false); |
280 TestAmmos(Actions, Me, false); |
282 |
281 |
283 BestRate:= RatePlace(Me); |
282 BestRate:= RatePlace(Me); |
284 BaseRate:= Max(BestRate, 0); |
283 BaseRate:= Max(BestRate, 0); |
285 |
284 |
289 |
288 |
290 if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0)) |
289 if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0)) |
291 and (CurrentHedgehog^.Effects[heArtillery] = 0) and (cGravityf <> 0) then |
290 and (CurrentHedgehog^.Effects[heArtillery] = 0) and (cGravityf <> 0) then |
292 begin |
291 begin |
293 tmp:= random(2) + 1; |
292 tmp:= random(2) + 1; |
294 Push(0, Actions, Me^, tmp); |
293 Push(Actions, Me^, tmp); |
295 Push(0, Actions, Me^, tmp xor 3); |
294 Push(Actions, Me^, tmp xor 3); |
296 |
295 |
297 while (Stack.Count > 0) and (not StopThinking) do |
296 while (Stack.Count > 0) and (not StopThinking) do |
298 begin |
297 begin |
299 Pop(ticks, Actions, Me^); |
298 Pop(Actions, Me^); |
300 |
299 |
301 AddAction(Actions, Me^.Message, aim_push, 250, 0, 0); |
300 AddAction(Actions, Me^.Message, aim_push, 250, 0, 0); |
302 if (Me^.Message and gmLeft) <> 0 then |
301 if (Me^.Message and gmLeft) <> 0 then |
303 AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0) |
302 AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0) |
304 else |
303 else |
309 while (not StopThinking) do |
308 while (not StopThinking) do |
310 begin |
309 begin |
311 {$HINTS OFF} |
310 {$HINTS OFF} |
312 CanGo:= HHGo(Me, @AltMe, GoInfo); |
311 CanGo:= HHGo(Me, @AltMe, GoInfo); |
313 {$HINTS ON} |
312 {$HINTS ON} |
314 oldticks:= ticks; |
313 oldticks:= Actions.ticks; |
315 inc(ticks, GoInfo.Ticks); |
314 inc(Actions.ticks, GoInfo.Ticks); |
316 if ticks > maxticks then |
315 if (Actions.ticks > maxticks) or (TurnTimeLeft < BestActions.ticks + 5000) then |
|
316 begin |
|
317 if (BotLevel < 5) |
|
318 and (not isAfterAttack) |
|
319 and (BestActions.Score > 0) // we have a good move |
|
320 and (TurnTimeLeft < BestActions.ticks + 5000) // we won't have a lot of time after attack |
|
321 and (HHHasAmmo(Me^.Hedgehog^, amExtraTime) > 0) // but can use extra time |
|
322 then |
|
323 begin |
|
324 BestActions.Count:= 0; |
|
325 AddAction(BestActions, aia_Weapon, Longword(amExtraTime), 80, 0, 0); |
|
326 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
|
327 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
|
328 end; |
|
329 |
317 break; |
330 break; |
|
331 end; |
318 |
332 |
319 if (BotLevel < 5) |
333 if (BotLevel < 5) |
320 and (GoInfo.JumpType = jmpHJump) |
334 and (GoInfo.JumpType = jmpHJump) |
321 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped)) |
335 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped)) |
322 then // hjump support |
336 then // hjump support |
323 begin |
337 begin |
324 // check if we could go backwards and maybe ljump over a gap after this hjump |
338 // check if we could go backwards and maybe ljump over a gap after this hjump |
325 addMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped); |
339 addMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped); |
326 if Push(ticks, Actions, AltMe, Me^.Message xor 3) then |
340 if Push(Actions, AltMe, Me^.Message xor 3) then |
327 begin |
341 begin |
328 with Stack.States[Pred(Stack.Count)] do |
342 with Stack.States[Pred(Stack.Count)] do |
329 begin |
343 begin |
330 if (Me^.Message and gmLeft) <> 0 then |
344 if (Me^.Message and gmLeft) <> 0 then |
331 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0) |
345 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0) |
334 |
348 |
335 AddAction(MadeActions, aia_HJump, 0, 305 + random(50), 0, 0); |
349 AddAction(MadeActions, aia_HJump, 0, 305 + random(50), 0, 0); |
336 AddAction(MadeActions, aia_HJump, 0, 350, 0, 0); |
350 AddAction(MadeActions, aia_HJump, 0, 350, 0, 0); |
337 end; |
351 end; |
338 // but first check walking forward |
352 // but first check walking forward |
339 Push(ticks, Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message) |
353 Push(Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message) |
340 end; |
354 end; |
341 end; |
355 end; |
342 if (BotLevel < 3) |
356 if (BotLevel < 3) |
343 and (GoInfo.JumpType = jmpLJump) |
357 and (GoInfo.JumpType = jmpLJump) |
344 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped)) |
358 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped)) |
345 then // ljump support |
359 then // ljump support |
346 begin |
360 begin |
347 addMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped); |
361 addMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped); |
348 // at final check where we go after jump walking backward |
362 // at final check where we go after jump walking backward |
349 if Push(ticks, Actions, AltMe, Me^.Message xor 3) then |
363 if Push(Actions, AltMe, Me^.Message xor 3) then |
350 with Stack.States[Pred(Stack.Count)] do |
364 with Stack.States[Pred(Stack.Count)] do |
351 begin |
365 begin |
352 if (Me^.Message and gmLeft) <> 0 then |
366 if (Me^.Message and gmLeft) <> 0 then |
353 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0) |
367 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0) |
354 else |
368 else |
356 |
370 |
357 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
371 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
358 end; |
372 end; |
359 |
373 |
360 // push current position so we proceed from it after checking jump+forward walk opportunities |
374 // push current position so we proceed from it after checking jump+forward walk opportunities |
361 if CanGo then Push(ticks, Actions, Me^, Me^.Message); |
375 if CanGo then Push(Actions, Me^, Me^.Message); |
362 |
376 |
363 // first check where we go after jump walking forward |
377 // first check where we go after jump walking forward |
364 if Push(ticks, Actions, AltMe, Me^.Message) then |
378 if Push(Actions, AltMe, Me^.Message) then |
365 with Stack.States[Pred(Stack.Count)] do |
379 with Stack.States[Pred(Stack.Count)] do |
366 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
380 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
|
381 |
367 break |
382 break |
368 end; |
383 end; |
369 |
384 |
370 // 'not CanGO' means we cannot go straight, possible jumps are checked above |
385 // 'not CanGO' means we cannot go straight, possible jumps are checked above |
371 if not CanGo then |
386 if not CanGo then |
377 if Rate > BestRate then |
392 if Rate > BestRate then |
378 begin |
393 begin |
379 BestActions:= Actions; |
394 BestActions:= Actions; |
380 BestActions.isWalkingToABetterPlace:= true; |
395 BestActions.isWalkingToABetterPlace:= true; |
381 BestRate:= Rate; |
396 BestRate:= Rate; |
382 Me^.State:= Me^.State or gstAttacked // we have better place, go there and do not use ammo |
397 isAfterAttack:= true // we have better place, go there and do not use ammo |
383 end |
398 end |
384 else if Rate < BestRate then |
399 else if Rate < BestRate then |
385 break; |
400 break; |
386 |
401 |
387 if ((Me^.State and gstAttacked) = 0) and ((steps mod 4) = 0) then |
402 if (not isAfterAttack) and ((steps mod 4) = 0) then |
388 begin |
403 begin |
389 if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere) then |
404 if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere) then |
390 break; |
405 break; |
391 addMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere); |
406 addMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere); |
392 |
407 |
393 TestAmmos(Actions, Me, ticks shr 12 = oldticks shr 12); |
408 TestAmmos(Actions, Me, Actions.ticks shr 12 = oldticks shr 12); |
394 |
|
395 end; |
409 end; |
396 |
410 |
397 if GoInfo.FallPix >= FallPixForBranching then |
411 if GoInfo.FallPix >= FallPixForBranching then |
398 Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right |
412 Push(Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right |
399 |
|
400 if (StartTicks > GameTicks - 1500) and (not StopThinking) then |
|
401 SDL_Delay(1000); |
|
402 |
|
403 end {while}; |
413 end {while}; |
404 |
414 |
405 if BestRate > BaseRate then |
415 if BestRate > BaseRate then |
406 exit |
416 exit |
407 end {while} |
417 end {while} |
461 until (not (switchImmediatelyAvailable or (switchCount > 0))) |
471 until (not (switchImmediatelyAvailable or (switchCount > 0))) |
462 or StopThinking |
472 or StopThinking |
463 or (itHedgehog = currHedgehogIndex) |
473 or (itHedgehog = currHedgehogIndex) |
464 or BestActions.isWalkingToABetterPlace; |
474 or BestActions.isWalkingToABetterPlace; |
465 |
475 |
466 if (StartTicks > GameTicks - 1500) and (not StopThinking) then |
476 if (StartTicks > GameTicks - 1500) and (not StopThinking) then |
467 SDL_Delay(700); |
477 SDL_Delay(700); |
468 |
478 |
469 if (BestActions.Score < -1023) and (not BestActions.isWalkingToABetterPlace) then |
479 if (BestActions.Score < -1023) and (not BestActions.isWalkingToABetterPlace) then |
470 begin |
480 begin |
471 BestActions.Count:= 0; |
481 BestActions.Count:= 0; |
472 |
482 |
473 FillBonuses(false); |
483 FillBonuses(false); |
474 |
484 |
475 // Hog has no idea what to do. Use tardis or skip |
485 // Hog has no idea what to do. Use tardis or skip |
476 if not bonuses.activity then |
486 if not bonuses.activity then |
477 if (((GameFlags and gfInfAttack) <> 0) or (not isInMultiShoot)) and ((HHHasAmmo(Me^.Hedgehog^, amTardis) > 0)) and (CanUseTardis(Me^.Hedgehog^.Gear)) and (random(4) < 3) then |
487 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 |
478 // Tardis brings hog to a random place. Perfect for clueless AI |
488 // Tardis brings hog to a random place. Perfect for clueless AI |
479 begin |
489 begin |
480 AddAction(BestActions, aia_Weapon, Longword(amTardis), 80, 0, 0); |
490 AddAction(BestActions, aia_Weapon, Longword(amTardis), 80, 0, 0); |
481 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
491 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
482 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
492 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
542 begin |
552 begin |
543 OutError('AI: no targets!?', false); |
553 OutError('AI: no targets!?', false); |
544 exit |
554 exit |
545 end; |
555 end; |
546 |
556 |
547 FillBonuses(((Me^.State and gstAttacked) <> 0) and (not isInMultiShoot)); |
557 FillBonuses(((Me^.State and gstAttacked) <> 0) and (not isInMultiShoot) and ((GameFlags and gfInfAttack) = 0)); |
548 |
558 |
549 SDL_LockMutex(ThreadLock); |
559 SDL_LockMutex(ThreadLock); |
550 ThinkThread:= SDL_CreateThread(@Think, PChar('think'), Me); |
560 ThinkThread:= SDL_CreateThread(@Think, PChar('think'), Me); |
551 SDL_UnlockMutex(ThreadLock); |
561 SDL_UnlockMutex(ThreadLock); |
552 end; |
562 end; |
559 const cStopThinkTime = 40; |
569 const cStopThinkTime = 40; |
560 begin |
570 begin |
561 with CurrentHedgehog^ do |
571 with CurrentHedgehog^ do |
562 if (Gear <> nil) |
572 if (Gear <> nil) |
563 and ((Gear^.State and gstHHDriven) <> 0) |
573 and ((Gear^.State and gstHHDriven) <> 0) |
564 and (TurnTimeLeft < cHedgehogTurnTime - 50) then |
574 and ((TurnTimeLeft < cHedgehogTurnTime - 50) or (TurnTimeLeft > cHedgehogTurnTime)) then |
565 if ((Gear^.State and gstHHThinking) = 0) then |
575 if ((Gear^.State and gstHHThinking) = 0) then |
566 if (BestActions.Pos >= BestActions.Count) |
576 if (BestActions.Pos >= BestActions.Count) |
567 and (TurnTimeLeft > cStopThinkTime) then |
577 and (TurnTimeLeft > cStopThinkTime) then |
568 begin |
578 begin |
569 if Gear^.Message <> 0 then |
579 if Gear^.Message <> 0 then |