source: branches/delphi/AI/ToolAI.pas

Last change on this file was 22, checked in by chronos, 7 years ago
  • Added: Delphi AI development kit.
File size: 52.3 KB
Line 
1{$INCLUDE switches.inc}
2unit ToolAI;
3
4interface
5
6uses
7{$IFDEF DEBUG}SysUtils, {$ENDIF} // necessary for debug exceptions
8{$IFDEF DEBUG}Names, {$ENDIF}
9 Protocol, CustomAI;
10
11type
12 TGroupTransportPlan = record
13 LoadLoc, uixTransport, nLoad, TurnsEmpty, TurnsLoaded: integer;
14 uixLoad: array [0 .. 15] of integer;
15 end;
16
17 TToolAI = class(TCustomAI)
18 protected
19{$IFDEF DEBUG}DebugMap: array [0 .. lxmax * lymax - 1] of integer; {$ENDIF}
20 function CityTaxBalance(cix: integer;
21 const CityReport: TCityReport): integer;
22 // calculates exact difference of income and maintenance cost for a single city
23 // positive result = income higher than maintenance
24 // negative result = income lower than maintenance
25 // respects production and food converted to gold
26 // CityReport must have been prepared before
27 procedure SumCities(TaxRate: integer; var TaxSum, ScienceSum: integer);
28 // calculates exact total tax and science income
29 // tax is reduced by maintenance (so might be negative)
30 // luxury not supported
31
32 procedure OptimizeCityTiles;
33 // obsolete; use City_OptimizeTiles instead
34
35 procedure GetCityProdPotential;
36 // calculates potential collected production resources of a city
37 // result: list for all cities in CityResult
38 procedure GetCityTradePotential;
39 // calculates potential collected trade resources of a city
40 // result: list for all cities in CityResult
41
42 procedure JobAssignment_Initialize;
43 // initialization, must be called first of the JobAssignment functions
44 procedure JobAssignment_AddJob(Loc, Job, Score: integer);
45 // add job for settlers with certain score
46 // jobs include founding cities!
47 procedure JobAssignment_AddUnit(uix: integer);
48 // add a settler unit to do jobs
49 procedure JobAssignment_Go;
50 // to be called after all jobs and the settlers for them have been added
51 // assigns each job to one settler, moves the settlers and makes them work
52 // settlers prefer jobs which are closer to their current location and jobs with a higher score
53 // starting a job one turn earlier counts the same as 4 points of score
54 // function does not cancel jobs that are already started
55 function JobAssignment_GotJob(uix: integer): boolean;
56 // can be called after JobAssignment_Go to find out whether
57 // a certain settler has been assigned a job to
58
59 procedure AnalyzeMap;
60 // calculates formations and districts
61
62 function CheckStep(MoveStyle, TimeBeforeStep, CrossCorner: integer;
63 var TimeAfterStep, RecoverTurns: integer;
64 FromTile, ToTile: integer): integer;
65 // forecast single unit move between adjacent tiles
66 // format of TimeBeforeStep and TimeAfterStep: $1000*number of turns + $800-MP left
67 // RecoverTurns: number of turns needed to rest outside city in order to
68 // recover from damage taken in this move (rounded up)
69 // FromTile and ToTile must be Map[FromLoc] and Map[ToLoc], no location codes
70 // CrossCorner=1 for long moves that cross the tile corner, =0 for short ones that don't
71
72 function GetMyMoveStyle(mix, Health: integer): integer;
73
74 function Unit_MoveEx(uix, ToLoc: integer; Options: integer = 0): integer;
75
76 procedure SeaTransport_BeginInitialize;
77 procedure SeaTransport_EndInitialize;
78 // sea transport, obligatory call order:
79 // 1. BeginInitialize
80 // [2. AddLoad/AddTransport/AddDestination]
81 // 3. EndInitialize
82 // [4. MakeGroupPlan, MakeGroupPlan, MakeGroupPlan...]
83 // don't use Pile between BeginInitialize and EndInitialize
84 // sea transport only works well if
85 // - all transports have same speed
86 // - all transports have same capacity
87 // - no transport is damaged
88 procedure SeaTransport_AddLoad(uix: integer);
89 procedure SeaTransport_AddTransport(uix: integer);
90 procedure SeaTransport_AddDestination(Loc: integer);
91 function SeaTransport_MakeGroupPlan(var TransportPlan
92 : TGroupTransportPlan): boolean;
93 // make plan for group of units to transport from a single loading location by a single transport
94 // the plan optimizes:
95 // - time for the units to move to the loading location
96 // - time for the transport to move to the loading location
97 // - time for the transport to move to one of the destination locations
98 // after the plan is made, units and transport are removed from the pool, so that
99 // subsequent calls to MakeGroupPlan result in plans that may be executed parallel
100 // function returns false if no more transports are possible
101
102 end;
103
104const
105 // no-formations
106 nfUndiscovered = -1;
107 nfPole = -2;
108 nfPeace = -3;
109
110 // return codes of CheckStep
111 csOk = 0;
112 // step is valid
113 // TimeAfterMove has been calculated
114 csForbiddenTile = 1;
115 // unit can not move onto this tile
116 // TimeAfterMove not calculated
117 csForbiddenStep = 2;
118 // (ZoC unit only) unit can not do this step because of ZoC violation
119 // maybe tile can be reached using another way
120 // TimeAfterMove not calculated
121 csCheckTerritory = 3;
122 // move within other nations's territory shortly after making peace
123 // step is only possible if RO.Territory is the same for both tiles
124 // TimeAfterMove has been calculated
125
126 // Unit_MoveEx
127 mxAdjacent = $00000001;
128
129var
130 nContinent, nOcean, nDistrict: integer;
131 Formation: array [0 .. lxmax * lymax - 1] of integer;
132 // water: ocean index, land: continent index, sorted by size
133 // territory unpassable due to peace treaty divides a continent
134 District: array [0 .. lxmax * lymax - 1] of integer;
135 // index of coherent own territory, sorted by size
136 CityResult: array [0 .. nCmax - 1] of integer;
137
138 Advancedness: array [0 .. nAdv - 1] of integer;
139 // total number of prerequisites for each advance
140
141implementation
142
143uses
144 Pile;
145
146type
147 pinteger = ^integer;
148
149var
150 // for JobAssignment
151 MaxScore: integer;
152 TileJob, TileJobScore: array [0 .. lxmax * lymax - 1] of byte;
153 JobLocOfSettler: array [0 .. nUmax - 1] of integer; // ToAssign = find job
154
155 // for Transport
156 TransportMoveStyle, TransportCapacity, nTransportLoad: integer;
157 InitComplete, HaveDestinations: boolean;
158 uixTransportLoad, TransportAvailable: array [0 .. nUmax - 1] of integer;
159 TurnsAfterLoad: array [0 .. lxmax * lymax - 1] of shortint;
160
161procedure ReplaceD(Start, Stop: pinteger; Raider, Twix: integer);
162begin
163 while Start <> Stop do
164 begin
165 if Start^ = Raider then
166 Start^ := Twix;
167 inc(Start)
168 end;
169end;
170
171function NextZero(Start, Stop: pinteger; Mask: cardinal): pinteger;
172begin
173 while (Start <> Stop) and (Start^ and Mask <> 0) do
174 inc(Start);
175 result := Start;
176end;
177
178function TToolAI.CityTaxBalance(cix: integer;
179 const CityReport: TCityReport): integer;
180var
181 i: integer;
182begin
183 result := 0;
184 if (CityReport.Working - CityReport.Happy <= MyCity[cix].Size shr 1)
185 { no disorder }
186 and (MyCity[cix].Flags and chCaptured = 0) then // not captured
187 begin
188 inc(result, CityReport.Tax);
189 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and
190 (CityReport.ProdRep > CityReport.Support) then
191 inc(result, CityReport.ProdRep - CityReport.Support);
192 if ((RO.Government = gLybertarianism) or
193 (MyCity[cix].Size >= NeedAqueductSize) and
194 (CityReport.FoodRep < CityReport.Eaten + 2)) and
195 (CityReport.FoodRep > CityReport.Eaten) then
196 inc(result, CityReport.FoodRep - CityReport.Eaten);
197 end;
198 for i := 28 to nImp - 1 do
199 if MyCity[cix].Built[i] > 0 then
200 dec(result, Imp[i].Maint);
201end;
202
203procedure TToolAI.SumCities(TaxRate: integer; var TaxSum, ScienceSum: integer);
204var
205 cix, p1: integer;
206 CityReport: TCityReport;
207begin
208 TaxSum := 0;
209 ScienceSum := 0;
210 if RO.Government = gAnarchy then
211 exit;
212 for p1 := 0 to nPl - 1 do
213 if RO.Tribute[p1] <= RO.TributePaid[p1] then
214 // don't rely on tribute from bankrupt nations
215 TaxSum := TaxSum + RO.Tribute[p1];
216 for cix := 0 to RO.nCity - 1 do
217 if MyCity[cix].Loc >= 0 then
218 begin
219 City_GetHypoReport(cix, -1, TaxRate, 0, CityReport);
220 if (CityReport.Working - CityReport.Happy <= MyCity[cix].Size shr 1)
221 { no disorder }
222 and (MyCity[cix].Flags and chCaptured = 0) then // not captured
223 ScienceSum := ScienceSum + CityReport.Science;
224 TaxSum := TaxSum + CityTaxBalance(cix, CityReport);
225 end;
226end;
227
228
229// ------------------------------------------------------------------------------
230// City Tiles Processing
231
232const
233 pctOptimize = 0;
234 pctGetProdPotential = 1;
235 pctGetTradePotential = 2;
236
237procedure TToolAI.OptimizeCityTiles;
238var
239 cix: integer;
240begin
241 for cix := 0 to RO.nCity - 1 do
242 with MyCity[cix] do
243 if Loc >= 0 then
244 City_OptimizeTiles(cix);
245end;
246
247procedure TToolAI.GetCityProdPotential;
248var
249 cix: integer;
250 Advice: TCityTileAdviceData;
251begin
252 for cix := 0 to RO.nCity - 1 do
253 with MyCity[cix] do
254 if Loc >= 0 then
255 begin
256 Advice.ResourceWeights := rwMaxProd;
257 Server(sGetCityTileAdvice, me, cix, Advice);
258 CityResult[cix] := Advice.CityReport.ProdRep;
259 // considers factory, but shouldn't
260 end;
261end;
262
263procedure TToolAI.GetCityTradePotential;
264var
265 cix: integer;
266 Advice: TCityTileAdviceData;
267begin
268 for cix := 0 to RO.nCity - 1 do
269 with MyCity[cix] do
270 if Loc >= 0 then
271 begin
272 Advice.ResourceWeights := rwMaxScience;
273 Server(sGetCityTileAdvice, me, cix, Advice);
274 CityResult[cix] := Advice.CityReport.Trade;
275 end;
276end;
277
278
279// ------------------------------------------------------------------------------
280// JobAssignment
281
282const
283 ToAssign = lxmax * lymax;
284
285procedure TToolAI.JobAssignment_Initialize;
286begin
287 fillchar(JobLocOfSettler, RO.nUn * sizeof(integer), $FF); // -1
288 fillchar(TileJob, MapSize, jNone);
289 fillchar(TileJobScore, MapSize, 0);
290 MaxScore := 0;
291end;
292
293procedure TToolAI.JobAssignment_AddJob(Loc, Job, Score: integer);
294begin
295 if Score > 255 then
296 Score := 255;
297 if Score > TileJobScore[Loc] then
298 begin
299 TileJob[Loc] := Job;
300 TileJobScore[Loc] := Score;
301 if Score > MaxScore then
302 MaxScore := Score
303 end;
304end;
305
306procedure TToolAI.JobAssignment_AddUnit(uix: integer);
307begin
308 assert(MyModel[MyUnit[uix].mix].Kind in [mkSettler, mkSlaves]);
309 JobLocOfSettler[uix] := ToAssign
310end;
311
312function TToolAI.JobAssignment_GotJob(uix: integer): boolean;
313begin
314 result := JobLocOfSettler[uix] >= 0;
315end;
316
317procedure TToolAI.JobAssignment_Go;
318const
319 DistanceScore = 4;
320 StepSizeByTerrain: array [0 .. 11] of integer = (0, 0, 1, 2, 1, 1, 0, 1,
321 0, 1, 1, 2);
322 // Oc-Sh-Gr-De-Pr-Tu-Ar-Sw-XX-Fo-Hi-Mo
323var
324 uix, BestScore, BestCount, BestLoc, BestJob, BestDistance, TestLoc, NextLoc,
325 TestDistance, V8, TestScore, StepSize, MoveResult: integer;
326 UnitsToAssign: boolean;
327 Adjacent: TVicinity8Loc;
328 SettlerOfJobLoc, DistToLoc: array [0 .. lxmax * lymax - 1] of smallint;
329 // DistToLoc is only defined where SettlerOfJobLoc>=0
330 TileChecked: array [0 .. lxmax * lymax - 1] of boolean;
331begin
332 fillchar(SettlerOfJobLoc, MapSize * 2, $FF); // -1
333
334 // keep up jobs that are already started
335 for uix := 0 to RO.nUn - 1 do
336 if (MyUnit[uix].Loc >= 0) and (MyUnit[uix].Job > jNone) then
337 begin
338 JobLocOfSettler[uix] := MyUnit[uix].Loc;
339 SettlerOfJobLoc[MyUnit[uix].Loc] := uix;
340 DistToLoc[MyUnit[uix].Loc] := 0
341 end;
342
343 // assign remaining jobs to remaining settlers
344 UnitsToAssign := true;
345 while UnitsToAssign do
346 begin
347 UnitsToAssign := false;
348 for uix := 0 to RO.nUn - 1 do
349 if JobLocOfSettler[uix] = ToAssign then
350 begin
351 BestJob := jNone;
352 BestScore := -999999;
353 fillchar(TileChecked, MapSize * sizeof(boolean), false);
354 Pile.Create(MapSize);
355 Pile.Put(MyUnit[uix].Loc, 0);
356 // start search for new job at current location
357 while Pile.Get(TestLoc, TestDistance) do
358 begin
359 // add surrounding tiles to queue, but only if there's a chance to beat BestScore
360 if MaxScore - DistanceScore * (TestDistance + 1) >= BestScore then
361 begin
362 V8_to_Loc(TestLoc, Adjacent);
363 for V8 := 0 to 7 do
364 begin
365 NextLoc := Adjacent[V8];
366 if (NextLoc >= 0) and not TileChecked[NextLoc] and
367 (Map[NextLoc] and fTerrain <> fUNKNOWN) then
368 begin
369 StepSize := StepSizeByTerrain[Map[NextLoc] and fTerrain];
370 if (StepSize > 0) // no water or arctic tile
371 and (Map[NextLoc] and (fUnit or fOwned) <> fUnit)
372 // no foreign unit
373 and ((RO.Territory[NextLoc] < 0) or
374 (RO.Territory[NextLoc] = me)) // no foreign territory
375 and (Map[TestLoc] and Map[NextLoc] and fInEnemyZoC = 0) then
376 // move not prevented by ZoC
377 Pile.Put(NextLoc, TestDistance + StepSize)
378 // simplification, only optimal for 150 mp units in land with no roads
379 end;
380 end;
381 end;
382
383 // check tile for job
384 if (TileJob[TestLoc] > jNone) and
385 ((MyModel[MyUnit[uix].mix].Kind <> mkSlaves) or
386 (TileJob[TestLoc] <> jCity)) and
387 ((SettlerOfJobLoc[TestLoc] < 0) or
388 (DistToLoc[TestLoc] > TestDistance)) then
389 begin
390 TestScore := integer(TileJobScore[TestLoc]) - DistanceScore *
391 TestDistance;
392 if TestScore > BestScore then
393 BestCount := 0;
394 if TestScore >= BestScore then
395 begin
396 inc(BestCount);
397 if random(BestCount) = 0 then
398 begin
399 BestScore := TestScore;
400 BestLoc := TestLoc;
401 BestJob := TileJob[TestLoc];
402 BestDistance := TestDistance
403 end
404 end;
405 end;
406 TileChecked[TestLoc] := true;
407 end;
408 Pile.Free;
409
410 if BestJob > jNone then
411 begin // new job found for this unit
412 if SettlerOfJobLoc[BestLoc] >= 0 then
413 begin // another unit was already assigned to this job, but is not as close -- reassign that unit!
414 JobLocOfSettler[SettlerOfJobLoc[BestLoc]] := ToAssign;
415 UnitsToAssign := true;
416 end;
417 JobLocOfSettler[uix] := BestLoc;
418 SettlerOfJobLoc[BestLoc] := uix;
419 DistToLoc[BestLoc] := BestDistance
420 end
421 else
422 JobLocOfSettler[uix] := -1; // no jobs for this settler
423 end; // for uix
424 end;
425
426 // move settlers and start new jobs
427 for uix := 0 to RO.nUn - 1 do
428 with MyUnit[uix] do
429 if (Loc >= 0) and (Job = jNone) and (JobLocOfSettler[uix] >= 0) then
430 begin
431 if Loc <> JobLocOfSettler[uix] then
432 repeat
433 MoveResult := Unit_Move(uix, JobLocOfSettler[uix])
434 until (MoveResult < rExecuted) or
435 (MoveResult and (rLocationReached or rMoreTurns or
436 rUnitRemoved) <> 0);
437 if (Loc = JobLocOfSettler[uix]) and (Movement >= 100) then
438 Unit_StartJob(uix, TileJob[JobLocOfSettler[uix]]);
439 end;
440end; // JobAssignment_Go
441
442
443// ------------------------------------------------------------------------------
444// Map Analysis
445
446procedure TToolAI.AnalyzeMap;
447var
448 i, j, Loc, Loc1, V8, Count, Kind, MostIndex: integer;
449 Adjacent: TVicinity8Loc;
450 IndexOfID: array [0 .. lxmax * lymax - 1] of smallint;
451 IDOfIndex: array [0 .. lxmax * lymax div 2 - 1] of smallint;
452begin
453 fillchar(District, MapSize * 4, $FF);
454 for Loc := 0 to MapSize - 1 do
455 if Map[Loc] and fTerrain = fUNKNOWN then
456 Formation[Loc] := nfUndiscovered
457 else if Map[Loc] and fTerrain = fArctic then
458 Formation[Loc] := nfPole
459 else if Map[Loc] and fPeace <> 0 then
460 Formation[Loc] := nfPeace
461 else
462 begin
463 Formation[Loc] := Loc;
464 V8_to_Loc(Loc, Adjacent);
465 for V8 := 0 to 7 do
466 begin
467 Loc1 := Adjacent[V8];
468 if (Loc1 < Loc) and (Loc1 >= 0) and (Formation[Loc1] >= 0) and
469 ((Map[Loc1] and fTerrain >= fGrass) = (Map[Loc] and fTerrain >=
470 fGrass)) then
471 if Formation[Loc] = Loc then
472 Formation[Loc] := Formation[Loc1]
473 else if Formation[Loc] < Formation[Loc1] then
474 ReplaceD(@Formation[Formation[Loc1]], @Formation[Loc + 1],
475 Formation[Loc1], Formation[Loc])
476 else if Formation[Loc] > Formation[Loc1] then
477 ReplaceD(@Formation[Formation[Loc]], @Formation[Loc + 1],
478 Formation[Loc], Formation[Loc1]);
479 end;
480 if (RO.Territory[Loc] = me) and (Map[Loc] and fTerrain >= fGrass) then
481 begin
482 District[Loc] := Loc;
483 for V8 := 0 to 7 do
484 begin
485 Loc1 := Adjacent[V8];
486 if (Loc1 < Loc) and (Loc1 >= 0) and (District[Loc1] >= 0) then
487 if District[Loc] = Loc then
488 District[Loc] := District[Loc1]
489 else if District[Loc] < District[Loc1] then
490 ReplaceD(@District[District[Loc1]], @District[Loc + 1],
491 District[Loc1], District[Loc])
492 else if District[Loc] > District[Loc1] then
493 ReplaceD(@District[District[Loc]], @District[Loc + 1],
494 District[Loc], District[Loc1]);
495 end
496 end
497 end;
498
499 // sort continents, oceans and districts by size
500 for Kind := 0 to 2 do
501 begin
502 fillchar(IndexOfID, MapSize * 2, 0);
503 case Kind of
504 0: // continents
505 for Loc := 0 to MapSize - 1 do
506 if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain >= fGrass) then
507 inc(IndexOfID[Formation[Loc]]);
508 1: // oceans
509 for Loc := 0 to MapSize - 1 do
510 if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain < fGrass) then
511 inc(IndexOfID[Formation[Loc]]);
512 2: // districts
513 for Loc := 0 to MapSize - 1 do
514 if District[Loc] >= 0 then
515 inc(IndexOfID[District[Loc]]);
516 end;
517
518 Count := 0;
519 for Loc := 0 to MapSize - 1 do
520 if IndexOfID[Loc] > 0 then
521 begin
522 IDOfIndex[Count] := Loc;
523 inc(Count);
524 end;
525 for i := 0 to Count - 2 do
526 begin
527 MostIndex := i;
528 for j := i + 1 to Count - 1 do
529 if IndexOfID[IDOfIndex[j]] > IndexOfID[IDOfIndex[MostIndex]] then
530 MostIndex := j;
531 if MostIndex <> i then
532 begin
533 j := IDOfIndex[i];
534 IDOfIndex[i] := IDOfIndex[MostIndex];
535 IDOfIndex[MostIndex] := j;
536 end
537 end;
538 for i := 0 to Count - 1 do
539 IndexOfID[IDOfIndex[i]] := i;
540
541 case Kind of
542 0: // continents
543 begin
544 nContinent := Count;
545 for Loc := 0 to MapSize - 1 do
546 if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain >= fGrass) then
547 Formation[Loc] := IndexOfID[Formation[Loc]];
548 end;
549 1: // oceans
550 begin
551 nOcean := Count;
552 for Loc := 0 to MapSize - 1 do
553 if (Formation[Loc] >= 0) and (Map[Loc] and fTerrain < fGrass) then
554 Formation[Loc] := IndexOfID[Formation[Loc]];
555 end;
556 2: // districts
557 begin
558 nDistrict := Count;
559 for Loc := 0 to MapSize - 1 do
560 if District[Loc] >= 0 then
561 District[Loc] := IndexOfID[District[Loc]];
562 end;
563 end
564 end;
565end;
566
567
568// ------------------------------------------------------------------------------
569// Path Finding
570
571const
572 // basic move styles
573 msGround = $00000000;
574 msNoGround = $10000000;
575 msAlpine = $20000000;
576 msOver = $40000000;
577 msSpy = $50000000;
578
579 // other
580 msHostile = $08000000;
581
582 // bits: |31|30|29|28|27|26 .. 16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
583 // ground: | Basic |Ho| Speed | HeavyCost | RailCost |
584 // other: | Basic | 0| Speed | X X X | MaxTerrType |
585
586function TToolAI.GetMyMoveStyle(mix, Health: integer): integer;
587begin
588 with MyModel[mix] do
589 begin
590 result := Speed shl 16;
591 case Domain of
592 dGround:
593 begin
594 inc(result, (50 + (Speed - 150) * 13 shr 7) shl 8); // HeavyCost
595 if RO.Wonder[woShinkansen].EffectiveOwner <> me then
596 inc(result, Speed * (4 * 1311) shr 17); // RailCost
597 if RO.Wonder[woGardens].EffectiveOwner <> me then
598 inc(result, msHostile);
599 if Kind = mkDiplomat then
600 inc(result, msSpy)
601 else if Cap[mcOver] > 0 then
602 inc(result, msOver)
603 else if Cap[mcAlpine] > 0 then
604 inc(result, msAlpine)
605 else
606 inc(result, msGround);
607 end;
608 dSea:
609 begin
610 result := Speed;
611 if RO.Wonder[woMagellan].EffectiveOwner = me then
612 inc(result, 200);
613 if Health < 100 then
614 result := ((result - 250) * Health div 5000) * 50 + 250;
615 result := result shl 16;
616 inc(result, msNoGround);
617 if Cap[mcNav] > 0 then
618 inc(result);
619 end;
620 dAir:
621 inc(result, msNoGround + fUNKNOWN xor 1 - 1);
622 end;
623 end
624end;
625
626function TToolAI.CheckStep(MoveStyle, TimeBeforeStep, CrossCorner: integer;
627 var TimeAfterStep, RecoverTurns: integer; FromTile, ToTile: integer): integer;
628var
629 MoveCost, RecoverCost: integer;
630begin
631 assert(((FromTile and fTerrain <= fMountains) or
632 (FromTile and fTerrain = fUNKNOWN)) and ((ToTile and fTerrain <= fMountains)
633 or (ToTile and fTerrain = fUNKNOWN)));
634 // do not pass location codes for FromTile and ToTile!
635 RecoverTurns := 0;
636 if MoveStyle < msGround + $10000000 then
637 begin // common ground units
638 if (ToTile + 1) and fTerrain < fGrass + 1 then
639 result := csForbiddenTile
640 else if (ToTile and not FromTile and fPeace = 0) and
641 (ToTile and (fUnit or fOwned) <> fUnit) then
642 if (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or
643 fOwned) or (ToTile and FromTile and (fInEnemyZoC or fOwnZoCUnit) <>
644 fInEnemyZoC) then
645 begin // ZoC is ok
646 if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0)
647 then
648 begin // no railroad
649 if (ToTile and (fRoad or fRR or fCity) <> 0) and
650 (FromTile and (fRoad or fRR or fCity) <> 0) or
651 (FromTile and ToTile and (fRiver or fCanal) <> 0) then
652 MoveCost := 20 // move along road, river or canal
653 else
654 begin
655 case Terrain[ToTile and fTerrain].MoveCost of
656 1:
657 MoveCost := 50; // plain terrain
658 2:
659 MoveCost := MoveStyle shr 8 and $FF; // heavy terrain
660 else // mountains
661 begin
662 if TimeBeforeStep and $FFF + MoveStyle shr 16 and $7FF <= $800
663 then
664 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800
665 else
666 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $2800;
667 // must wait for next turn
668 if (MoveStyle and msHostile <> 0) and
669 ((FromTile and (fTerrain or fSpecial1) = fDesert) or
670 (FromTile and fTerrain = fArctic)) and
671 (FromTile and (fCity or fRiver or fCanal) = 0) then
672 begin
673 RecoverCost := ($800 - TimeBeforeStep and $FFF) * 5 shr 1;
674 while RecoverCost > 0 do
675 begin
676 inc(RecoverTurns);
677 dec(RecoverCost, MoveStyle shr 16 and $7FF);
678 end;
679 end;
680 result := csOk;
681 if ToTile and fPeace <> 0 then
682 result := csCheckTerritory;
683 exit
684 end;
685 end
686 end
687 end
688 else
689 MoveCost := MoveStyle and $FF; // move along railroad
690
691 inc(MoveCost, MoveCost shl CrossCorner);
692 if (MoveStyle and msHostile = 0) or
693 (ToTile and (fTerrain or fSpecial1) <> fDesert) and
694 (ToTile and fTerrain <> fArctic) or
695 (ToTile and (fCity or fRiver or fCanal) <> 0) then
696 RecoverCost := 0
697 else
698 RecoverCost := (MoveCost * 5) shr 1;
699 // damage from movement: MoveCost*DesertThurst/NoCityRecovery
700 if (TimeBeforeStep and $FFF + MoveCost <= $800) and
701 (TimeBeforeStep and $FFF < $800) then
702 TimeAfterStep := TimeBeforeStep + MoveCost
703 else
704 begin
705 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 -
706 MoveStyle shr 16 and $7FF + MoveCost; // must wait for next turn
707 if (MoveStyle and msHostile <> 0) and
708 ((FromTile and (fTerrain or fSpecial1) = fDesert) or
709 (FromTile and fTerrain = fArctic)) and
710 (FromTile and (fCity or fRiver or fCanal) = 0) then
711 inc(RecoverCost, ($800 - TimeBeforeStep and $FFF) * 5 shr 1);
712 end;
713 while RecoverCost > 0 do
714 begin
715 inc(RecoverTurns);
716 dec(RecoverCost, MoveStyle shr 16 and $7FF);
717 end;
718 result := csOk;
719 if ToTile and fPeace <> 0 then
720 result := csCheckTerritory
721 end
722 else
723 result := csForbiddenStep // ZoC violation
724 else
725 result := csForbiddenTile
726 end
727
728 else if MoveStyle < msNoGround + $10000000 then
729 begin // ships and aircraft
730 if ((ToTile and fTerrain xor 1 > MoveStyle and fTerrain) and
731 (ToTile and (fCity or fCanal) = 0)) or
732 (ToTile and not FromTile and fPeace <> 0) or
733 (ToTile and (fUnit or fOwned) = fUnit) then
734 result := csForbiddenTile
735 else
736 begin
737 MoveCost := 50 shl CrossCorner + 50;
738 if TimeBeforeStep and $FFF + MoveCost <= $800 then
739 TimeAfterStep := TimeBeforeStep + MoveCost
740 else
741 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 -
742 MoveStyle shr 16 and $7FF + MoveCost; // must wait for next turn
743 result := csOk;
744 if ToTile and fPeace <> 0 then
745 result := csCheckTerritory
746 end
747 end
748
749 else if MoveStyle < msAlpine + $10000000 then
750 begin // alpine
751 if (ToTile + 1) and fTerrain < fGrass + 1 then
752 result := csForbiddenTile
753 else if (ToTile and not FromTile and fPeace = 0) and
754 (ToTile and (fUnit or fOwned) <> fUnit) then
755 if (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or
756 fOwned) or (ToTile and FromTile and (fInEnemyZoC or fOwnZoCUnit) <>
757 fInEnemyZoC) then
758 begin
759 if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0)
760 then
761 MoveCost := 20 // no railroad
762 else
763 MoveCost := MoveStyle and $FF; // move along railroad
764 inc(MoveCost, MoveCost shl CrossCorner);
765 if (TimeBeforeStep and $FFF + MoveCost <= $800) and
766 (TimeBeforeStep and $FFF < $800) then
767 TimeAfterStep := TimeBeforeStep + MoveCost
768 else
769 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 -
770 MoveStyle shr 16 and $7FF + MoveCost; // must wait for next turn
771 result := csOk;
772 if ToTile and fPeace <> 0 then
773 result := csCheckTerritory
774 end
775 else
776 result := csForbiddenStep // ZoC violation
777 else
778 result := csForbiddenTile
779 end
780
781 else if MoveStyle < msOver + $10000000 then
782 begin // overweight
783 if (ToTile + 1) and fTerrain < fGrass + 1 then
784 result := csForbiddenTile
785 else if (ToTile and not FromTile and fPeace = 0) and
786 (ToTile and (fUnit or fOwned) <> fUnit) then
787 if (FromTile and fCity <> 0) or (ToTile and (fCity or fOwned) = fCity or
788 fOwned) or (ToTile and FromTile and (fInEnemyZoC or fOwnZoCUnit) <>
789 fInEnemyZoC) then
790 begin
791 if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0)
792 then
793 begin // no railroad
794 if (ToTile and (fRoad or fRR or fCity) <> 0) and
795 (FromTile and (fRoad or fRR or fCity) <> 0) or
796 (FromTile and ToTile and (fRiver or fCanal) <> 0) then
797 MoveCost := 40 // move along road, river or canal
798 else
799 begin
800 result := csForbiddenTile;
801 exit
802 end
803 end
804 else
805 MoveCost := MoveStyle and $FF; // move along railroad
806 inc(MoveCost, MoveCost shl CrossCorner);
807 if (TimeBeforeStep and $FFF + MoveCost <= $800) and
808 (TimeBeforeStep and $FFF < $800) then
809 TimeAfterStep := TimeBeforeStep + MoveCost
810 else
811 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 -
812 MoveStyle shr 16 and $7FF + MoveCost; // must wait for next turn
813 result := csOk;
814 if ToTile and fPeace <> 0 then
815 result := csCheckTerritory
816 end
817 else
818 result := csForbiddenStep // ZoC violation
819 else
820 result := csForbiddenTile
821 end
822
823 else { if MoveStyle<msSpy+$10000000 then }
824 begin // spies
825 if (ToTile + 1) and fTerrain < fGrass + 1 then
826 result := csForbiddenTile
827 else if ToTile and (fUnit or fOwned) <> fUnit then
828 begin
829 if (ToTile and (fRR or fCity) = 0) or (FromTile and (fRR or fCity) = 0)
830 then
831 begin // no railroad
832 if (ToTile and (fRoad or fRR or fCity) <> 0) and
833 (FromTile and (fRoad or fRR or fCity) <> 0) or
834 (FromTile and ToTile and (fRiver or fCanal) <> 0) then
835 MoveCost := 20 // move along road, river or canal
836 else
837 begin
838 case Terrain[ToTile and fTerrain].MoveCost of
839 1:
840 MoveCost := 50; // plain terrain
841 2:
842 MoveCost := MoveStyle shr 8 and $FF; // heavy terrain
843 else // mountains
844 begin
845 if TimeBeforeStep and $FFF + MoveStyle shr 16 and $7FF <= $800
846 then
847 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800
848 else
849 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $2800;
850 // must wait for next turn
851 result := csOk;
852 exit
853 end;
854 end
855 end
856 end
857 else
858 MoveCost := MoveStyle and $FF; // move along railroad
859 inc(MoveCost, MoveCost shl CrossCorner);
860 if (TimeBeforeStep and $FFF + MoveCost <= $800) and
861 (TimeBeforeStep and $FFF < $800) then
862 TimeAfterStep := TimeBeforeStep + MoveCost
863 else
864 TimeAfterStep := TimeBeforeStep and $7FFFF000 + $1800 -
865 MoveStyle shr 16 and $7FF + MoveCost; // must wait for next turn
866 result := csOk;
867 end
868 else
869 result := csForbiddenTile
870 end;
871end; // CheckStep
872
873(*
874 -------- Pathfinding Reference Implementation --------
875 var
876 MoveStyle,V8,Loc,Time,NextLoc,NextTime,RecoverTurns: integer;
877 Adjacent: TVicinity8Loc;
878 Reached: array[0..lxmax*lymax-1] of boolean;
879 begin
880 fillchar(Reached, MapSize, false);
881 MoveStyle:=GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health);
882 Pile.Create(MapSize);
883 Pile.Put(MyUnit[uix].Loc, $800-MyUnit[uix].Movement);
884 while Pile.Get(Loc, Time) do
885 begin
886 // todo: check exit condition, e.g. whether destination reached
887
888 Reached[Loc]:=true;
889 V8_to_Loc(Loc, Adjacent);
890 for V8:=0 to 7 do
891 begin
892 NextLoc:=Adjacent[V8];
893 if (NextLoc>=0) and not Reached[NextLoc] then
894 case CheckStep(MoveStyle, Time, V8 and 1, NextTime, RecoverTurns, Map[Loc], Map[NextLoc]) of
895 csOk:
896 Pile.Put(NextLoc, NextTime+RecoverTurns*$1000);
897 csForbiddenTile:
898 Reached[NextLoc]:=true; // don't check moving there again
899 csCheckTerritory:
900 if RO.Territory[NextLoc]=RO.Territory[Loc] then
901 Pile.Put(NextLoc, NextTime+RecoverTurns*$1000);
902 end
903 end;
904 end;
905 Pile.Free;
906 end;
907*)
908
909function TToolAI.Unit_MoveEx(uix, ToLoc: integer; Options: integer): integer;
910var
911 Loc, NextLoc, Temp, FromLoc, EndLoc, Time, V8, MoveResult, RecoverTurns,
912 NextTime, MoveStyle: integer;
913 Adjacent: TVicinity8Loc;
914 PreLoc: array [0 .. lxmax * lymax - 1] of integer;
915 Reached: array [0 .. lxmax * lymax - 1] of boolean;
916begin
917 result := eOk;
918 FromLoc := MyUnit[uix].Loc;
919 if FromLoc = ToLoc then
920 exit;
921
922 fillchar(Reached, MapSize, false);
923 MoveStyle := GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health);
924 EndLoc := -1;
925 Pile.Create(MapSize);
926 Pile.Put(FromLoc, $800 - MyUnit[uix].Movement);
927 while Pile.Get(Loc, Time) do
928 begin
929 if (Loc = ToLoc) or (ToLoc = maNextCity) and (Map[Loc] and fCity <> 0) and
930 (Map[Loc] and fOwned <> 0) then
931 begin
932 EndLoc := Loc;
933 Break;
934 end;
935 Reached[Loc] := true;
936 V8_to_Loc(Loc, Adjacent);
937 for V8 := 0 to 7 do
938 begin
939 NextLoc := Adjacent[V8];
940 if NextLoc >= 0 then
941 if (NextLoc = ToLoc) and (Options and mxAdjacent <> 0) then
942 begin
943 EndLoc := Loc;
944 Break
945 end
946 else if not Reached[NextLoc] then
947 case CheckStep(MoveStyle, Time, V8 and 1, NextTime, RecoverTurns,
948 Map[Loc], Map[NextLoc]) of
949 csOk:
950 if Pile.Put(NextLoc, NextTime + RecoverTurns * $1000) then
951 PreLoc[NextLoc] := Loc;
952 csForbiddenTile:
953 Reached[NextLoc] := true; // don't check moving there again
954 csCheckTerritory:
955 if RO.Territory[NextLoc] = RO.Territory[Loc] then
956 if Pile.Put(NextLoc, NextTime + RecoverTurns * $1000) then
957 PreLoc[NextLoc] := Loc;
958 end
959 end;
960 if EndLoc >= 0 then
961 Break;
962 end;
963 Pile.Free;
964
965 if EndLoc >= 0 then
966 begin
967 Loc := EndLoc;
968 NextLoc := PreLoc[Loc];
969 while Loc <> FromLoc do
970 begin // invert meaning of PreLoc
971 Temp := Loc;
972 Loc := NextLoc;
973 NextLoc := PreLoc[Loc];
974 PreLoc[Loc] := Temp;
975 end;
976 while Loc <> EndLoc do
977 begin
978 Loc := PreLoc[Loc];
979 MoveResult := Unit_Step(uix, Loc);
980 if (MoveResult <> eOk) and (MoveResult <> eLoaded) then
981 begin
982 result := MoveResult;
983 Break
984 end;
985 end;
986 end
987 else
988 result := eNoWay;
989end;
990
991
992// ------------------------------------------------------------------------------
993// Oversea Transport
994
995procedure TToolAI.SeaTransport_BeginInitialize;
996begin
997 fillchar(TransportAvailable, RO.nUn * sizeof(integer), $FF); // -1
998 InitComplete := false;
999 HaveDestinations := false;
1000 nTransportLoad := 0;
1001 TransportMoveStyle := 0;
1002 TransportCapacity := $100;
1003 Pile.Create(MapSize);
1004end;
1005
1006procedure TToolAI.SeaTransport_AddLoad(uix: integer);
1007var
1008 i: integer;
1009begin
1010 assert(not InitComplete); // call order violation!
1011 if Map[MyUnit[uix].Loc] and fTerrain < fGrass then
1012 exit;
1013 for i := 0 to nTransportLoad - 1 do
1014 if uix = uixTransportLoad[i] then
1015 exit;
1016 uixTransportLoad[nTransportLoad] := uix;
1017 inc(nTransportLoad);
1018end;
1019
1020procedure TToolAI.SeaTransport_AddTransport(uix: integer);
1021var
1022 MoveStyle: integer;
1023begin
1024 assert(not InitComplete); // call order violation!
1025 assert(MyModel[MyUnit[uix].mix].Cap[mcSeaTrans] > 0);
1026 TransportAvailable[uix] := 1;
1027 with MyModel[MyUnit[uix].mix] do
1028 begin
1029 if MTrans * Cap[mcSeaTrans] < TransportCapacity then
1030 TransportCapacity := MTrans * Cap[mcSeaTrans];
1031 MoveStyle := GetMyMoveStyle(MyUnit[uix].mix, 100);
1032 if (TransportMoveStyle = 0) or (MoveStyle < TransportMoveStyle) and
1033 (MoveStyle and not TransportMoveStyle and 1 = 0) or
1034 (not MoveStyle and TransportMoveStyle and 1 <> 0) then
1035 TransportMoveStyle := MoveStyle;
1036 end
1037end;
1038
1039procedure TToolAI.SeaTransport_AddDestination(Loc: integer);
1040begin
1041 assert(not InitComplete); // call order violation!
1042 Pile.Put(Loc, $800);
1043 HaveDestinations := true;
1044end;
1045
1046procedure TToolAI.SeaTransport_EndInitialize;
1047var
1048 Loc0, Time0, V8, Loc1, ArriveTime, RecoverTurns: integer;
1049 Adjacent: TVicinity8Loc;
1050begin
1051 assert(not InitComplete); // call order violation!
1052 InitComplete := true;
1053 if HaveDestinations then
1054 begin // calculate TurnsAfterLoad from destination locs
1055 fillchar(TurnsAfterLoad, MapSize, $FF); // -1
1056 while Pile.Get(Loc0, Time0) do
1057 begin // search backward
1058 if Time0 = $800 then
1059 TurnsAfterLoad[Loc0] := 1
1060 else
1061 TurnsAfterLoad[Loc0] := Time0 shr 12;
1062 V8_to_Loc(Loc0, Adjacent);
1063 for V8 := 0 to 7 do
1064 begin
1065 Loc1 := Adjacent[V8];
1066 if (Loc1 >= 0) and (TurnsAfterLoad[Loc1] = -1) then
1067 begin
1068 case CheckStep(TransportMoveStyle, Time0, V8 and 1, ArriveTime,
1069 RecoverTurns, Map[Loc0], Map[Loc1]) of
1070 csOk:
1071 Pile.Put(Loc1, ArriveTime);
1072 csForbiddenStep:
1073 TurnsAfterLoad[Loc1] := -2;
1074 end;
1075 end
1076 end
1077 end;
1078 end;
1079 Pile.Free;
1080end;
1081
1082function TToolAI.SeaTransport_MakeGroupPlan(var TransportPlan
1083 : TGroupTransportPlan): boolean;
1084var
1085 V8, i, j, iPicked, uix, Loc0, Time0, Loc1, RecoverTurns, MoveStyle,
1086 TurnsLoaded, TurnCount, tuix, tuix1, ArriveTime, TotalDelay, BestTotalDelay,
1087 GroupCount, BestGroupCount, BestLoadLoc, FullMovementLoc, nSelectedLoad, f,
1088 OriginContinent, a, b: integer;
1089 CompleteFlag, NotReachedFlag, ContinueUnit: cardinal;
1090 IsComplete, ok, IsFirstLoc: boolean;
1091 StartLocPtr, ArrivedEnd: pinteger;
1092 Adjacent: TVicinity8Loc;
1093 uixSelectedLoad: array [0 .. 15] of integer;
1094 tuixSelectedLoad: array [0 .. 15] of integer;
1095 Arrived: array [0 .. lxmax * lymax] of cardinal;
1096 ResponsibleTransport: array [0 .. lxmax * lymax - 1] of smallint;
1097 TurnsBeforeLoad: array [0 .. lxmax * lymax - 1] of shortint;
1098 GroupComplete: array [0 .. lxmax * lymax - 1] of boolean;
1099begin
1100 assert(InitComplete); // call order violation!
1101
1102 if HaveDestinations and (nTransportLoad > 0) then
1103 begin // transport and units already adjacent?
1104 for uix := 0 to RO.nUn - 1 do
1105 if (TransportAvailable[uix] > 0) and
1106 (Map[MyUnit[uix].Loc] and fTerrain = fShore) then
1107 begin
1108 GroupCount := 0;
1109 for tuix := 0 to nTransportLoad - 1 do
1110 begin
1111 Loc_to_ab(MyUnit[uix].Loc, MyUnit[uixTransportLoad[tuix]].Loc, a, b);
1112 if (abs(a) <= 1) and (abs(b) <= 1) then
1113 begin
1114 assert((a <> 0) or (b <> 0));
1115 inc(GroupCount);
1116 end
1117 end;
1118 if (GroupCount = nTransportLoad) or (GroupCount >= TransportCapacity)
1119 then
1120 begin
1121 TransportPlan.LoadLoc := MyUnit[uix].Loc;
1122 TransportPlan.uixTransport := uix;
1123 TransportAvailable[uix] := 0;
1124 TransportPlan.TurnsEmpty := 0;
1125 TransportPlan.TurnsLoaded := TurnsAfterLoad[TransportPlan.LoadLoc];
1126 TransportPlan.nLoad := 0;
1127 for tuix := nTransportLoad - 1 downto 0 do
1128 begin
1129 Loc_to_ab(TransportPlan.LoadLoc, MyUnit[uixTransportLoad[tuix]]
1130 .Loc, a, b);
1131 if (abs(a) <= 1) and (abs(b) <= 1) then
1132 begin
1133 TransportPlan.uixLoad[TransportPlan.nLoad] :=
1134 uixTransportLoad[tuix];
1135 uixTransportLoad[tuix] := uixTransportLoad[nTransportLoad - 1];
1136 dec(nTransportLoad);
1137 inc(TransportPlan.nLoad);
1138 if TransportPlan.nLoad = TransportCapacity then
1139 Break;
1140 end;
1141 end;
1142 result := true;
1143 exit;
1144 end
1145 end
1146 end;
1147
1148 while HaveDestinations and (nTransportLoad > 0) do
1149 begin
1150 // select units from same continent
1151 fillchar(Arrived, 4 * nContinent, 0); // misuse Arrived as counter
1152 for tuix := 0 to nTransportLoad - 1 do
1153 begin
1154 assert(Map[MyUnit[uixTransportLoad[tuix]].Loc] and fTerrain >= fGrass);
1155 f := Formation[MyUnit[uixTransportLoad[tuix]].Loc];
1156 if f >= 0 then
1157 inc(Arrived[f]);
1158 end;
1159 OriginContinent := 0;
1160 for f := 1 to nContinent - 1 do
1161 if Arrived[f] > Arrived[OriginContinent] then
1162 OriginContinent := f;
1163 nSelectedLoad := 0;
1164 for tuix := 0 to nTransportLoad - 1 do
1165 if Formation[MyUnit[uixTransportLoad[tuix]].Loc] = OriginContinent then
1166 begin
1167 tuixSelectedLoad[nSelectedLoad] := tuix;
1168 uixSelectedLoad[nSelectedLoad] := uixTransportLoad[tuix];
1169 inc(nSelectedLoad);
1170 if nSelectedLoad = 16 then
1171 Break;
1172 end;
1173
1174 Pile.Create(MapSize);
1175 fillchar(ResponsibleTransport, MapSize * 2, $FF); // -1
1176 fillchar(TurnsBeforeLoad, MapSize, $FF); // -1
1177 ok := false;
1178 for uix := 0 to RO.nUn - 1 do
1179 if TransportAvailable[uix] > 0 then
1180 begin
1181 ok := true;
1182 Pile.Put(MyUnit[uix].Loc, ($800 - MyUnit[uix].Movement) shl 12 + uix);
1183 end;
1184 if not ok then // no transports
1185 begin
1186 TransportPlan.LoadLoc := -1;
1187 result := false;
1188 Pile.Free;
1189 exit
1190 end;
1191 while Pile.Get(Loc0, Time0) do
1192 begin
1193 uix := Time0 and $FFF;
1194 Time0 := Time0 shr 12;
1195 ResponsibleTransport[Loc0] := uix;
1196 TurnsBeforeLoad[Loc0] := Time0 shr 12;
1197 V8_to_Loc(Loc0, Adjacent);
1198 for V8 := 0 to 7 do
1199 begin
1200 Loc1 := Adjacent[V8];
1201 if (Loc1 >= 0) and (ResponsibleTransport[Loc1] < 0) then
1202 case CheckStep(GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health),
1203 Time0, V8 and 1, ArriveTime, RecoverTurns, Map[Loc0], Map[Loc1]) of
1204 csOk:
1205 Pile.Put(Loc1, ArriveTime shl 12 + uix);
1206 csForbiddenTile:
1207 ResponsibleTransport[Loc1] := RO.nUn; // don't check again
1208 end
1209 end
1210 end;
1211
1212 fillchar(Arrived, MapSize * 4, $55); // set NotReachedFlag for all tiles
1213 fillchar(GroupComplete, MapSize, false);
1214 BestLoadLoc := -1;
1215
1216 // check direct loading
1217 for tuix := 0 to nSelectedLoad - 1 do
1218 begin
1219 uix := uixSelectedLoad[tuix];
1220 if MyUnit[uix].Movement = integer(MyModel[MyUnit[uix].mix].Speed) then
1221 begin
1222 NotReachedFlag := 1 shl (2 * tuix);
1223 CompleteFlag := NotReachedFlag shl 1;
1224 V8_to_Loc(MyUnit[uix].Loc, Adjacent);
1225 for V8 := 0 to 7 do
1226 begin
1227 Loc1 := Adjacent[V8];
1228 if (Loc1 >= 0) and (Map[Loc1] and fTerrain < fGrass) and
1229 not GroupComplete[Loc1] then
1230 begin // possible transport start location
1231 Arrived[Loc1] := (Arrived[Loc1] or CompleteFlag) and
1232 not NotReachedFlag;
1233 if (TurnsBeforeLoad[Loc1] >= 0) and (TurnsAfterLoad[Loc1] >= 0) then
1234 begin
1235 i := 1;
1236 GroupCount := 0;
1237 for tuix1 := 0 to nSelectedLoad - 1 do
1238 begin
1239 if Arrived[Loc1] and i = 0 then
1240 inc(GroupCount);
1241 i := i shl 2;
1242 end;
1243 assert(GroupCount <= TransportCapacity);
1244 if (GroupCount = TransportCapacity) or (GroupCount = nSelectedLoad)
1245 then
1246 GroupComplete[Loc1] := true;
1247 TotalDelay := TurnsBeforeLoad[Loc1] + TurnsAfterLoad[Loc1];
1248 if (BestLoadLoc < 0) or
1249 (GroupCount shl 16 - TotalDelay > BestGroupCount shl 16 -
1250 BestTotalDelay) then
1251 begin
1252 BestLoadLoc := Loc1;
1253 BestGroupCount := GroupCount;
1254 BestTotalDelay := TotalDelay
1255 end
1256 end
1257 end
1258 end;
1259 end
1260 end;
1261
1262 TurnCount := 0;
1263 ArrivedEnd := @Arrived[MapSize];
1264
1265 // check moving+loading
1266 ContinueUnit := 1 shl nSelectedLoad - 1;
1267 while (ContinueUnit > 0) and
1268 ((BestLoadLoc < 0) or (TurnCount < BestTotalDelay - 2)) do
1269 begin
1270 for tuix := 0 to nSelectedLoad - 1 do
1271 if 1 shl tuix and ContinueUnit <> 0 then
1272 begin
1273 uix := uixSelectedLoad[tuix];
1274 MoveStyle := GetMyMoveStyle(MyUnit[uix].mix, MyUnit[uix].Health);
1275 NotReachedFlag := 1 shl (2 * tuix);
1276 CompleteFlag := NotReachedFlag shl 1;
1277 FullMovementLoc := -1;
1278
1279 Pile.Empty;
1280 if TurnCount = 0 then
1281 begin
1282 Pile.Put(MyUnit[uix].Loc, $1800 - MyUnit[uix].Movement);
1283 if MyUnit[uix].Movement = integer(MyModel[MyUnit[uix].mix].Speed)
1284 then
1285 FullMovementLoc := MyUnit[uix].Loc;
1286 // surrounding tiles can be loaded immediately
1287 StartLocPtr := ArrivedEnd;
1288 end
1289 else
1290 StartLocPtr := @Arrived;
1291 IsFirstLoc := true;
1292
1293 repeat
1294 if StartLocPtr <> ArrivedEnd then
1295 // search next movement start location for this turn
1296 StartLocPtr := NextZero(StartLocPtr, ArrivedEnd, CompleteFlag or
1297 NotReachedFlag);
1298 if StartLocPtr <> ArrivedEnd then
1299 begin
1300 Loc0 := (integer(StartLocPtr) - integer(@Arrived)) shr 2;
1301 inc(StartLocPtr);
1302 Time0 := $800
1303 end
1304 else if not Pile.Get(Loc0, Time0) then
1305 begin
1306 if IsFirstLoc then
1307 ContinueUnit := ContinueUnit and not(1 shl tuix);
1308 Break;
1309 end;
1310 IsFirstLoc := false;
1311
1312 Arrived[Loc0] := Arrived[Loc0] and not NotReachedFlag;
1313 if not GroupComplete[Loc0] and (Map[Loc0] and fTerrain <> fMountains)
1314 then
1315 begin // check whether group complete -- no mountains because complete flag might be faked there
1316 i := 1;
1317 GroupCount := 0;
1318 for tuix1 := 0 to nSelectedLoad - 1 do
1319 begin
1320 if Arrived[Loc0] and i = 0 then
1321 inc(GroupCount);
1322 i := i shl 2;
1323 end;
1324 assert(GroupCount <= TransportCapacity);
1325 if (GroupCount = TransportCapacity) or (GroupCount = nSelectedLoad)
1326 then
1327 GroupComplete[Loc0] := true
1328 end;
1329
1330 V8_to_Loc(Loc0, Adjacent);
1331 IsComplete := true;
1332 for V8 := 0 to 7 do
1333 begin
1334 Loc1 := Adjacent[V8];
1335 if (Loc1 < G.ly) or (Loc1 >= MapSize - G.ly) then
1336 Adjacent[V8] := -1 // pole, don't consider moving here
1337 else if Arrived[Loc1] and NotReachedFlag = 0 then
1338 Adjacent[V8] := -1 // unit has already arrived this tile
1339 else if GroupComplete[Loc1] then
1340 Adjacent[V8] := -1 // already other group complete
1341 else if Map[Loc1] and fTerrain < fGrass then
1342 begin // possible transport start location
1343 Arrived[Loc1] := (Arrived[Loc1] or CompleteFlag) and
1344 not NotReachedFlag;
1345 Adjacent[V8] := -1;
1346 if (TurnsBeforeLoad[Loc1] >= 0) and (TurnsAfterLoad[Loc1] >= 0)
1347 then
1348 begin
1349 i := 1;
1350 GroupCount := 0;
1351 for tuix1 := 0 to nSelectedLoad - 1 do
1352 begin
1353 if Arrived[Loc1] and i = 0 then
1354 inc(GroupCount);
1355 i := i shl 2;
1356 end;
1357 assert(GroupCount <= TransportCapacity);
1358 if (GroupCount = TransportCapacity) or
1359 (GroupCount = nSelectedLoad) then
1360 GroupComplete[Loc1] := true;
1361 if TurnsBeforeLoad[Loc1] > TurnCount + 1 then
1362 TotalDelay := TurnsBeforeLoad[Loc1] + TurnsAfterLoad[Loc1]
1363 else
1364 TotalDelay := TurnCount + 1 + TurnsAfterLoad[Loc1];
1365 if (BestLoadLoc < 0) or
1366 (GroupCount shl 16 - TotalDelay > BestGroupCount shl 16 -
1367 BestTotalDelay) then
1368 begin
1369 BestLoadLoc := Loc1;
1370 BestGroupCount := GroupCount;
1371 BestTotalDelay := TotalDelay
1372 end
1373 end
1374 end
1375 else if (Map[Loc1] and fTerrain = fMountains) and
1376 ((Map[Loc0] and (fRoad or fRR or fCity) = 0) or
1377 (Map[Loc1] and (fRoad or fRR or fCity) = 0)) and
1378 (Map[Loc0] and Map[Loc1] and (fRiver or fCanal) = 0) then
1379 begin // mountain delay too complicated for this algorithm
1380 Arrived[Loc1] := (Arrived[Loc1] or CompleteFlag) and
1381 not NotReachedFlag;
1382 Adjacent[V8] := -1;
1383 end
1384 else
1385 IsComplete := false;
1386 end;
1387 if IsComplete then
1388 begin
1389 Arrived[Loc0] := (Arrived[Loc0] or CompleteFlag) and
1390 not NotReachedFlag;
1391 continue
1392 end;
1393 IsComplete := true;
1394 for V8 := 0 to 7 do
1395 begin
1396 Loc1 := Adjacent[V8];
1397 if Loc1 >= 0 then
1398 begin
1399 ok := false;
1400 case CheckStep(MoveStyle, Time0, V8 and 1, ArriveTime,
1401 RecoverTurns, Map[Loc0], Map[Loc1]) of
1402 csOk:
1403 ok := true;
1404 csForbiddenTile:
1405 ; // !!! don't check moving there again
1406 csCheckTerritory:
1407 ok := RO.Territory[Loc1] = RO.Territory[Loc0];
1408 end;
1409 if ok and Pile.TestPut(Loc1, ArriveTime) then
1410 if ArriveTime < $2000 then
1411 Pile.Put(Loc1, ArriveTime)
1412 else
1413 IsComplete := false
1414 end
1415 end;
1416 if IsComplete then
1417 Arrived[Loc0] := (Arrived[Loc0] or CompleteFlag) and
1418 not NotReachedFlag;
1419 until false;
1420 end;
1421
1422 inc(TurnCount);
1423 end;
1424 Pile.Free;
1425
1426 if BestLoadLoc >= 0 then
1427 begin
1428 TransportPlan.LoadLoc := BestLoadLoc;
1429 TransportPlan.uixTransport := ResponsibleTransport[BestLoadLoc];
1430 TransportAvailable[TransportPlan.uixTransport] := 0;
1431 TransportPlan.TurnsEmpty := BestTotalDelay - TurnsAfterLoad[BestLoadLoc];
1432 TransportPlan.TurnsLoaded := TurnsAfterLoad[BestLoadLoc];
1433 TransportPlan.nLoad := 0;
1434 for tuix := nSelectedLoad - 1 downto 0 do
1435 if 1 shl (2 * tuix) and Arrived[BestLoadLoc] = 0 then
1436 begin
1437 assert(uixTransportLoad[tuixSelectedLoad[tuix]]
1438 = uixSelectedLoad[tuix]);
1439 TransportPlan.uixLoad[TransportPlan.nLoad] := uixSelectedLoad[tuix];
1440 uixTransportLoad[tuixSelectedLoad[tuix]] :=
1441 uixTransportLoad[nTransportLoad - 1];
1442 dec(nTransportLoad);
1443 inc(TransportPlan.nLoad)
1444 end;
1445 result := true;
1446 exit
1447 end;
1448
1449 // no loading location for a single of these units -- remove all
1450 // should be pretty rare case
1451 for tuix := nSelectedLoad - 1 downto 0 do
1452 begin
1453 assert(uixTransportLoad[tuixSelectedLoad[tuix]] = uixSelectedLoad[tuix]);
1454 uixTransportLoad[tuixSelectedLoad[tuix]] := uixTransportLoad
1455 [nTransportLoad - 1];
1456 dec(nTransportLoad);
1457 end;
1458 end;
1459 TransportPlan.LoadLoc := -1;
1460 result := false;
1461end;
1462
1463
1464// ------------------------------------------------------------------------------
1465
1466procedure SetAdvancedness;
1467var
1468 ad, j, Reduction, AgeThreshold: integer;
1469 known: array [0 .. nAdv - 1] of integer;
1470 procedure MarkPreqs(ad: integer);
1471 var
1472 i: integer;
1473 begin
1474 if known[ad] = 0 then
1475 begin
1476 known[ad] := 1;
1477 for i := 0 to 2 do
1478 if AdvPreq[ad, i] >= 0 then
1479 MarkPreqs(AdvPreq[ad, i]);
1480 end
1481 end;
1482
1483begin
1484 fillchar(Advancedness, sizeof(Advancedness), 0);
1485 for ad := 0 to nAdv - 1 do
1486 begin
1487 fillchar(known, sizeof(known), 0);
1488 MarkPreqs(ad);
1489 for j := 0 to nAdv - 1 do
1490 if known[j] > 0 then
1491 inc(Advancedness[ad]);
1492 end;
1493 AgeThreshold := Advancedness[adScience];
1494 Reduction := Advancedness[adScience] div 3;
1495 for ad := 0 to nAdv - 5 do
1496 if Advancedness[ad] >= AgeThreshold then
1497 dec(Advancedness[ad], Reduction);
1498 AgeThreshold := Advancedness[adMassProduction];
1499 Reduction := (Advancedness[adMassProduction] - Advancedness[adScience]) div 3;
1500 for ad := 0 to nAdv - 5 do
1501 if Advancedness[ad] >= AgeThreshold then
1502 dec(Advancedness[ad], Reduction)
1503end;
1504
1505initialization
1506
1507SetAdvancedness;
1508
1509end.
Note: See TracBrowser for help on using the repository browser.