Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stupid AI good to go #91

Merged
merged 8 commits into from
Jan 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified Content/EmptierThanVoid/UI/HUD/WBP_GameUI_ShipStatus.uasset
Binary file not shown.
144 changes: 144 additions & 0 deletions Source/ETV/ETVAI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright (C) Team13. All rights reserved.

#include "ETVAI.h"
#include "ETVGameModeBase.h"
#include "ETVActionTarget_Fire.h"
#include "ETVActionTarget_Move.h"

// TODO Returns duplicate objects of all the ships on the board for AI to calculate the next move
TArray<AETVShip*> GetBoardStateClone(TArray<AETVShip*> BoardState)
{
TArray<AETVShip*> Clones;
for(auto ShipRef : BoardState)
{
// Check for class?

// Cast and create clone

// Get clone's reference

// Push reference to clones

}
return Clones;
}

// TODO Calculates maximum score and the coresponding board state if the given ship makes a move
TArray<AETVShip*> GetNextBoardStateAndScore(TArray<AETVShip*> BoardState, int32 ShipIndex, TArray<TArray<int32>> &MoveInstructions)
{
return TArray<AETVShip*>();
}

// TODO Returns the 5 best moves for the given boardState
TArray<TArray<AETVShip*>> GetTop5Moves(TArray<AETVShip*> BoardState, TArray<TArray<int32>> &MoveInstructions)
{
return TArray<TArray<AETVShip*>>();
}


UETVAI::UETVAI()
{

}
// TODO Better Implementation
TArray<int32> UETVAI::GetMove(TArray<AETVShip*> Ships)
{
TArray<int32> MoveInstructions;

// Get gamemode
AETVGameModeBase* GameMode = Cast<AETVGameModeBase>(Ships[0]->GetWorld()->GetAuthGameMode());

int32 MostImportantPlayerShip = -1;
int32 MostImportantAIShip = -1;
int32 RandomAIShip = -1;
// Find most important ships
int index = 0;
for (auto ShipReference : Ships)
{
// AI Ship
if (ShipReference->IsEnemy())
{
if (MostImportantAIShip != -1)
{
// This will always be the capital
if (ShipReference->GetScore() > Ships[MostImportantAIShip]->GetScore())
{
MostImportantAIShip = index;
}
}
else
{
MostImportantAIShip = index;
RandomAIShip = index;
}
// 1/8 chance
if (FMath::RandBool() && FMath::RandBool() && FMath::RandBool())
{
RandomAIShip = index;
}
}
else if (GameMode->IsTileVisible(ShipReference->GetTilePosition(), EETVShipType::EnemyShip) == true)
{
if (MostImportantPlayerShip != -1)
{
if (ShipReference->GetScore() > Ships[MostImportantPlayerShip]->GetScore())
{
MostImportantPlayerShip = index;
}
}
else
{
MostImportantPlayerShip = index;
}
}
++index;
}
// Does AI see enemy ship
if (MostImportantPlayerShip != -1)
{
int jndex = 0;
// If possible try and attack
for (auto ShipReference : Ships)
{
if (ShipReference->IsEnemy())
{
// Currently fire laser is always 0 and torpedo is 1 on fighters
int ActionIndex = 0;
if(ShipReference->GetShipClass() == "Fighter" || ShipReference->GetShipClass() == "Capital")
ActionIndex = 1;
UETVActionTarget_Fire* Laser = Cast<UETVActionTarget_Fire>(ShipReference->GetActions()[ActionIndex]);
// TODO - Add check if torped avaliable
Laser->SetTarget(Ships[MostImportantPlayerShip], Ships[MostImportantPlayerShip]->GetX(), Ships[MostImportantPlayerShip]->GetY());
if (Laser->CanPerform())
{
MoveInstructions.SetNum(4);
MoveInstructions[0] = (Ships[MostImportantPlayerShip]->GetScore());
MoveInstructions[1] = (jndex);
MoveInstructions[2] = (ActionIndex);
MoveInstructions[3] = (MostImportantPlayerShip);
return MoveInstructions;
}
}
++jndex;
}
}
// We we didn't find enemy ship or we can't attack it
// We move a ship randomly
// TODO Add checker if we can heal most important ship
UETVActionTarget_Move* MoveAction = Cast<UETVActionTarget_Move>(Ships[RandomAIShip]->GetActions().Last(0));
int x = -1;
int y = -1;
while (!MoveAction->CanPerform())
{
x = Ships[RandomAIShip]->GetTilePosition().X + FMath::RandRange(1, Ships[RandomAIShip]->GetMoveRange());
y = Ships[RandomAIShip]->GetTilePosition().Y + FMath::RandRange(1, Ships[RandomAIShip]->GetMoveRange());
MoveAction->SetTarget(GameMode->GetTileMapActor(), x, y);
}
MoveInstructions.SetNum(5);
MoveInstructions[0] = (Ships[RandomAIShip]->GetScore());
MoveInstructions[1] = (RandomAIShip);
MoveInstructions[2] = (Ships[RandomAIShip]->GetActions().Num()-1);
MoveInstructions[3] = (x);
MoveInstructions[4] = (y);
return MoveInstructions;
};
49 changes: 49 additions & 0 deletions Source/ETV/ETVAI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (C) Team13. All rights reserved.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "ETVShip.h"
#include "ETVAI.generated.h"

/**
* Class that contains all the logic for the AI in the game
* It passes move instructions to the game mode in a form of An Array of int32 Arrays, where ints represent ship indexes,
* 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
* --------------------------------------------
* 0: Score
* 1: Ship index
* 2: Action index
* 3: Target ship index/X coordinate
* 4: Y coordinate
* --------------------------------------------
* 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
* of the board
* --------------------------------------------
* Ai will look a certain amount of turns into the future, predict the best moves and choose the move that will
* lead to the best outcome in set amount of turns
* --------------------------------------------
* It currently does not work like this.
* Problems to solve: Dynamic Deep Copying of actors or support for dummy properties in actors that AI can use to calculate board states.
* --------------------------------------------
* AI currently atack the highes priority target of enemy, heals the ship with highest priority on his sie or moves a ship randomly
* --------------------------------------------
*/
UCLASS()
class ETV_API UETVAI : public UObject
{
GENERATED_BODY()

public:
// Sets default values for this actor's properties
UETVAI();


public:
// Returns the move AI will make this turn depending on the given board state
TArray<int32> GetMove(TArray<AETVShip*> Ships);



};
3 changes: 2 additions & 1 deletion Source/ETV/ETVAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ void UETVAction::OnEndPerform()
AETVGameModeBase* GameMode = Cast<AETVGameModeBase>(GetWorld()->GetAuthGameMode());
GameMode->UpdateVisibleTiles(EETVShipType::PlayerShip);

if (bEndsTurn)
// End turn if action automatically ends it and turn is not AI controlled (AI automatically goes to next turn)
if (bEndsTurn && !GameMode->IsCurrentTurnAI())
{
// TODO Delay this until all effects are done
GameMode->EndTurn();
Expand Down
4 changes: 2 additions & 2 deletions Source/ETV/ETVActionTarget_Fire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ bool UETVActionTarget_Fire::CanPerform()
return true;
}

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

return false;
Expand Down
4 changes: 4 additions & 0 deletions Source/ETV/ETVCalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ void UETVCalculator::CalculateWeaponEffect(AETVShip *User, AETVWeapon *WeaponUse

// Calculate the value of change for this effect
int32 ChangeValue = User->GetMultiplier()*WeaponUsed->GetDMG();
if(ChangeValue < 3)
{
ChangeValue = 3;
}

// Weapon ignores shields
if (WeaponUsed->GetType() == AETVWeapon::DamageHull)
Expand Down
30 changes: 28 additions & 2 deletions Source/ETV/ETVGameModeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ETVCalculator.h"
#include "Runtime/Engine/Classes/Engine/UserInterfaceSettings.h"
#include "Runtime/Core/Public/Misc/FileHelper.h"
#include "ETVAI.h"
//#include "DrawDebugHelpers.h" // Uncomment for debug drawing

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


/* Game Loop */
// Set first turn to player
CurrentTurnSide = EETVShipType::PlayerShip;

// Disable game time (until everything is generated)
ElapsedTime = -1.0f;
CurrentTurn = 0;
Expand Down Expand Up @@ -574,6 +578,7 @@ void AETVGameModeBase::EndTurn()
CurShip->UnconditionallyCloseContextMenu();
}

CurrentTurnSide = EETVShipType::EnemyShip;
CurrentTurnTime = 0.0f;

// Handle turn end
Expand All @@ -594,8 +599,23 @@ void AETVGameModeBase::EndTurn()
}

// Move control to AI
// TODO Call into AI to do its thing
// TODO AI calls NextTurn() when done
UETVAI* AI = NewObject<UETVAI>();
TArray<int32> Instructions = AI->GetMove(Ships);
if (Instructions.Num() == 4)
{
UETVActionTarget* Action = Cast<UETVActionTarget>(Ships[Instructions[1]]->GetActions()[Instructions[2]]);
Action->SetTarget(Ships[Instructions[3]], Ships[Instructions[3]]->GetX(), Ships[Instructions[3]]->GetY());
Action->Perform();
}
else if (Instructions.Num() == 5)
{
UETVActionTarget* Action = Cast<UETVActionTarget>(Ships[Instructions[1]]->GetActions()[Instructions[2]]);
Action->SetTarget(TileMapActor, Instructions[3], Instructions[4]);
Action->Perform();
}

// AI continue to next turn when done
NextTurn();
}
}

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

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

Expand Down Expand Up @@ -898,3 +919,8 @@ bool AETVGameModeBase::TileHasShip(int32 x, int32 y)
FPaperTileInfo TileInfo = TileMapComp->GetTile(x, y, EETVTileLayer::Ship);
return TileInfo.TileSet != nullptr;
}

UPaperTileSet* AETVGameModeBase::GetShipSprite(AETVShip* Ship)
{
return TileMapComp->GetTile(Ship->GetX(), Ship->GetY(), EETVTileLayer::Ship).TileSet;
}
13 changes: 12 additions & 1 deletion Source/ETV/ETVGameModeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ETV_API AETVGameModeBase : public AGameModeBase


/* Game Loop */
EETVShipType CurrentTurnSide;
TArray<UETVAction*> MultiTurnActions;


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

// Returns if current turn is AI controlled
UFUNCTION(BlueprintCallable, Category = "ETV Game")
bool IsCurrentTurnAI() const { return !bDisableAI && CurrentTurnSide == EETVShipType::EnemyShip; }

// Add multi-turn action for execution in subsequent turns automatically
UFUNCTION(BlueprintCallable, Category = "ETV Game")
void AddMultiTurnAction(UETVAction* Action);
Expand Down Expand Up @@ -320,7 +325,7 @@ class ETV_API AETVGameModeBase : public AGameModeBase
AETVShip* GetLastClickedShip() const { return LastClickedShip; }


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

UFUNCTION()
bool TileHasShip(int32 x, int32 y);

UFUNCTION()
UPaperTileSet* GetShipSprite(AETVShip* Ship);

UFUNCTION()
APaperTileMapActor* GetTileMapActor() const { return TileMapActor; }
};


Expand Down
Loading