Disclaimer: the project is a thought experiment/school project, assumed to be under the Fair Use definition
Mean disclaimer: it is recommended to stop reading further if “how it looks” is more important than “how it works”
Facetious disclaimer: the project is based on the code from the original Witcher 3 game that I shamelessly stolen(read: reverse engineered from AS3 and translated into UE4/C++)
Technical disclaimer: the project is a C++ project and relies heavily on reflection, in particular UFunction*/UFUNCTION(). Uses UE4 4.15
Unlike other attempts to re-create the original Witcher 3 Gwent card game, which relied on human players, this project attempts to create a fully functional AI that can be used either against another AI or against a human player.
Notable things to mention without breaking the attention span with a wall of text are:
The function “decideWhichCardToPlay” which returns UCardTransaction*, and its evaluation relies on the following switch statements
TACTIC_SPY_DUMMY_BEST_THEN_PASS
TACTIC_MINIMIZE_LOSS
TACTIC_MINIMIZE_WIN
TACTIC_MAXIMIZE_WIN
TACTIC_AVERAGE_WIN
TACTIC_MINIMAL_WIN
TACTIC_JUST_WAIT
TACTIC_WAIT_DUMMY
TACTIC_SPY
Also there are 3 finite state machines.
The game flow controller
stateMachine->AddState("Initializing", nullptr, state_update_Initializing, state_leave_Initializing);
stateMachine->AddState("Tutorials", state_begin_Tutorials, state_update_Tutorials, nullptr);
stateMachine->AddState("SpawnLeaders", state_begin_SpawnLeaders, state_update_SpawnLeaders, nullptr);
stateMachine->AddState("CoinToss", state_begin_CoinToss, state_update_CoinToss, nullptr);
stateMachine->AddState("Mulligan", state_begin_Mulligan, state_update_Mulligan, nullptr);
stateMachine->AddState("RoundStart", state_begin_RoundStart, state_update_RoundStart, nullptr);
stateMachine->AddState("PlayerTurn", state_begin_PlayerTurn, state_update_PlayerTurn, state_leave_PlayerTurn);
stateMachine->AddState("ChangingPlayer", state_begin_ChangingPlayer, state_update_ChangingPlayer, nullptr);
stateMachine->AddState("ShowingRoundResult", state_begin_ShowingRoundResult, state_update_ShowingRoundResult, nullptr);
stateMachine->AddState("ClearingBoard", state_begin_ClearingBoard, state_update_ClearingBoard, state_leave_ClearingBoard);
stateMachine->AddState("ShowingFinalResult", state_begin_ShowingFinalResult, state_update_ShowingFinalResult, nullptr);
stateMachine->AddState("Reset", state_begin_reset, nullptr, nullptr);
the human player controller
_stateMachine->AddState("Idle", state_begin_Idle, on_state_about_to_update, state_end_Idle);
_stateMachine->AddState("ChoosingCard", state_begin_ChoosingCard, state_update_ChoosingCard, state_end_ChoosingCard);
_stateMachine->AddState("ChoosingHandler", state_begin_ChoosingHandler, state_update_ChoosingHandler, nullptr);
_stateMachine->AddState("ChoosingTargetCard", state_begin_ChoosingTargetCard, state_update_ChoosingTargetCard, nullptr);
_stateMachine->AddState("WaitConfirmation", state_begin_WaitConfirmation, state_update_WaitConfirmation, nullptr);
_stateMachine->AddState("ApplyingCard", state_begin_ApplyingCard, state_update_ApplyingCard, nullptr);
and the AI player controller
_stateMachine->AddState("Idle", state_begin_Idle, nullptr, state_end_Idle);
_stateMachine->AddState("ChoosingMove", state_begin_ChoosingMove, state_update_ChoosingMove, nullptr);
_stateMachine->AddState("SendingCardToTransaction", state_begin_SendingCard, state_update_SendingCard, nullptr);
_stateMachine->AddState("DelayBetweenActions", state_begin_DelayAction, state_update_DelayAction, nullptr);
_stateMachine->AddState("ApplyingCard", state_begin_ApplyingCard, state_update_ApplyingCard, nullptr);
all the “state_” are actually UFunction*
if you would like to learn more (card instance, card leader, card template, card transaction, card manager etc.), please let me know. Thanks!