hedgewars/uAI.pas
changeset 433 9f8f22094c0e
parent 425 a7e1dabc8fb7
child 434 2c6ccce17f39
equal deleted inserted replaced
432:b0f693024b50 433:9f8f22094c0e
    18 
    18 
    19 unit uAI;
    19 unit uAI;
    20 interface
    20 interface
    21 uses uFloat;
    21 uses uFloat;
    22 {$INCLUDE options.inc}
    22 {$INCLUDE options.inc}
    23 procedure ProcessBot(FrameNo: Longword);
    23 procedure ProcessBot;
    24 procedure FreeActionsList;
    24 procedure FreeActionsList;
    25 
    25 
    26 implementation
    26 implementation
    27 uses uTeams, uConsts, SDLh, uAIMisc, uGears, uAIAmmoTests, uAIActions, uMisc,
    27 uses uTeams, uConsts, SDLh, uAIMisc, uGears, uAIAmmoTests, uAIActions, uMisc,
    28      uAIThinkStack, uAmmos;
    28      uAmmos;
    29 
    29 
    30 var BestActions: TActions;
    30 var BestActions: TActions;
       
    31     ThinkThread: PSDL_Thread = nil;
       
    32     StopThinking: boolean;
    31     CanUseAmmo: array [TAmmoType] of boolean;
    33     CanUseAmmo: array [TAmmoType] of boolean;
    32     AIThinkStart: Longword;
       
    33     isThinking: boolean = false;
       
    34 
    34 
    35 procedure FreeActionsList;
    35 procedure FreeActionsList;
    36 begin
    36 begin
    37 isThinking:= false;
    37 {$IFDEF DEBUGFILE}AddFileLog('FreeActionsList called');{$ENDIF}
       
    38 if ThinkThread <> nil then
       
    39    begin
       
    40    {$IFDEF DEBUGFILE}AddFileLog('Waiting AI thread to finish');{$ENDIF}
       
    41    StopThinking:= true;
       
    42    SDL_WaitThread(ThinkThread, nil);
       
    43    ThinkThread:= nil
       
    44    end;
    38 BestActions.Count:= 0;
    45 BestActions.Count:= 0;
    39 BestActions.Pos:= 0
    46 BestActions.Pos:= 0
    40 end;
    47 end;
    41 
    48 
    42 procedure TestAmmos(var Actions: TActions; Me: PGear);
    49 procedure TestAmmos(var Actions: TActions; Me: PGear);
    44     Angle, Power, Score, ExplX, ExplY, ExplR: LongInt;
    51     Angle, Power, Score, ExplX, ExplY, ExplR: LongInt;
    45     i: LongInt;
    52     i: LongInt;
    46     a, aa: TAmmoType;
    53     a, aa: TAmmoType;
    47 begin
    54 begin
    48 BotLevel:= PHedgehog(Me^.Hedgehog)^.BotLevel;
    55 BotLevel:= PHedgehog(Me^.Hedgehog)^.BotLevel;
       
    56 
    49 for i:= 0 to Pred(Targets.Count) do
    57 for i:= 0 to Pred(Targets.Count) do
    50     if (Targets.ar[i].Score >= 0) then
    58     if (Targets.ar[i].Score >= 0) then
    51        begin
    59        begin
    52        with CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog] do
    60        with CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog] do
    53             a:= Ammo^[CurSlot, CurAmmo].AmmoType;
    61             a:= Ammo^[CurSlot, CurAmmo].AmmoType;
    65               if Time <> 0 then AddAction(BestActions, aia_Timer, Time div 1000, 400, 0, 0);
    73               if Time <> 0 then AddAction(BestActions, aia_Timer, Time div 1000, 400, 0, 0);
    66               if (Angle > 0) then AddAction(BestActions, aia_LookRight, 0, 200, 0, 0)
    74               if (Angle > 0) then AddAction(BestActions, aia_LookRight, 0, 200, 0, 0)
    67               else if (Angle < 0) then AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0);
    75               else if (Angle < 0) then AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0);
    68               if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then
    76               if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then
    69                  begin
    77                  begin
    70                  Angle:= LongInt(Me^.Angle) - Abs(Angle);
    78                  Angle:= integer(Me^.Angle) - Abs(Angle);
    71                  if Angle > 0 then
    79                  if Angle > 0 then
    72                     begin
    80                     begin
    73                     AddAction(BestActions, aia_Up, aim_push, 500, 0, 0);
    81                     AddAction(BestActions, aia_Up, aim_push, 500, 0, 0);
    74                     AddAction(BestActions, aia_Up, aim_release, Angle, 0, 0)
    82                     AddAction(BestActions, aia_Up, aim_release, Angle, 0, 0)
    75                     end else if Angle < 0 then
    83                     end else if Angle < 0 then
    84                  AddAction(BestActions, aia_AwareExpl, ExplR, 10, ExplX, ExplY);
    92                  AddAction(BestActions, aia_AwareExpl, ExplR, 10, ExplX, ExplY);
    85               end
    93               end
    86            end;
    94            end;
    87         if a = High(TAmmoType) then a:= Low(TAmmoType)
    95         if a = High(TAmmoType) then a:= Low(TAmmoType)
    88                                else inc(a)
    96                                else inc(a)
    89        until (a = aa) or (PHedgehog(Me^.Hedgehog)^.AttacksNum > 0)
    97        until (a = aa) or (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].AttacksNum > 0)
    90        end
    98        end
    91 end;
    99 end;
    92 
   100 
    93 procedure Walk(Me: PGear);
   101 procedure Walk(Me: PGear);
    94 const FallPixForBranching = cHHRadius * 2 + 8;
   102 const FallPixForBranching = cHHRadius * 2 + 8;
    95       
   103       cBranchStackSize = 12;
       
   104 
       
   105 type TStackEntry = record
       
   106                    WastedTicks: Longword;
       
   107                    MadeActions: TActions;
       
   108                    Hedgehog: TGear;
       
   109                    end;
       
   110                    
       
   111 var Stack: record
       
   112            Count: Longword;
       
   113            States: array[0..Pred(cBranchStackSize)] of TStackEntry;
       
   114            end;
       
   115 
       
   116     function Push(Ticks: Longword; const Actions: TActions; const Me: TGear; Dir: integer): boolean;
       
   117     var Result: boolean;
       
   118     begin
       
   119     Result:= (Stack.Count < cBranchStackSize) and (Actions.Count < MAXACTIONS - 5);
       
   120     if Result then
       
   121        with Stack.States[Stack.Count] do
       
   122             begin
       
   123             WastedTicks:= Ticks;
       
   124             MadeActions:= Actions;
       
   125             Hedgehog:= Me;
       
   126             Hedgehog.Message:= Dir;
       
   127             inc(Stack.Count)
       
   128             end;
       
   129     Push:= Result
       
   130     end;
       
   131 
       
   132     procedure Pop(var Ticks: Longword; var Actions: TActions; var Me: TGear);
       
   133     begin
       
   134     dec(Stack.Count);
       
   135     with Stack.States[Stack.Count] do
       
   136          begin
       
   137          Ticks:= WastedTicks;
       
   138          Actions:= MadeActions;
       
   139          Me:= Hedgehog
       
   140          end
       
   141     end;
       
   142 
       
   143     function PosInThinkStack(Me: PGear): boolean;
       
   144     var i: Longword;
       
   145     begin
       
   146     i:= 0;
       
   147     while (i < Stack.Count) do
       
   148           begin
       
   149           if(not(hwAbs(Stack.States[i].Hedgehog.X - Me^.X) +
       
   150                  hwAbs(Stack.States[i].Hedgehog.Y - Me^.Y) > 2)) and
       
   151               (Stack.States[i].Hedgehog.Message = Me^.Message) then exit(true);
       
   152           inc(i)
       
   153           end;
       
   154     PosInThinkStack:= false
       
   155     end;
       
   156 
       
   157 
    96 var Actions: TActions;
   158 var Actions: TActions;
    97     ticks, maxticks, steps, BotLevel: Longword;
   159     ticks, maxticks, steps, BotLevel, tmp: Longword;
    98     BaseRate, Rate: LongInt;
   160     BaseRate, BestRate, Rate: integer;
    99     GoInfo: TGoInfo;
   161     GoInfo: TGoInfo;
   100     CanGo: boolean;
   162     CanGo: boolean;
   101     AltMe: TGear;
   163     AltMe: TGear;
   102 begin
   164 begin
       
   165 Actions.Count:= 0;
       
   166 Actions.Pos:= 0;
       
   167 Actions.Score:= 0;
       
   168 Stack.Count:= 0;
   103 BotLevel:= PHedgehog(Me^.Hedgehog)^.BotLevel;
   169 BotLevel:= PHedgehog(Me^.Hedgehog)^.BotLevel;
   104 
   170 
       
   171 tmp:= random(2) + 1;
       
   172 Push(0, Actions, Me^, tmp);
       
   173 Push(0, Actions, Me^, tmp xor 3);
       
   174 
   105 if (Me^.State and gstAttacked) = 0 then maxticks:= max(0, TurnTimeLeft - 5000 - 4000 * BotLevel)
   175 if (Me^.State and gstAttacked) = 0 then maxticks:= max(0, TurnTimeLeft - 5000 - 4000 * BotLevel)
   106                                   else maxticks:= TurnTimeLeft;
   176                                    else maxticks:= TurnTimeLeft;
   107 
   177 
   108 BaseRate:= RatePlace(Me);
   178 if (Me^.State and gstAttacked) = 0 then TestAmmos(Actions, Me);
   109 
   179 BestRate:= RatePlace(Me);
   110 repeat
   180 BaseRate:= max(BestRate, 0);
   111     if not Pop(ticks, Actions, Me^) then
   181 
   112        begin
   182 while (Stack.Count > 0) and not StopThinking do
   113        isThinking:= false;
   183     begin
   114        exit
   184     Pop(ticks, Actions, Me^);
   115        end;
   185 
   116 
   186     AddAction(Actions, Me^.Message, aim_push, 250, 0, 0);
   117     AddAction(Actions, Me^.Message, aim_push, 10, 0, 0);
       
   118     if (Me^.Message and gm_Left) <> 0 then AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0)
   187     if (Me^.Message and gm_Left) <> 0 then AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0)
   119                                       else AddAction(Actions, aia_WaitXR, hwRound(Me^.X), 0, 0, 0);
   188                                       else AddAction(Actions, aia_WaitXR, hwRound(Me^.X), 0, 0, 0);
   120     AddAction(Actions, Me^.Message, aim_release, 0, 0, 0);
   189     AddAction(Actions, Me^.Message, aim_release, 0, 0, 0);
   121     steps:= 0;
   190     steps:= 0;
   122     if ((Me^.State and gstAttacked) = 0) then TestAmmos(Actions, Me);
   191 
   123 
   192     while (not StopThinking) and (not PosInThinkStack(Me)) do
   124     while not PosInThinkStack(Me) do
       
   125        begin
   193        begin
   126        CanGo:= HHGo(Me, @AltMe, GoInfo);
   194        CanGo:= HHGo(Me, @AltMe, GoInfo);
   127        inc(ticks, GoInfo.Ticks);
   195        inc(ticks, GoInfo.Ticks);
   128        if ticks > maxticks then break;
   196        if ticks > maxticks then break;
   129 
   197 
   130        if (BotLevel < 5) and (GoInfo.JumpType = jmpHJump) then // hjump support
   198        if (BotLevel < 5) and (GoInfo.JumpType = jmpHJump) then // hjump support
   131           if Push(ticks, Actions, AltMe, Me^.Message) then
   199           if Push(ticks, Actions, AltMe, Me^.Message) then
   132              with ThinkStack.States[Pred(ThinkStack.Count)] do
   200              with Stack.States[Pred(Stack.Count)] do
   133                   begin
   201                   begin
   134                   AddAction(MadeActions, aia_HJump, 0, 305, 0, 0);
   202                   AddAction(MadeActions, aia_HJump, 0, 305, 0, 0);
   135                   AddAction(MadeActions, aia_HJump, 0, 350, 0, 0);
   203                   AddAction(MadeActions, aia_HJump, 0, 350, 0, 0);
   136                   if (Me^.dX < 0) then AddAction(MadeActions, aia_WaitXL, hwRound(AltMe.X), 0, 0, 0)
       
   137                                   else AddAction(MadeActions, aia_WaitXR, hwRound(AltMe.X), 0, 0, 0);
       
   138                   end;
   204                   end;
   139        if (BotLevel < 3) and (GoInfo.JumpType = jmpLJump) then // ljump support
   205        if (BotLevel < 3) and (GoInfo.JumpType = jmpLJump) then // ljump support
   140           if Push(ticks, Actions, AltMe, Me^.Message) then
   206           if Push(ticks, Actions, AltMe, Me^.Message) then
   141              with ThinkStack.States[Pred(ThinkStack.Count)] do
   207              with Stack.States[Pred(Stack.Count)] do
   142                   begin
       
   143                   AddAction(MadeActions, aia_LJump, 0, 305, 0, 0);
   208                   AddAction(MadeActions, aia_LJump, 0, 305, 0, 0);
   144                   if (Me^.dX < 0) then AddAction(MadeActions, aia_WaitXL, hwRound(AltMe.X), 0, 0, 0)
   209 
   145                                   else AddAction(MadeActions, aia_WaitXR, hwRound(AltMe.X), 0, 0, 0);
       
   146                   end;
       
   147        if not CanGo then break;
   210        if not CanGo then break;
   148        inc(steps);
   211        inc(steps);
   149        Actions.actions[Actions.Count - 2].Param:= hwRound(Me^.X);
   212        Actions.actions[Actions.Count - 2].Param:= hwRound(Me^.X);
   150        Rate:= RatePlace(Me);
   213        Rate:= RatePlace(Me);
   151        if Rate > BaseRate then
   214        if Rate > BestRate then
   152           begin
   215           begin
   153           BestActions:= Actions;
   216           BestActions:= Actions;
   154           BestActions.Score:= 1;
   217           BestRate:= Rate;
   155           isThinking:= false;
   218           Me^.State:= Me^.State or gstAttacked // we have better place, go there and don't use ammo
   156           exit
       
   157           end
   219           end
   158        else if Rate < BaseRate then break;
   220        else if Rate < BestRate then break;
       
   221        if ((Me^.State and gstAttacked) = 0)
       
   222            and ((steps mod 4) = 0) then TestAmmos(Actions, Me);
   159        if GoInfo.FallPix >= FallPixForBranching then
   223        if GoInfo.FallPix >= FallPixForBranching then
   160           Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right
   224           Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right
   161 
       
   162        if ((Me^.State and gstAttacked) = 0)
       
   163            and ((steps mod 4) = 0) then
       
   164            begin
       
   165            TestAmmos(Actions, Me);
       
   166            if SDL_GetTicks - AIThinkStart >= Pred(cTimerInterval) then
       
   167               begin
       
   168               dec(Actions.Count, 3);
       
   169               Push(ticks, Actions, Me^, Me^.Message);
       
   170               exit
       
   171               end
       
   172            end
       
   173        end;
   225        end;
   174 until false
   226 
   175 end;
   227     if BestRate > BaseRate then exit
   176 
   228     end
   177 procedure Think(Me: PGear);
   229 end;
       
   230 
       
   231 procedure Think(Me: PGear); cdecl;
   178 var BackMe, WalkMe: TGear;
   232 var BackMe, WalkMe: TGear;
   179 begin
   233     StartTicks: Longword;
   180 AIThinkStart:= SDL_GetTicks;
   234 begin
       
   235 StartTicks:= GameTicks;
       
   236 BestActions.Count:= 0;
       
   237 BestActions.Pos:= 0;
       
   238 BestActions.Score:= Low(integer);
   181 BackMe:= Me^;
   239 BackMe:= Me^;
   182 WalkMe:= BackMe;
   240 WalkMe:= BackMe;
   183 if (Me^.State and gstAttacked) = 0 then
   241 if (Me^.State and gstAttacked) = 0 then
   184    if Targets.Count > 0 then
   242    if Targets.Count > 0 then
   185       begin
   243       begin
   186       Walk(@WalkMe);
   244       Walk(@WalkMe);
   187       if not isThinking then
   245       if (StartTicks > GameTicks - 1500) and not StopThinking then SDL_Delay(2000);
       
   246       if BestActions.Score < -1023 then
   188          begin
   247          begin
   189          if BestActions.Score < -1023 then
   248          BestActions.Count:= 0;
   190             begin
   249          AddAction(BestActions, aia_Skip, 0, 250, 0, 0);
   191             BestActions.Count:= 0;
   250          end;
   192             AddAction(BestActions, aia_Skip, 0, 250, 0, 0);
       
   193             end;
       
   194          Me^.State:= Me^.State and not gstHHThinking
       
   195          end
       
   196       end else
   251       end else
   197 else begin
   252 else begin
   198       FillBonuses(true);
       
   199       Walk(@WalkMe);
   253       Walk(@WalkMe);
   200       AddAction(BestActions, aia_Wait, GameTicks + 100, 100, 0, 0);
   254       while (not StopThinking) and (BestActions.Count = 0) do
   201       end
   255             begin
       
   256             SDL_Delay(100);
       
   257             FillBonuses(true);
       
   258             WalkMe:= BackMe;
       
   259             Walk(@WalkMe)
       
   260             end
       
   261       end;
       
   262 Me^.State:= Me^.State and not gstHHThinking
   202 end;
   263 end;
   203 
   264 
   204 procedure StartThink(Me: PGear);
   265 procedure StartThink(Me: PGear);
   205 var a: TAmmoType;
   266 var a: TAmmoType;
   206     tmp: LongInt;
       
   207 begin
   267 begin
   208 if ((Me^.State and gstAttacking) <> 0) or isInMultiShoot then exit;
   268 if ((Me^.State and gstAttacking) <> 0) or isInMultiShoot then exit;
   209 ThinkingHH:= Me;
       
   210 isThinking:= true;
       
   211 
       
   212 ClearThinkStack;
       
   213 
       
   214 Me^.State:= Me^.State or gstHHThinking;
   269 Me^.State:= Me^.State or gstHHThinking;
   215 Me^.Message:= 0;
   270 Me^.Message:= 0;
       
   271 StopThinking:= false;
       
   272 ThinkingHH:= Me;
   216 FillTargets;
   273 FillTargets;
   217 if Targets.Count = 0 then
   274 if Targets.Count = 0 then
   218    begin
   275    begin
   219    OutError('AI: no targets!?', false);
   276    OutError('AI: no targets!?', false);
   220    exit
   277    exit
   221    end;
   278    end;
   222 
       
   223 FillBonuses((Me^.State and gstAttacked) <> 0);
   279 FillBonuses((Me^.State and gstAttacked) <> 0);
   224 
       
   225 for a:= Low(TAmmoType) to High(TAmmoType) do
   280 for a:= Low(TAmmoType) to High(TAmmoType) do
   226     CanUseAmmo[a]:= Assigned(AmmoTests[a]) and HHHasAmmo(PHedgehog(Me^.Hedgehog), a);
   281     CanUseAmmo[a]:= Assigned(AmmoTests[a]) and HHHasAmmo(PHedgehog(Me^.Hedgehog), a);
   227 
   282 {$IFDEF DEBUGFILE}AddFileLog('Enter Think Thread');{$ENDIF}
   228 BestActions.Count:= 0;
   283 ThinkThread:= SDL_CreateThread(@Think, Me)
   229 BestActions.Pos:= 0;
   284 end;
   230 BestActions.Score:= 0;
   285 
   231 tmp:= random(2) + 1;
   286 procedure ProcessBot;
   232 Push(0, BestActions, Me^, tmp);
   287 const StartTicks: Longword = 0;
   233 Push(0, BestActions, Me^, tmp xor 3);
       
   234 BestActions.Score:= Low(LongInt);
       
   235 
       
   236 Think(Me)
       
   237 end; 
       
   238 
       
   239 procedure ProcessBot(FrameNo: Longword);
       
   240 const LastFrameNo: Longword = 0;
       
   241 begin
   288 begin
   242 with CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog] do
   289 with CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog] do
   243      if (Gear <> nil)
   290      if (Gear <> nil)
   244         and ((Gear^.State and gstHHDriven) <> 0)
   291         and ((Gear^.State and gstHHDriven) <> 0)
   245         and (TurnTimeLeft < cHedgehogTurnTime - 50) then
   292         and (TurnTimeLeft < cHedgehogTurnTime - 50) then
   246         if not isThinking then
   293         if ((Gear^.State and gstHHThinking) = 0) then
   247            if (BestActions.Pos >= BestActions.Count) then StartThink(Gear)
   294            if (BestActions.Pos >= BestActions.Count) then
   248                                                      else ProcessAction(BestActions, Gear)
   295               begin
   249         else if FrameNo <> LastFrameNo then
   296               StartThink(Gear);
   250                 begin
   297               StartTicks:= GameTicks
   251                 LastFrameNo:= FrameNo;
   298               end else ProcessAction(BestActions, Gear)
   252                 Think(Gear)
   299         else if (GameTicks - StartTicks) > cMaxAIThinkTime then StopThinking:= true
   253                 end;
   300 end;
   254 end;
   301 
   255 
   302 
   256 end.
   303 end.