hedgewars/uAI.pas
branchui-scaling
changeset 15288 c4fd2813b127
parent 14674 fd22b6d474e7
child 15370 fcdb6e3a9d36
equal deleted inserted replaced
13395:0135e64c6c66 15288:c4fd2813b127
    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