diff -r bde641cf53c8 -r e350500c4edb hedgewars/uAI.pas --- a/hedgewars/uAI.pas Thu Apr 04 14:37:19 2013 +0200 +++ b/hedgewars/uAI.pas Tue Jun 04 22:28:12 2013 +0200 @@ -1,6 +1,6 @@ (* * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2012 Andrey Korotaev + * Copyright (c) 2004-2013 Andrey Korotaev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,7 +30,7 @@ implementation uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions, - uAmmos, SysUtils{$IFNDEF USE_SDLTHREADS} {$IFDEF UNIX}, cthreads{$ENDIF} {$ENDIF}, uTypes, + uAmmos, SysUtils, uTypes, uVariables, uCommands, uUtils, uDebug, uAILandMarks; {$IFDEF AI_MAINTHREAD} @@ -41,25 +41,21 @@ var BestActions: TActions; CanUseAmmo: array [TAmmoType] of boolean; StopThinking: boolean; -{$IFDEF USE_SDLTHREADS} - ThinkThread: PSDL_Thread = nil; -{$ELSE} - ThinkThread: TThreadID; -{$ENDIF} - hasThread: LongInt; - StartTicks: LongInt; + StartTicks: Longword; + ThinkThread: PSDL_Thread; + ThreadLock: PSDL_Mutex; procedure FreeActionsList; begin AddFileLog('FreeActionsList called'); - if hasThread <> 0 then - begin - AddFileLog('Waiting AI thread to finish'); + if (ThinkThread <> nil) then + begin StopThinking:= true; - repeat - SDL_Delay(10) - until hasThread = 0 - end; + SDL_WaitThread(ThinkThread, nil); + end; + SDL_LockMutex(ThreadLock); + ThinkThread:= nil; + SDL_UnlockMutex(ThreadLock); with CurrentHedgehog^ do if Gear <> nil then @@ -71,7 +67,6 @@ end; - const cBranchStackSize = 12; type TStackEntry = record WastedTicks: Longword; @@ -128,22 +123,18 @@ with Me^.Hedgehog^ do a:= CurAmmoType; aa:= a; -{$IFDEF USE_SDLTHREADS} - SDL_delay(0); //ThreadSwitch was only a hint -{$ELSE} - ThreadSwitch(); -{$ENDIF} + SDL_delay(0); // hint to let the context switch run repeat - if (CanUseAmmo[a]) - and ((not rareChecks) or ((AmmoTests[a].flags and amtest_Rare) = 0)) - and ((i = 0) or ((AmmoTests[a].flags and amtest_NoTarget) = 0)) + if (CanUseAmmo[a]) + and ((not rareChecks) or ((AmmoTests[a].flags and amtest_Rare) = 0)) + and ((i = 0) or ((AmmoTests[a].flags and amtest_NoTarget) = 0)) then begin {$HINTS OFF} - Score:= AmmoTests[a].proc(Me, Targets.ar[i].Point, BotLevel, ap); + Score:= AmmoTests[a].proc(Me, Targets.ar[i], BotLevel, ap); {$HINTS ON} if Actions.Score + Score > BestActions.Score then - if (BestActions.Score < 0) or (Actions.Score + Score > BestActions.Score + Byte(BotLevel) * 2048) then + if (BestActions.Score < 0) or (Actions.Score + Score > BestActions.Score + Byte(BotLevel - 1) * 2048) then begin BestActions:= Actions; inc(BestActions.Score, Score); @@ -155,10 +146,10 @@ AddAction(BestActions, aia_LookRight, 0, 200, 0, 0) else if (ap.Angle < 0) then AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0); - + if (Ammoz[a].Ammo.Propz and ammoprop_Timerable) <> 0 then AddAction(BestActions, aia_Timer, ap.Time div 1000, 400, 0, 0); - + if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then begin dAngle:= LongInt(Me^.Angle) - Abs(ap.Angle); @@ -173,23 +164,23 @@ AddAction(BestActions, aia_Down, aim_release, -dAngle, 0, 0) end end; - + if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then begin AddAction(BestActions, aia_Put, 0, 1, ap.AttackPutX, ap.AttackPutY) end; - + if (Ammoz[a].Ammo.Propz and ammoprop_OscAim) <> 0 then begin AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0); AddAction(BestActions, aia_attack, aim_release, 1, 0, 0); - + if abs(ap.Angle) > 32 then begin AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0); AddAction(BestActions, aia_Down, aim_release, 32, 0, 0); end; - + AddAction(BestActions, aia_waitAngle, ap.Angle, 250, 0, 0); AddAction(BestActions, aia_attack, aim_push, 1, 0, 0); AddAction(BestActions, aia_attack, aim_release, 1, 0, 0); @@ -248,21 +239,21 @@ if (Me^.State and gstAttacked) = 0 then TestAmmos(Actions, Me, false); - + BestRate:= RatePlace(Me); BaseRate:= Max(BestRate, 0); -// switch to 'skip' if we can't move because of mouse cursor being shown +// switch to 'skip' if we cannot move because of mouse cursor being shown if (Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) <> 0 then AddAction(Actions, aia_Weapon, Longword(amSkip), 100 + random(200), 0, 0); - -if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0)) + +if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0)) and (GameFlags and gfArtillery = 0) then begin tmp:= random(2) + 1; Push(0, Actions, Me^, tmp); Push(0, Actions, Me^, tmp xor 3); - + while (Stack.Count > 0) and (not StopThinking) do begin Pop(ticks, Actions, Me^); @@ -272,7 +263,7 @@ AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0) else AddAction(Actions, aia_WaitXR, hwRound(Me^.X), 0, 0, 0); - + steps:= 0; while (not StopThinking) do @@ -285,8 +276,8 @@ if ticks > maxticks then break; - if (BotLevel < 5) - and (GoInfo.JumpType = jmpHJump) + if (BotLevel < 5) + and (GoInfo.JumpType = jmpHJump) and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped)) then // hjump support begin @@ -300,7 +291,7 @@ AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0) else AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0); - + AddAction(MadeActions, aia_HJump, 0, 305 + random(50), 0, 0); AddAction(MadeActions, aia_HJump, 0, 350, 0, 0); end; @@ -308,8 +299,8 @@ Push(ticks, Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message) end; end; - if (BotLevel < 3) - and (GoInfo.JumpType = jmpLJump) + if (BotLevel < 3) + and (GoInfo.JumpType = jmpLJump) and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped)) then // ljump support begin @@ -328,7 +319,7 @@ // push current position so we proceed from it after checking jump+forward walk opportunities if CanGo then Push(ticks, Actions, Me^, Me^.Message); - + // first check where we go after jump walking forward if Push(ticks, Actions, AltMe, Me^.Message) then with Stack.States[Pred(Stack.Count)] do @@ -336,10 +327,10 @@ break end; - // 'not CanGO' means we can't go straight, possible jumps are checked above - if (not CanGo) then + // 'not CanGO' means we cannot go straight, possible jumps are checked above + if not CanGo then break; - + inc(steps); Actions.actions[Pred(Actions.Count)].Param:= hwRound(Me^.X); Rate:= RatePlace(Me); @@ -352,17 +343,17 @@ end else if Rate < BestRate then break; - + if ((Me^.State and gstAttacked) = 0) and ((steps mod 4) = 0) then begin if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere) then - break; + break; addMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere); TestAmmos(Actions, Me, ticks shr 12 = oldticks shr 12); - + end; - + if GoInfo.FallPix >= FallPixForBranching then Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right @@ -382,31 +373,26 @@ end {if} end; -function Think(Me: Pointer): ptrint; +function Think(Me: PGear): LongInt; cdecl; export; var BackMe, WalkMe: TGear; switchCount: LongInt; currHedgehogIndex, itHedgehog, switchesNum, i: Longword; switchImmediatelyAvailable: boolean; Actions: TActions; begin -InterlockedIncrement(hasThread); - -{$IFDEF AI_MAINTHREAD} -StartTicks:= SDL_GetTicks(); -{$ELSE} +dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; StartTicks:= GameTicks; -{$ENDIF} currHedgehogIndex:= CurrentTeam^.CurrHedgehog; itHedgehog:= currHedgehogIndex; switchesNum:= 0; switchImmediatelyAvailable:= (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtSwitcher); -if PGear(Me)^.Hedgehog^.BotLevel <> 5 then +if Me^.Hedgehog^.BotLevel <> 5 then switchCount:= HHHasAmmo(PGear(Me)^.Hedgehog^, amSwitch) else switchCount:= 0; -if (PGear(Me)^.State and gstAttacking) = 0 then +if ((Me^.State and gstAttacked) = 0) or isInMultiShoot then if Targets.Count > 0 then begin // iterate over current team hedgehogs @@ -422,7 +408,7 @@ begin // when AI has to use switcher, make it cost smth unless they have a lot of switches if (switchCount < 10) then Actions.Score:= (-27+switchCount*3)*4000; - AddAction(Actions, aia_Weapon, Longword(amSwitch), 300 + random(200), 0, 0); + AddAction(Actions, aia_Weapon, Longword(amSwitch), 300 + random(200), 0, 0); AddAction(Actions, aia_attack, aim_push, 300 + random(300), 0, 0); AddAction(Actions, aia_attack, aim_release, 1, 0, 0); end; @@ -436,10 +422,9 @@ itHedgehog:= Succ(itHedgehog) mod CurrentTeam^.HedgehogsNumber; until (itHedgehog = currHedgehogIndex) or ((CurrentTeam^.Hedgehogs[itHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[itHedgehog].Effects[heFrozen]=0)); - inc(switchesNum); until (not (switchImmediatelyAvailable or (switchCount > 0))) - or StopThinking + or StopThinking or (itHedgehog = currHedgehogIndex) or BestActions.isWalkingToABetterPlace; @@ -460,10 +445,9 @@ end else SDL_Delay(100) else begin - BackMe:= PGear(Me)^; - -//{$IFNDEF AI_MAINTHREAD} - while (not StopThinking) and (BestActions.Count = 0) do + BackMe:= Me^; + i:= 12; + while (not StopThinking) and (BestActions.Count = 0) and (i > 0) do begin (* @@ -478,17 +462,17 @@ Actions.Pos:= 0; Actions.Score:= 0; Walk(@WalkMe, Actions); -{$IFNDEF AI_MAINTHREAD} - if (not StopThinking) then + dec(i); + if not StopThinking then SDL_Delay(100) -{$ENDIF} end -//{$ENDIF} end; -PGear(Me)^.State:= PGear(Me)^.State and (not gstHHThinking); +Me^.State:= Me^.State and (not gstHHThinking); +SDL_LockMutex(ThreadLock); +ThinkThread:= nil; +SDL_UnlockMutex(ThreadLock); Think:= 0; -InterlockedDecrement(hasThread) end; procedure StartThink(Me: PGear); @@ -517,22 +501,16 @@ exit end; -FillBonuses((Me^.State and gstAttacked) <> 0); -AddFileLog('Enter Think Thread'); +FillBonuses(((Me^.State and gstAttacked) <> 0) and (not isInMultiShoot)); -{$IFDEF AI_MAINTHREAD} -Think(Me); -{$ELSE} -{$IFDEF USE_SDLTHREADS} -ThinkThread := SDL_CreateThread(@Think{$IFDEF SDL13}, nil{$ENDIF}, Me); -{$ELSE} -BeginThread(@Think, Me, ThinkThread); -{$ENDIF} -AddFileLog('Thread started'); -{$ENDIF} +SDL_LockMutex(ThreadLock); +ThinkThread:= SDL_CreateThread(@Think{$IFDEF SDL13}, 'think'{$ENDIF}, Me); +SDL_UnlockMutex(ThreadLock); end; -//var scoreShown: boolean = false; +{$IFDEF DEBUGAI} +var scoreShown: boolean = false; +{$ENDIF} procedure ProcessBot; const cStopThinkTime = 40; @@ -550,21 +528,25 @@ StopMessages(Gear^.Message); TryDo((Gear^.Message and gmAllStoppable) = 0, 'Engine bug: AI may break demos playing', true); end; - + if Gear^.Message <> 0 then exit; - - //scoreShown:= false; + +{$IFDEF DEBUGAI} + scoreShown:= false; +{$ENDIF} StartThink(Gear); StartTicks:= GameTicks - + end else begin - {if not scoreShown then +{$IFDEF DEBUGAI} + if not scoreShown then begin if BestActions.Score > 0 then ParseCommand('/say Expected score = ' + inttostr(BestActions.Score div 1024), true); scoreShown:= true - end;} + end; +{$ENDIF} ProcessAction(BestActions, Gear) end else if ((GameTicks - StartTicks) > cMaxAIThinkTime) @@ -574,16 +556,15 @@ procedure initModule; begin - hasThread:= 0; StartTicks:= 0; -{$IFNDEF PAS2C} - ThinkThread:= ThinkThread; -{$ENDIF} + ThinkThread:= nil; + ThreadLock:= SDL_CreateMutex(); end; procedure freeModule; begin FreeActionsList(); + SDL_DestroyMutex(ThreadLock); end; end.