Changeset 202 for trunk/UGame.pas


Ignore:
Timestamp:
May 17, 2018, 5:41:47 PM (6 years ago)
Author:
chronos
Message:
  • Modified: AI player related code moved to UClientAI unit. It is now extension of TClient class to simulate regular human client.
  • Modified: More work on client-server architecture.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/UGame.pas

    r199 r202  
    290290  TPlayerMode = (pmHuman, pmComputer);
    291291  TComputerAgressivity = (caLow, caMedium, caHigh);
    292   TComputer = class;
    293292  TUnitMove = class;
    294293
     
    310309    procedure CheckCounterMove(Move: TUnitMove);
    311310    procedure SetMode(AValue: TPlayerMode);
    312     function SetMove(CellFrom, CellTo: TCell; Power: Integer; Confirmation: Boolean = True): TUnitMove;
    313311    procedure UpdateRepeatMoves;
    314312    procedure RemoveEmptyUnitMoves;
     
    328326    TurnStats: TGameTurnStats;
    329327    Moves: TUnitMoves;
    330     Computer: TComputer;
     328    function SetMove(CellFrom, CellTo: TCell; Power: Integer; Confirmation: Boolean = True): TUnitMove;
    331329    procedure Reset;
    332330    function IsAlive: Boolean;
     
    343341    property Mode: TPlayerMode read FMode write SetMode;
    344342    property OnMove: TMoveEvent read FOnMove write FOnMove;
    345   end;
    346 
    347     { TComputer }
    348 
    349   TComputer = class
    350     Game: TGame;
    351     //Targets: TFPGObjectList<TPlayer>;
    352     CellProcessDirection: Boolean;
    353     Player: TPlayer;
    354     procedure AttackNeutral;
    355     procedure AttackPlayers;
    356     procedure InnerMoves;
    357     procedure IncreaseMoves;
    358     procedure Process;
    359     procedure FallBack;
    360     function AttackersCount(Cell: TCell): Integer;
    361343  end;
    362344
     
    474456    procedure SaveToFile(FileName: string);
    475457    procedure ComputePlayerStats;
    476     procedure NextTurn;
     458    procedure NextPlayer;
    477459    procedure CheckWinObjective;
    478460    constructor Create;
     
    502484
    503485procedure InitStrings;
     486function CellCompare(const Item1, Item2: TCell): Integer;
     487function CellCompareDescending(const Item1, Item2: TCell): Integer;
    504488
    505489resourcestring
     
    20962080  FGame := AValue;
    20972081  Moves.Game := AValue;
    2098   Computer.Game := AValue;
    20992082end;
    21002083
     
    21872170  PlayerMap.Player := Self;
    21882171  TurnStats := TGameTurnStats.Create;
    2189   Computer := TComputer.Create;
    2190   Computer.Player := Self;
    21912172end;
    21922173
     
    21942175begin
    21952176  //Client := nil;
    2196   FreeAndNil(Computer);
    21972177  FreeAndNil(TurnStats);
    21982178  FreeAndNil(PlayerMap);
     
    22142194  Agressivity := Source.Agressivity;
    22152195  Defensive := Source.Defensive;
    2216   Computer.Game := Source.Computer.Game;
    2217   Computer.CellProcessDirection := Source.Computer.CellProcessDirection;
    22182196end;
    22192197
     
    23012279  else if Item1.Power < Item2.Power then Result := 1
    23022280  else Result := 0;
    2303 end;
    2304 
    2305 { TComputer }
    2306 
    2307 procedure TComputer.AttackNeutral;
    2308 var
    2309   AllCells: TCells;
    2310   TotalPower: Integer;
    2311   AttackPower: Integer;
    2312   TotalAttackPower: Integer;
    2313   CanAttack: Integer;
    2314   TargetCells: TCells;
    2315   Cell: TCell;
    2316   NeighborCell: TCell;
    2317 const
    2318   AttackDiff = 1;
    2319 begin
    2320   AllCells := Game.Map.Cells;
    2321   TargetCells := TCells.Create;
    2322   TargetCells.FreeObjects := False;
    2323 
    2324   // Get list of all attack target cells
    2325   for Cell in AllCells do
    2326   with Cell do begin
    2327     if (Terrain <> ttVoid) and (Player = nil) then begin
    2328       CanAttack := 0;
    2329       for NeighborCell in Neighbors do
    2330       if NeighborCell.Player = Game.CurrentPlayer then begin
    2331         Inc(CanAttack);
    2332       end;
    2333       if CanAttack > 0 then TargetCells.Add(Cell);
    2334     end;
    2335   end;
    2336 
    2337   // Sort ascending to attack cells with lower power first
    2338   // Low power cells are better for expanding our teritorry
    2339   TargetCells.Sort(CellCompare);
    2340 
    2341   for Cell in TargetCells do
    2342   with Cell do begin
    2343       // Attack to not owned cell yet
    2344       // Count own possible power
    2345       TotalPower := 0;
    2346       for NeighborCell in Neighbors do
    2347       if NeighborCell.Player = Game.CurrentPlayer then
    2348         TotalPower := TotalPower + NeighborCell.GetAvialPower;
    2349 
    2350       // Attack if target is weaker
    2351       if TotalPower >= (Power + AttackDiff) then begin
    2352         TotalAttackPower := 0;
    2353         for NeighborCell in Neighbors do
    2354         if NeighborCell.Player = Game.CurrentPlayer then begin
    2355           // Use only necessary power
    2356           AttackPower := Power - TotalAttackPower + AttackDiff;
    2357           if NeighborCell.GetAvialPower < AttackPower then
    2358             AttackPower := NeighborCell.GetAvialPower;
    2359           Self.Player.SetMove(NeighborCell, Cell, AttackPower, False);
    2360           TotalAttackPower := TotalAttackPower + AttackPower;
    2361         end;
    2362       end;
    2363   end;
    2364 
    2365   FreeAndNil(TargetCells);
    2366 end;
    2367 
    2368 procedure TComputer.AttackPlayers;
    2369 var
    2370   AllCells: TCells;
    2371   TotalPower: Integer;
    2372   AttackPower: Integer;
    2373   TotalAttackPower: Integer;
    2374   CanAttack: Integer;
    2375   TargetCells: TCells;
    2376   TargetCell: TCell;
    2377   NeighborCell: TCell;
    2378 begin
    2379   if Player.Defensive then Exit;
    2380 
    2381   AllCells := Game.Map.Cells;
    2382   TargetCells := TCells.Create;
    2383   TargetCells.FreeObjects := False;
    2384 
    2385   // Get list of all attack target cells
    2386   for TargetCell in AllCells do begin
    2387     if (TargetCell.Terrain <> ttVoid) and (TargetCell.Player <> Player) and
    2388       (TargetCell.Player <> nil) then begin
    2389       CanAttack := 0;
    2390       for NeighborCell in TargetCell.Neighbors do
    2391       if NeighborCell.Player = Player then begin
    2392         Inc(CanAttack);
    2393       end;
    2394       if CanAttack > 0 then TargetCells.Add(TargetCell);
    2395     end;
    2396   end;
    2397 
    2398   // Sort descending to attack cells with higher power first
    2399   // Higher power enemy cells can grow faster and is more dangerous
    2400   TargetCells.Sort(CellCompareDescending);
    2401 
    2402   for TargetCell in TargetCells do begin
    2403       // Attack to not owned cell yet
    2404       // Count own possible power
    2405       TotalPower := 0;
    2406       for NeighborCell in TargetCell.Neighbors do
    2407       if NeighborCell.Player = Player then begin
    2408         TotalPower := TotalPower + NeighborCell.GetAvialPower;
    2409       end;
    2410       // Attack if target is weaker
    2411       if Game.AttackProbability(TotalPower, TargetCell.Power) >=
    2412         ComputerAggroProbability[Player.Agressivity] then begin
    2413         // Try to limit total attacking power to necessary minimum
    2414         while Game.AttackProbability(TotalPower - 1, TargetCell.Power) >=
    2415           ComputerAggroProbability[Player.Agressivity] do
    2416           Dec(TotalPower);
    2417 
    2418         // Collect required attack units from our cells
    2419         TotalAttackPower := 0;
    2420         for NeighborCell in TargetCell.Neighbors do
    2421         if NeighborCell.Player = Player then begin
    2422           // Use only necessary power
    2423           AttackPower := TotalPower - TotalAttackPower;
    2424           if NeighborCell.GetAvialPower < AttackPower then
    2425             AttackPower := NeighborCell.GetAvialPower;
    2426           Self.Player.SetMove(NeighborCell, TargetCell, AttackPower, False);
    2427           TotalAttackPower := TotalAttackPower + AttackPower;
    2428           if TotalAttackPower >= TotalPower then Break;
    2429         end;
    2430       end;
    2431   end;
    2432 
    2433   FreeAndNil(TargetCells);
    2434 end;
    2435 
    2436 procedure TComputer.InnerMoves;
    2437 var
    2438   AllCells: TCells;
    2439   I, J: Integer;
    2440   C: Integer;
    2441   CanAttack: Integer;
    2442   TargetCells: TCells;
    2443   NewTargetCells: TCells;
    2444   Cells2: TCells;
    2445   MovedPower: Integer;
    2446 begin
    2447   // We need to move available power to borders to be available for attacks
    2448   // or defense
    2449   AllCells := Game.Map.Cells;
    2450   TargetCells := TCells.Create;
    2451   TargetCells.FreeObjects := False;
    2452   NewTargetCells := TCells.Create;
    2453   NewTargetCells.FreeObjects := False;
    2454 
    2455   // Get list of all enemy border cells
    2456   for C := 0 to AllCells.Count - 1 do
    2457   with AllCells[C] do begin
    2458     if (Player <> Game.CurrentPlayer) and (Player <> nil) and (Terrain <> ttVoid) then begin
    2459       CanAttack := 0;
    2460       for I := 0 to Neighbors.Count - 1 do
    2461       if ((Neighbors[I].Player = Game.CurrentPlayer) or
    2462       (Neighbors[I].Player = nil)) and (Neighbors[I].Terrain <> ttVoid) then begin
    2463         Inc(CanAttack);
    2464       end;
    2465       if CanAttack > 0 then TargetCells.Add(AllCells[C]);
    2466     end;
    2467   end;
    2468 
    2469   if CellProcessDirection then begin
    2470     // Reverse array
    2471     for I := 0 to (TargetCells.Count div 2) - 1 do
    2472       TargetCells.Exchange(I, TargetCells.Count - 1 - I);
    2473   end;
    2474 
    2475   Game.Map.Cells.ClearMark;
    2476 
    2477   while TargetCells.Count > 0 do begin
    2478     // Set mark for selected border cells
    2479     for C := 0 to TargetCells.Count - 1 do
    2480       TargetCells[C].Mark := True;
    2481 
    2482     // Move all power from unmarked cells and mark them
    2483     NewTargetCells.Count := 0;
    2484     for C := 0 to TargetCells.Count - 1 do
    2485     with TargetCells[C] do begin
    2486       for I := 0 to Neighbors.Count - 1 do begin
    2487         if (Neighbors[I].Terrain <> ttVoid) and (not Neighbors[I].Mark) then begin
    2488           if (TargetCells[C].Player = Game.CurrentPlayer) and
    2489           (Neighbors[I].Player = Game.CurrentPlayer) then begin
    2490             // Do not take units from front line
    2491             Cells2 := Neighbors[I].Neighbors;
    2492             CanAttack := 0;
    2493             for J := 0 to Cells2.Count - 1 do
    2494             if ((Cells2[J].Player <> Game.CurrentPlayer) or (Cells2[J].Player = nil))
    2495             and (Cells2[J].Terrain <> ttVoid) then begin
    2496               Inc(CanAttack);
    2497             end;
    2498             if CanAttack = 0 then begin
    2499               MovedPower := Neighbors[I].GetAvialPower;
    2500               if (TargetCells[C].GetAvialPower + TargetCells[C].GetAttackPower + MovedPower) > Game.Map.MaxPower then
    2501                 MovedPower := Game.Map.MaxPower - TargetCells[C].GetAvialPower - TargetCells[C].GetAttackPower;
    2502               Player.SetMove(Neighbors[I], TargetCells[C], MovedPower, False);
    2503             end;
    2504           end;
    2505           Neighbors[I].Mark := True;
    2506           NewTargetCells.Add(Neighbors[I]);
    2507         end;
    2508       end;
    2509     end;
    2510 
    2511     // Use source cells NewTargetCells as new TargetCells
    2512     FreeAndNil(TargetCells);
    2513     TargetCells := NewTargetCells;
    2514     NewTargetCells := TCells.Create;
    2515     NewTargetCells.FreeObjects := False;
    2516   end;
    2517 
    2518   FreeAndNil(TargetCells);
    2519   FreeAndNil(NewTargetCells);
    2520 end;
    2521 
    2522 procedure TComputer.IncreaseMoves;
    2523 var
    2524   Move: TUnitMove;
    2525   AvailPower: Integer;
    2526 begin
    2527   // If available power remains then use all for existed unit moves
    2528   for Move in Player.Moves do
    2529   with Move do begin
    2530     if CellFrom.GetAvialPower > 0 then begin
    2531       AvailPower := CellFrom.GetAvialPower;
    2532       CountOnce := CountOnce + Min(AvailPower div CellFrom.MovesFrom.Count, AvailPower);
    2533     end;
    2534   end;
    2535 end;
    2536 
    2537 procedure TComputer.Process;
    2538 begin
    2539   AttackPlayers;
    2540   AttackNeutral;
    2541   InnerMoves;
    2542   IncreaseMoves;
    2543   //FallBack;
    2544   CellProcessDirection := not CellProcessDirection;
    2545 end;
    2546 
    2547 procedure TComputer.FallBack;
    2548 var
    2549   C: Integer;
    2550   I: Integer;
    2551   AllCells: TCells;
    2552   BorderCells: TCells;
    2553   EnemyPower: Integer;
    2554 begin
    2555   BorderCells := TCells.Create;
    2556   BorderCells.FreeObjects := False;
    2557   AllCells := Game.Map.Cells;
    2558 
    2559   // Get list of border cells
    2560   for C := 0 to AllCells.Count - 1 do
    2561   with AllCells[C] do begin
    2562     if (Terrain <> ttVoid) and (Player = Game.CurrentPlayer)  then begin
    2563       if AttackersCount(AllCells[C]) > 0 then
    2564         BorderCells.Add(AllCells[C]);
    2565     end;
    2566   end;
    2567 
    2568   // Move all units back to inner area from weak border cells
    2569   for C := 0 to BorderCells.Count - 1 do
    2570   with BorderCells[C] do begin
    2571     // Calculate enemy power
    2572     // TODO: Do not sum different enemy power to one value
    2573     EnemyPower := 0;
    2574     for I := 0 to Neighbors.Count - 1 do
    2575     if (Neighbors[I].Player <> Game.CurrentPlayer) and (Neighbors[I].Player <> nil) then begin
    2576       Inc(EnemyPower, Neighbors[I].Power);
    2577     end;
    2578     if EnemyPower > (GetAvialPower + GetAttackPower) then begin
    2579       // Fallback
    2580       for I := MovesTo.Count - 1 downto 0 do
    2581         Player.Moves.Remove(MovesTo[I]);
    2582       for I := 0 to Neighbors.Count - 1 do
    2583       if (Neighbors[I].Player = Player) and (AttackersCount(Neighbors[I]) = 0) then begin
    2584         Player.SetMove(BorderCells[C], Neighbors[I], GetAvialPower, False);
    2585         Break;
    2586       end;
    2587     end;
    2588   end;
    2589 
    2590   FreeAndNil(BorderCells);
    2591 end;
    2592 
    2593 function TComputer.AttackersCount(Cell: TCell): Integer;
    2594 var
    2595   I: Integer;
    2596 begin
    2597   Result := 0;
    2598   for I := 0 to Cell.Neighbors.Count - 1 do
    2599   if (Cell.Neighbors[I].Player <> Game.CurrentPlayer) and
    2600     (Cell.Neighbors[I].Player <> nil) then begin
    2601       Inc(Result);
    2602   end;
    26032281end;
    26042282
     
    34573135end;
    34583136
    3459 procedure TGame.NextTurn;
     3137procedure TGame.NextPlayer;
    34603138var
    34613139  PrevPlayer: TPlayer;
    34623140begin
    3463   //TODO CurrentPlayer.View.SelectedCell := nil;
     3141  // Finalize current player
    34643142  CurrentPlayer.MoveAll;
    34653143  Map.Grow(CurrentPlayer);
    34663144  CurrentPlayer.UpdateRepeatMoves;
    34673145  ComputePlayerStats;
     3146
     3147  // Select new player
    34683148  PrevPlayer := CurrentPlayer;
    34693149  // Skip dead players
     
    34823162  CurrentPlayer.PlayerMap.CheckVisibility;
    34833163  CurrentPlayer.ReduceMovesPower;
    3484   // For computers take view from previous human
    3485   //if CurrentPlayer.Mode = pmComputer then CurrentPlayer.View.Assign(PrevPlayer.View);
    34863164  if Assigned(FOnChange) then
    34873165    FOnChange(Self);
Note: See TracChangeset for help on using the changeset viewer.