--- 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 <unC0Rr@gmail.com>
+ * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com>
*
* 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.