source: trunk/ClientAI.pas

Last change on this file was 337, checked in by chronos, 3 months ago
  • Added: Game systems action in Tools menu.
  • Modified: TurnStats moved to separate unit.
File size: 10.6 KB
Line 
1unit ClientAI;
2
3interface
4
5uses
6 Classes, SysUtils, GameClient, Game, Math, Player, Map, Generics.Defaults;
7
8type
9 { TComputer }
10
11 TComputer = class(TClient)
12 protected
13 procedure DoTurnStart; override;
14 public
15 //Targets: TFPGObjectList<TPlayer>;
16 CellProcessDirection: Boolean;
17 procedure AttackNeutral;
18 procedure AttackPlayers;
19 procedure InnerMoves;
20 procedure IncreaseMoves;
21 procedure Process;
22 procedure FallBack;
23 function AttackersCount(Cell: TPlayerCell): Integer;
24 end;
25
26
27implementation
28
29{ TComputer }
30
31procedure TComputer.DoTurnStart;
32begin
33 Process;
34 Protocol.TurnEnd;
35end;
36
37procedure TComputer.AttackNeutral;
38var
39 AllCells: TPlayerCells;
40 TotalPower: Integer;
41 AttackPower: Integer;
42 TotalAttackPower: Integer;
43 CanAttack: Integer;
44 TargetPower: Integer;
45 TargetCells: TPlayerCells;
46 Cell: TPlayerCell;
47 NeighborCell: TPlayerCell;
48const
49 AttackDiff = 1;
50begin
51 AllCells := ControlPlayer.PlayerMap.Cells;
52 TargetCells := TPlayerCells.Create;
53 TargetCells.OwnsObjects := False;
54
55 // Get list of all attack target cells
56 for Cell in AllCells do
57 with Cell do begin
58 if (MapCell.Terrain <> ttVoid) and (MapCell.Player = nil) then begin
59 CanAttack := 0;
60 for NeighborCell in Neighbors do
61 if NeighborCell.MapCell.Player = ControlPlayer then begin
62 Inc(CanAttack);
63 end;
64 if CanAttack > 0 then TargetCells.Add(Cell);
65 end;
66 end;
67
68 // Sort ascending to attack cells with lower power first
69 // Low power cells are better for expanding our territory
70 TargetCells.Sort(TComparer<TPlayerCell>.Construct(CellCompare));
71
72 for Cell in TargetCells do
73 with Cell do begin
74 // Attack to not owned cell yet
75 // Count own possible power
76 TotalPower := 0;
77 for NeighborCell in Neighbors do
78 if NeighborCell.MapCell.Player = ControlPlayer then
79 TotalPower := TotalPower + NeighborCell.GetAvialPowerForMove;
80
81 // Attack if target is weaker
82 if Assigned(MapCell.OneUnit) then TargetPower := MapCell.OneUnit.Power
83 else TargetPower := 0;
84 if TotalPower >= (TargetPower + AttackDiff) then begin
85 TotalAttackPower := 0;
86 for NeighborCell in Neighbors do
87 if NeighborCell.MapCell.Player = ControlPlayer then begin
88 // Use only necessary power
89 AttackPower := TargetPower - TotalAttackPower + AttackDiff;
90 if NeighborCell.GetAvialPower < AttackPower then
91 AttackPower := NeighborCell.GetAvialPower;
92 ControlPlayer.SetMove(NeighborCell, Cell, AttackPower, False);
93 TotalAttackPower := TotalAttackPower + AttackPower;
94 end;
95 end;
96 end;
97
98 FreeAndNil(TargetCells);
99end;
100
101procedure TComputer.AttackPlayers;
102var
103 AllCells: TPlayerCells;
104 TotalPower: Integer;
105 AttackPower: Integer;
106 TotalAttackPower: Integer;
107 CanAttack: Integer;
108 TargetCells: TPlayerCells;
109 TargetCell: TPlayerCell;
110 NeighborCell: TPlayerCell;
111 DefendCount: Integer;
112begin
113 if ControlPlayer.Defensive then Exit;
114
115 AllCells := ControlPlayer.PlayerMap.Cells;
116 TargetCells := TPlayerCells.Create;
117 TargetCells.OwnsObjects := False;
118
119 // Get list of all attack target cells
120 for TargetCell in AllCells do begin
121 if (TargetCell.MapCell.Terrain <> ttVoid) and (TargetCell.MapCell.Player <> ControlPlayer) and
122 (TargetCell.MapCell.Player <> nil) then begin
123 CanAttack := 0;
124 for NeighborCell in TargetCell.Neighbors do
125 if NeighborCell.MapCell.Player = ControlPlayer then begin
126 Inc(CanAttack);
127 end;
128 if CanAttack > 0 then TargetCells.Add(TargetCell);
129 end;
130 end;
131
132 // Sort descending to attack cells with higher power first
133 // Higher power enemy cells can grow faster and is more dangerous
134 TargetCells.Sort(TComparer<TPlayerCell>.Construct(CellCompareDescending));
135
136 for TargetCell in TargetCells do begin
137 // Attack to not owned cell yet
138 // Count own possible power
139 TotalPower := 0;
140 for NeighborCell in TargetCell.Neighbors do
141 if NeighborCell.MapCell.Player = ControlPlayer then begin
142 TotalPower := TotalPower + NeighborCell.GetAvialPower;
143 end;
144 // Attack if target is weaker
145 if Assigned(TargetCell.MapCell.OneUnit) then
146 DefendCount := TargetCell.MapCell.OneUnit.Power
147 else DefendCount := 0;
148 if Game.AttackProbability(TotalPower, DefendCount) >=
149 ComputerAggroProbability[ControlPlayer.Agressivity] then begin
150 // Try to limit total attacking power to necessary minimum
151 while Game.AttackProbability(TotalPower - 1, DefendCount) >=
152 ComputerAggroProbability[ControlPlayer.Agressivity] do
153 Dec(TotalPower);
154
155 // Collect required attack units from our cells
156 TotalAttackPower := 0;
157 for NeighborCell in TargetCell.Neighbors do
158 if NeighborCell.MapCell.Player = ControlPlayer then begin
159 // Use only necessary power
160 AttackPower := TotalPower - TotalAttackPower;
161 if NeighborCell.GetAvialPower < AttackPower then
162 AttackPower := NeighborCell.GetAvialPower;
163 Self.ControlPlayer.SetMove(NeighborCell, TargetCell, AttackPower, False);
164 TotalAttackPower := TotalAttackPower + AttackPower;
165 if TotalAttackPower >= TotalPower then Break;
166 end;
167 end;
168 end;
169
170 FreeAndNil(TargetCells);
171end;
172
173procedure TComputer.InnerMoves;
174var
175 AllCells: TPlayerCells;
176 I, J: Integer;
177 C: Integer;
178 CanAttack: Integer;
179 TargetCells: TPlayerCells;
180 NewTargetCells: TPlayerCells;
181 Cells2: TPlayerCells;
182 MovedPower: Integer;
183begin
184 // We need to move available power to borders to be available for attacks
185 // or defense
186 AllCells := ControlPlayer.PlayerMap.Cells;
187 TargetCells := TPlayerCells.Create;
188 TargetCells.OwnsObjects := False;
189 NewTargetCells := TPlayerCells.Create;
190 NewTargetCells.OwnsObjects := False;
191
192 // Get list of all enemy border cells
193 for C := 0 to AllCells.Count - 1 do
194 with AllCells[C] do begin
195 if (MapCell.Player <> ControlPlayer) and (MapCell.Terrain <> ttVoid) then begin
196 CanAttack := 0;
197 for I := 0 to Neighbors.Count - 1 do
198 if ((Neighbors[I].MapCell.Player = ControlPlayer) or
199 (Neighbors[I].MapCell.Player = nil)) and (Neighbors[I].MapCell.Terrain <> ttVoid) then begin
200 Inc(CanAttack);
201 end;
202 if CanAttack > 0 then TargetCells.Add(AllCells[C]);
203 end;
204 end;
205
206 if CellProcessDirection then begin
207 // Reverse array
208 for I := 0 to (TargetCells.Count div 2) - 1 do
209 TargetCells.Exchange(I, TargetCells.Count - 1 - I);
210 end;
211
212 Game.Map.Cells.ClearMark;
213
214 while TargetCells.Count > 0 do begin
215 // Set mark for selected border cells
216 for C := 0 to TargetCells.Count - 1 do
217 TargetCells[C].MapCell.Mark := True;
218
219 // Move all power from unmarked cells and mark them
220 NewTargetCells.Count := 0;
221 for C := 0 to TargetCells.Count - 1 do
222 with TargetCells[C] do begin
223 for I := 0 to Neighbors.Count - 1 do begin
224 if (Neighbors[I].MapCell.Terrain <> ttVoid) and (not Neighbors[I].MapCell.Mark) then begin
225 if (TargetCells[C].MapCell.Player = ControlPlayer) and
226 (Neighbors[I].MapCell.Player = ControlPlayer) then begin
227 // Do not take units from front line
228 Cells2 := Neighbors[I].Neighbors;
229 CanAttack := 0;
230 for J := 0 to Cells2.Count - 1 do
231 if ((Cells2[J].MapCell.Player <> ControlPlayer) or (Cells2[J].MapCell.Player = nil))
232 and (Cells2[J].MapCell.Terrain <> ttVoid) then begin
233 Inc(CanAttack);
234 end;
235 if CanAttack = 0 then begin
236 MovedPower := Neighbors[I].GetAvialPower;
237 if (TargetCells[C].GetAvialPower + TargetCells[C].GetAttackPower + MovedPower) > Game.Map.MaxPower then
238 MovedPower := Game.Map.MaxPower - TargetCells[C].GetAvialPower - TargetCells[C].GetAttackPower;
239 TPlayer(MapCell.Player).SetMove(Neighbors[I], TargetCells[C], MovedPower, False);
240 end;
241 end;
242 Neighbors[I].MapCell.Mark := True;
243 NewTargetCells.Add(Neighbors[I]);
244 end;
245 end;
246 end;
247
248 // Use source cells NewTargetCells as new TargetCells
249 FreeAndNil(TargetCells);
250 TargetCells := NewTargetCells;
251 NewTargetCells := TPlayerCells.Create;
252 NewTargetCells.OwnsObjects := False;
253 end;
254
255 FreeAndNil(TargetCells);
256 FreeAndNil(NewTargetCells);
257end;
258
259procedure TComputer.IncreaseMoves;
260var
261 Move: TUnitMove;
262 AvailPower: Integer;
263begin
264 // If available power remains then use all for existed unit moves
265 for Move in ControlPlayer.Moves do
266 with Move do begin
267 if CellFrom.GetAvialPower > 0 then begin
268 AvailPower := CellFrom.GetAvialPower;
269 CountOnce := CountOnce + Min(AvailPower div CellFrom.MovesFrom.Count, AvailPower);
270 end;
271 end;
272end;
273
274procedure TComputer.Process;
275begin
276 AttackPlayers;
277 AttackNeutral;
278 InnerMoves;
279 IncreaseMoves;
280 //FallBack;
281 CellProcessDirection := not CellProcessDirection;
282end;
283
284procedure TComputer.FallBack;
285var
286 C: Integer;
287 I: Integer;
288 AllCells: TPlayerCells;
289 BorderCells: TPlayerCells;
290 EnemyPower: Integer;
291begin
292 BorderCells := TPlayerCells.Create;
293 BorderCells.OwnsObjects := False;
294 AllCells := ControlPlayer.PlayerMap.Cells;
295
296 // Get list of border cells
297 for C := 0 to AllCells.Count - 1 do
298 with AllCells[C] do begin
299 if (MapCell.Terrain <> ttVoid) and (MapCell.Player = ControlPlayer) then begin
300 if AttackersCount(AllCells[C]) > 0 then
301 BorderCells.Add(AllCells[C]);
302 end;
303 end;
304
305 // Move all units back to inner area from weak border cells
306 for C := 0 to BorderCells.Count - 1 do
307 with TPlayerCell(BorderCells[C]) do begin
308 // Calculate enemy power
309 // TODO: Do not sum different enemy power to one value
310 EnemyPower := 0;
311 for I := 0 to Neighbors.Count - 1 do
312 if (Neighbors[I].MapCell.Player <> ControlPlayer) and (Neighbors[I].MapCell.Player <> nil) then begin
313 Inc(EnemyPower, Neighbors[I].MapCell.OneUnit.Power);
314 end;
315 if EnemyPower > (GetAvialPower + GetAttackPower) then begin
316 // Fallback
317 for I := MovesTo.Count - 1 downto 0 do
318 TPlayer(MapCell.Player).Moves.Remove(MovesTo[I]);
319 for I := 0 to Neighbors.Count - 1 do
320 if (Neighbors[I].MapCell.Player = MapCell.Player) and (AttackersCount(Neighbors[I]) = 0) then begin
321 TPlayer(MapCell.Player).SetMove(BorderCells[C], Neighbors[I], GetAvialPower, False);
322 Break;
323 end;
324 end;
325 end;
326
327 FreeAndNil(BorderCells);
328end;
329
330function TComputer.AttackersCount(Cell: TPlayerCell): Integer;
331var
332 I: Integer;
333begin
334 Result := 0;
335 for I := 0 to Cell.Neighbors.Count - 1 do
336 if (Cell.Neighbors[I].MapCell.Player <> ControlPlayer) and
337 (Cell.Neighbors[I].MapCell.Player <> nil) then begin
338 Inc(Result);
339 end;
340end;
341
342end.
343
Note: See TracBrowser for help on using the repository browser.