Skip to content

Commit 9c7019b

Browse files
authored
Merge pull request #91 from jonpas/AI
Stupid AI good to go
2 parents a017213 + 9fd28e6 commit 9c7019b

13 files changed

+293
-27
lines changed
Binary file not shown.

Source/ETV/ETVAI.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright (C) Team13. All rights reserved.
2+
3+
#include "ETVAI.h"
4+
#include "ETVGameModeBase.h"
5+
#include "ETVActionTarget_Fire.h"
6+
#include "ETVActionTarget_Move.h"
7+
8+
// TODO Returns duplicate objects of all the ships on the board for AI to calculate the next move
9+
TArray<AETVShip*> GetBoardStateClone(TArray<AETVShip*> BoardState)
10+
{
11+
TArray<AETVShip*> Clones;
12+
for(auto ShipRef : BoardState)
13+
{
14+
// Check for class?
15+
16+
// Cast and create clone
17+
18+
// Get clone's reference
19+
20+
// Push reference to clones
21+
22+
}
23+
return Clones;
24+
}
25+
26+
// TODO Calculates maximum score and the coresponding board state if the given ship makes a move
27+
TArray<AETVShip*> GetNextBoardStateAndScore(TArray<AETVShip*> BoardState, int32 ShipIndex, TArray<TArray<int32>> &MoveInstructions)
28+
{
29+
return TArray<AETVShip*>();
30+
}
31+
32+
// TODO Returns the 5 best moves for the given boardState
33+
TArray<TArray<AETVShip*>> GetTop5Moves(TArray<AETVShip*> BoardState, TArray<TArray<int32>> &MoveInstructions)
34+
{
35+
return TArray<TArray<AETVShip*>>();
36+
}
37+
38+
39+
UETVAI::UETVAI()
40+
{
41+
42+
}
43+
// TODO Better Implementation
44+
TArray<int32> UETVAI::GetMove(TArray<AETVShip*> Ships)
45+
{
46+
TArray<int32> MoveInstructions;
47+
48+
// Get gamemode
49+
AETVGameModeBase* GameMode = Cast<AETVGameModeBase>(Ships[0]->GetWorld()->GetAuthGameMode());
50+
51+
int32 MostImportantPlayerShip = -1;
52+
int32 MostImportantAIShip = -1;
53+
int32 RandomAIShip = -1;
54+
// Find most important ships
55+
int index = 0;
56+
for (auto ShipReference : Ships)
57+
{
58+
// AI Ship
59+
if (ShipReference->IsEnemy())
60+
{
61+
if (MostImportantAIShip != -1)
62+
{
63+
// This will always be the capital
64+
if (ShipReference->GetScore() > Ships[MostImportantAIShip]->GetScore())
65+
{
66+
MostImportantAIShip = index;
67+
}
68+
}
69+
else
70+
{
71+
MostImportantAIShip = index;
72+
RandomAIShip = index;
73+
}
74+
// 1/8 chance
75+
if (FMath::RandBool() && FMath::RandBool() && FMath::RandBool())
76+
{
77+
RandomAIShip = index;
78+
}
79+
}
80+
else if (GameMode->IsTileVisible(ShipReference->GetTilePosition(), EETVShipType::EnemyShip) == true)
81+
{
82+
if (MostImportantPlayerShip != -1)
83+
{
84+
if (ShipReference->GetScore() > Ships[MostImportantPlayerShip]->GetScore())
85+
{
86+
MostImportantPlayerShip = index;
87+
}
88+
}
89+
else
90+
{
91+
MostImportantPlayerShip = index;
92+
}
93+
}
94+
++index;
95+
}
96+
// Does AI see enemy ship
97+
if (MostImportantPlayerShip != -1)
98+
{
99+
int jndex = 0;
100+
// If possible try and attack
101+
for (auto ShipReference : Ships)
102+
{
103+
if (ShipReference->IsEnemy())
104+
{
105+
// Currently fire laser is always 0 and torpedo is 1 on fighters
106+
int ActionIndex = 0;
107+
if(ShipReference->GetShipClass() == "Fighter" || ShipReference->GetShipClass() == "Capital")
108+
ActionIndex = 1;
109+
UETVActionTarget_Fire* Laser = Cast<UETVActionTarget_Fire>(ShipReference->GetActions()[ActionIndex]);
110+
// TODO - Add check if torped avaliable
111+
Laser->SetTarget(Ships[MostImportantPlayerShip], Ships[MostImportantPlayerShip]->GetX(), Ships[MostImportantPlayerShip]->GetY());
112+
if (Laser->CanPerform())
113+
{
114+
MoveInstructions.SetNum(4);
115+
MoveInstructions[0] = (Ships[MostImportantPlayerShip]->GetScore());
116+
MoveInstructions[1] = (jndex);
117+
MoveInstructions[2] = (ActionIndex);
118+
MoveInstructions[3] = (MostImportantPlayerShip);
119+
return MoveInstructions;
120+
}
121+
}
122+
++jndex;
123+
}
124+
}
125+
// We we didn't find enemy ship or we can't attack it
126+
// We move a ship randomly
127+
// TODO Add checker if we can heal most important ship
128+
UETVActionTarget_Move* MoveAction = Cast<UETVActionTarget_Move>(Ships[RandomAIShip]->GetActions().Last(0));
129+
int x = -1;
130+
int y = -1;
131+
while (!MoveAction->CanPerform())
132+
{
133+
x = Ships[RandomAIShip]->GetTilePosition().X + FMath::RandRange(1, Ships[RandomAIShip]->GetMoveRange());
134+
y = Ships[RandomAIShip]->GetTilePosition().Y + FMath::RandRange(1, Ships[RandomAIShip]->GetMoveRange());
135+
MoveAction->SetTarget(GameMode->GetTileMapActor(), x, y);
136+
}
137+
MoveInstructions.SetNum(5);
138+
MoveInstructions[0] = (Ships[RandomAIShip]->GetScore());
139+
MoveInstructions[1] = (RandomAIShip);
140+
MoveInstructions[2] = (Ships[RandomAIShip]->GetActions().Num()-1);
141+
MoveInstructions[3] = (x);
142+
MoveInstructions[4] = (y);
143+
return MoveInstructions;
144+
};

Source/ETV/ETVAI.h

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (C) Team13. All rights reserved.
2+
3+
#pragma once
4+
5+
#include "CoreMinimal.h"
6+
#include "UObject/NoExportTypes.h"
7+
#include "ETVShip.h"
8+
#include "ETVAI.generated.h"
9+
10+
/**
11+
* Class that contains all the logic for the AI in the game
12+
* It passes move instructions to the game mode in a form of An Array of int32 Arrays, where ints represent ship indexes,
13+
* action indexesm coordinates or traget ship indexes and the board state if this move is used, 1 array of this ints represents 1 posssible move
14+
* --------------------------------------------
15+
* 0: Score
16+
* 1: Ship index
17+
* 2: Action index
18+
* 3: Target ship index/X coordinate
19+
* 4: Y coordinate
20+
* --------------------------------------------
21+
* If array returned contains 4 ints it can be deduced that the last int represnts an index for the target ship else it represents the X coordinate
22+
* of the board
23+
* --------------------------------------------
24+
* Ai will look a certain amount of turns into the future, predict the best moves and choose the move that will
25+
* lead to the best outcome in set amount of turns
26+
* --------------------------------------------
27+
* It currently does not work like this.
28+
* Problems to solve: Dynamic Deep Copying of actors or support for dummy properties in actors that AI can use to calculate board states.
29+
* --------------------------------------------
30+
* AI currently atack the highes priority target of enemy, heals the ship with highest priority on his sie or moves a ship randomly
31+
* --------------------------------------------
32+
*/
33+
UCLASS()
34+
class ETV_API UETVAI : public UObject
35+
{
36+
GENERATED_BODY()
37+
38+
public:
39+
// Sets default values for this actor's properties
40+
UETVAI();
41+
42+
43+
public:
44+
// Returns the move AI will make this turn depending on the given board state
45+
TArray<int32> GetMove(TArray<AETVShip*> Ships);
46+
47+
48+
49+
};

Source/ETV/ETVAction.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ void UETVAction::OnEndPerform()
112112
AETVGameModeBase* GameMode = Cast<AETVGameModeBase>(GetWorld()->GetAuthGameMode());
113113
GameMode->UpdateVisibleTiles(EETVShipType::PlayerShip);
114114

115-
if (bEndsTurn)
115+
// End turn if action automatically ends it and turn is not AI controlled (AI automatically goes to next turn)
116+
if (bEndsTurn && !GameMode->IsCurrentTurnAI())
116117
{
117118
// TODO Delay this until all effects are done
118119
GameMode->EndTurn();

Source/ETV/ETVActionTarget_Fire.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ bool UETVActionTarget_Fire::CanPerform()
5555
return true;
5656
}
5757

58-
// Check if enemy
58+
// Check if enemy (not of same side, for AI compatibility)
5959
AETVShip* SelectedShip = Cast<AETVShip>(SelectedTarget); // Required type is ship (checked in parent) so casting is safe
60-
return SelectedShip->IsEnemy();
60+
return SelectedShip->GetType() != OwnerShip->GetType();
6161
}
6262

6363
return false;

Source/ETV/ETVCalculator.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ void UETVCalculator::CalculateWeaponEffect(AETVShip *User, AETVWeapon *WeaponUse
2020

2121
// Calculate the value of change for this effect
2222
int32 ChangeValue = User->GetMultiplier()*WeaponUsed->GetDMG();
23+
if(ChangeValue < 3)
24+
{
25+
ChangeValue = 3;
26+
}
2327

2428
// Weapon ignores shields
2529
if (WeaponUsed->GetType() == AETVWeapon::DamageHull)

Source/ETV/ETVGameModeBase.cpp

+28-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "ETVCalculator.h"
1111
#include "Runtime/Engine/Classes/Engine/UserInterfaceSettings.h"
1212
#include "Runtime/Core/Public/Misc/FileHelper.h"
13+
#include "ETVAI.h"
1314
//#include "DrawDebugHelpers.h" // Uncomment for debug drawing
1415

1516
// Sets default values
@@ -25,6 +26,9 @@ AETVGameModeBase::AETVGameModeBase()
2526

2627

2728
/* Game Loop */
29+
// Set first turn to player
30+
CurrentTurnSide = EETVShipType::PlayerShip;
31+
2832
// Disable game time (until everything is generated)
2933
ElapsedTime = -1.0f;
3034
CurrentTurn = 0;
@@ -574,6 +578,7 @@ void AETVGameModeBase::EndTurn()
574578
CurShip->UnconditionallyCloseContextMenu();
575579
}
576580

581+
CurrentTurnSide = EETVShipType::EnemyShip;
577582
CurrentTurnTime = 0.0f;
578583

579584
// Handle turn end
@@ -594,8 +599,23 @@ void AETVGameModeBase::EndTurn()
594599
}
595600

596601
// Move control to AI
597-
// TODO Call into AI to do its thing
598-
// TODO AI calls NextTurn() when done
602+
UETVAI* AI = NewObject<UETVAI>();
603+
TArray<int32> Instructions = AI->GetMove(Ships);
604+
if (Instructions.Num() == 4)
605+
{
606+
UETVActionTarget* Action = Cast<UETVActionTarget>(Ships[Instructions[1]]->GetActions()[Instructions[2]]);
607+
Action->SetTarget(Ships[Instructions[3]], Ships[Instructions[3]]->GetX(), Ships[Instructions[3]]->GetY());
608+
Action->Perform();
609+
}
610+
else if (Instructions.Num() == 5)
611+
{
612+
UETVActionTarget* Action = Cast<UETVActionTarget>(Ships[Instructions[1]]->GetActions()[Instructions[2]]);
613+
Action->SetTarget(TileMapActor, Instructions[3], Instructions[4]);
614+
Action->Perform();
615+
}
616+
617+
// AI continue to next turn when done
618+
NextTurn();
599619
}
600620
}
601621

@@ -608,6 +628,7 @@ void AETVGameModeBase::NextTurn()
608628
GetShipListWidget()->Update();
609629

610630
// Apply next turn
631+
CurrentTurnSide = EETVShipType::PlayerShip;
611632
CurrentTurn++;
612633
CurrentTurnTime = static_cast<float>(TurnTime);
613634

@@ -898,3 +919,8 @@ bool AETVGameModeBase::TileHasShip(int32 x, int32 y)
898919
FPaperTileInfo TileInfo = TileMapComp->GetTile(x, y, EETVTileLayer::Ship);
899920
return TileInfo.TileSet != nullptr;
900921
}
922+
923+
UPaperTileSet* AETVGameModeBase::GetShipSprite(AETVShip* Ship)
924+
{
925+
return TileMapComp->GetTile(Ship->GetX(), Ship->GetY(), EETVTileLayer::Ship).TileSet;
926+
}

Source/ETV/ETVGameModeBase.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ETV_API AETVGameModeBase : public AGameModeBase
5555

5656

5757
/* Game Loop */
58+
EETVShipType CurrentTurnSide;
5859
TArray<UETVAction*> MultiTurnActions;
5960

6061

@@ -241,6 +242,10 @@ class ETV_API AETVGameModeBase : public AGameModeBase
241242
UFUNCTION(BlueprintCallable, Category = "ETV Game")
242243
float GetCurrentTurnPercentage();
243244

245+
// Returns if current turn is AI controlled
246+
UFUNCTION(BlueprintCallable, Category = "ETV Game")
247+
bool IsCurrentTurnAI() const { return !bDisableAI && CurrentTurnSide == EETVShipType::EnemyShip; }
248+
244249
// Add multi-turn action for execution in subsequent turns automatically
245250
UFUNCTION(BlueprintCallable, Category = "ETV Game")
246251
void AddMultiTurnAction(UETVAction* Action);
@@ -320,7 +325,7 @@ class ETV_API AETVGameModeBase : public AGameModeBase
320325
AETVShip* GetLastClickedShip() const { return LastClickedShip; }
321326

322327

323-
/* Get Widgets */
328+
/* Widget and Ship Interaction */
324329
// Get log widget
325330
UFUNCTION(BlueprintCallable, Category = "ETV UI")
326331
UETVActionLogWidget* GetLogWidget() const { return ActionLogClass; }
@@ -334,6 +339,12 @@ class ETV_API AETVGameModeBase : public AGameModeBase
334339

335340
UFUNCTION()
336341
bool TileHasShip(int32 x, int32 y);
342+
343+
UFUNCTION()
344+
UPaperTileSet* GetShipSprite(AETVShip* Ship);
345+
346+
UFUNCTION()
347+
APaperTileMapActor* GetTileMapActor() const { return TileMapActor; }
337348
};
338349

339350

0 commit comments

Comments
 (0)