Changes for bot logic.

This commit is contained in:
Ashby Issac 2026-01-27 20:55:55 +05:30
parent a9af5d1d17
commit bfa63f0e8f
3 changed files with 214 additions and 43 deletions

View File

@ -9,8 +9,7 @@ public enum PlayerType
Player1 = 0, Player1 = 0,
Player2 = 1, Player2 = 1,
Player3 = 2, Player3 = 2,
Player4 = 3, Player4 = 3
Bot
} }
[System.Serializable] [System.Serializable]
@ -20,17 +19,26 @@ public class PlayerGameData
public int startIndex; public int startIndex;
public int endIndex; public int endIndex;
public Transform playersParent; public Transform playersParent;
public List<PlayerPawn> playerPawnsDict; public Dictionary<int, PlayerPawn> playerPawnsDict;
public int totalPawnsFinished = 0; public int totalPawnsFinished = 0;
} }
[System.Serializable] [System.Serializable]
public enum MatchType public enum MatchType
{ {
LAN, PVP,
Bot Bot
} }
public enum BotMove
{
FinishingPathMove = 0,
SafeMove = 1,
AttackMove = 2,
NormalMove = 3,
NoMoves = 4
}
public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
{ {
[SerializeField] private TextMeshProUGUI diceText; [SerializeField] private TextMeshProUGUI diceText;
@ -45,13 +53,17 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
private PlayerType currentPlayerTypeTurn; private PlayerType currentPlayerTypeTurn;
private int currentPlayerTurnIndex = 0; private int currentPlayerTurnIndex = 0;
private List<PlayerType> playerTypes = new List<PlayerType>(); private List<PlayerType> allPlayerTypes = new List<PlayerType>();
private List<PlayerType> botTypesInGame = new List<PlayerType>();
public List<PlayerType> PlayerTypesCollection => playerTypes; public List<PlayerType> PlayerTypesCollection => allPlayerTypes;
// private Dictionary<PlayerTypes, > playerPawnsDict = new Dictionary<PlayerTypes, List<PlayerPawn>>(); // private Dictionary<PlayerTypes, > playerPawnsDict = new Dictionary<PlayerTypes, List<PlayerPawn>>();
private Dictionary<PlayerType, PlayerGameData> playerGameDatasDict = new Dictionary<PlayerType, PlayerGameData>(); private Dictionary<PlayerType, PlayerGameData> playerGameDatasDict = new Dictionary<PlayerType, PlayerGameData>();
// botRuntimeMovementData: where can the bot move if a particular dice value is thrown, and based on the value set the bot move for each using a predictive approach and select based on the the priority
private Dictionary<PlayerType, Dictionary<int, BotMove>> botRuntimeMovementData = new Dictionary<PlayerType, Dictionary<int, BotMove>>();
private TilesManager tilesManager; private TilesManager tilesManager;
private int diceRolledValue; private int diceRolledValue;
@ -74,53 +86,119 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
CanRollDice = true; CanRollDice = true;
// initialize the player list from UI. // initialize the player list from UI.
InitPlayerTypesForLAN(null); // InitPlayerTypesForLAN(null);
InitPlayerTypesForBotMatch(PlayerType.Player1, 1);
} }
// TODO :: Call when the UI selection is made and game starts // TODO :: Call when the UI selection is made and game starts
public void InitPlayerTypesForLAN(List<PlayerType> types) public void InitPlayerTypesForLAN(List<PlayerType> types)
{ {
// TODO :: 2P, 3P, 4P // TODO :: 2P, 3P, 4P
playerTypes = new List<PlayerType> { PlayerType.Player1, PlayerType.Player2, PlayerType.Player3, PlayerType.Player4 }; allPlayerTypes = new List<PlayerType> { PlayerType.Player1, PlayerType.Player2, PlayerType.Player3, PlayerType.Player4 };
playerBaseHandler.InitPlayerTypes(playerTypes); playerBaseHandler.InitPlayerTypes(allPlayerTypes);
InitCurrentGamePlayerInfo(); InitCurrentGamePlayerInfo();
} }
public void InitPlayerTypesForBotMatch(PlayerType selectedPlayerType, int botCount) public void InitPlayerTypesForBotMatch(PlayerType selectedPlayerType, int botCount)
{ {
// test data botTypesInGame.Clear();
allPlayerTypes.Clear();
AssignBotTypes(selectedPlayerType, botCount);
InitBotRuntimeData();
allPlayerTypes.Add(selectedPlayerType);
allPlayerTypes.AddRange(botTypesInGame);
playerBaseHandler.InitPlayerTypes(allPlayerTypes);
InitCurrentGamePlayerInfo();
AssignPlayerAndBotStates(selectedPlayerType);
}
private void InitBotRuntimeData()
{
botRuntimeMovementData.Clear();
foreach (var botType in botTypesInGame)
{
PlayerGameData playerGameData = playerGameDatasDict[botType];
if (!botRuntimeMovementData.ContainsKey(botType))
{
botRuntimeMovementData.Add(botType, new Dictionary<int, BotMove>());
}
foreach (var pawn in playerGameData.playerPawnsDict)
{
botRuntimeMovementData[botType].Add(pawn.Value.PlayerId, BotMove.SafeMove);
}
}
}
private void AssignPlayerAndBotStates(PlayerType selectedPlayerType)
{
PlayerBase playerBase = playerBaseHandler.GetPlayerBase(selectedPlayerType);
playerBase.AssignBotState(PawnType.User);
foreach (PlayerType playerType in botTypesInGame)
{
playerBase = playerBaseHandler.GetPlayerBase(playerType);
playerBase.AssignBotState(PawnType.Bot);
}
}
private void AssignBotTypes(PlayerType selectedPlayerType, int botCount)
{
int addedBots = 0;
if (botCount == 1) if (botCount == 1)
{ {
var playerType = (PlayerType)((int)selectedPlayerType + 2 < Enum.GetValues(typeof(PlayerType)).Length ? var playerType = (PlayerType)((int)selectedPlayerType + 2 < Enum.GetValues(typeof(PlayerType)).Length ?
(int)selectedPlayerType + 2 : (int)selectedPlayerType - 2); (int)selectedPlayerType + 2 : (int)selectedPlayerType - 2);
botTypesInGame.Add(playerType);
}
else if (botCount == 2)
{
foreach (PlayerType enumType in Enum.GetValues(typeof(PlayerType)))
{
if (enumType == selectedPlayerType) continue;
botTypesInGame.Add(enumType);
addedBots++;
if (addedBots == botCount) break;
}
} }
else else
{ {
foreach (PlayerType enumType in Enum.GetValues(typeof(PlayerType)))
{
if (enumType == selectedPlayerType) continue;
botTypesInGame.Add(enumType);
}
} }
} }
// TODO :: Call based on 2P/3P/4P // TODO :: Call based on 2P/3P/4P
public void InitCurrentGamePlayerInfo() public void InitCurrentGamePlayerInfo()
{ {
currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; currentPlayerTypeTurn = allPlayerTypes[currentPlayerTurnIndex];
// initialize the board based on the player types // initialize the board based on the player types
foreach (PlayerGameData playerGameData in playerGameDatas) foreach (PlayerGameData playerGameData in playerGameDatas)
{ {
if (!playerTypes.Contains(playerGameData.playerType)) if (!allPlayerTypes.Contains(playerGameData.playerType))
continue; continue;
Debug.Log($"playerGameData.playerType: {playerGameData.playerType}");
playerGameDatasDict.Add(playerGameData.playerType, playerGameData); playerGameDatasDict.Add(playerGameData.playerType, playerGameData);
playerGameDatasDict[playerGameData.playerType].playerPawnsDict = new List<PlayerPawn>(); playerGameDatasDict[playerGameData.playerType].playerPawnsDict = new Dictionary<int, PlayerPawn>();
foreach (Transform playerPawnChild in playerGameData.playersParent) foreach (Transform playerPawnChild in playerGameData.playersParent)
{ {
if (!playerPawnChild.gameObject.activeInHierarchy) continue; if (!playerPawnChild.gameObject.activeInHierarchy) continue;
playerGameDatasDict[playerGameData.playerType].playerPawnsDict.Add(playerPawnChild.GetComponent<PlayerPawn>()); var pawn = playerPawnChild.GetComponent<PlayerPawn>();
playerGameDatasDict[playerGameData.playerType].playerPawnsDict.Add(pawn.PlayerId, pawn);
} }
} }
} }
@ -129,9 +207,9 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
{ {
foreach (var playerPawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict) foreach (var playerPawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict)
{ {
if (playerPawn.GetPlayerState() == PlayerState.InFinishingPath) continue; if (playerPawn.Value.GetPlayerState() == PlayerState.InFinishingPath) continue;
playerPawn.SetPlayerSelectionState(state); playerPawn.Value.SetPlayerSelectionState(state);
} }
} }
@ -149,23 +227,9 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
// provide option to select a pawn from the list // provide option to select a pawn from the list
// also play a simple animation before selecting // also play a simple animation before selecting
EnablePlayerSelectionStates(true); EnablePlayerSelectionStates(true);
CanRollDiceAgain = true; CanRollDiceAgain = true;
bool AreAllPawnsInFinishingPath = false; if (AreAllPawnsInFinishingPath())
foreach (var pawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict)
{
if (pawn.GetPlayerState() == PlayerState.InFinishingPath && pawn.GetPlayerState() != PlayerState.HasFinished)
{
AreAllPawnsInFinishingPath = true;
continue;
}
AreAllPawnsInFinishingPath = false;
break;
}
if (AreAllPawnsInFinishingPath)
CanRollDice = true; CanRollDice = true;
pointerMeshRend.material = selectMat; pointerMeshRend.material = selectMat;
@ -174,7 +238,7 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
else // if there are any other pawns that are in safe or moving state else // if there are any other pawns that are in safe or moving state
{ {
// for player's logic // for player's logic
IEnumerable<PlayerPawn> availPlayers = playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict.Select(pawn => pawn) IEnumerable<PlayerPawn> availPlayers = playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict.Values.Select(pawn => pawn)
.Where(pawn => pawn.GetPlayerState() == PlayerState.InSafeZone || .Where(pawn => pawn.GetPlayerState() == PlayerState.InSafeZone ||
pawn.GetPlayerState() == PlayerState.Moving || pawn.GetPlayerState() == PlayerState.Moving ||
pawn.GetPlayerState() == PlayerState.InFinishingPath); pawn.GetPlayerState() == PlayerState.InFinishingPath);
@ -204,6 +268,7 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
Debug.Log($"CustomAvailablePlayers: {customAvailPlayers}"); Debug.Log($"CustomAvailablePlayers: {customAvailPlayers}");
if (customAvailPlayers < 1) if (customAvailPlayers < 1)
{ {
// TODO :: Switch the player once the dice rolling has completed
SwitchPlayer(); SwitchPlayer();
CanRollDice = true; CanRollDice = true;
} }
@ -212,6 +277,24 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
} }
} }
private bool AreAllPawnsInFinishingPath()
{
bool areAllPawnsInFinishingPath = false;
foreach (var pawn in playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict)
{
if (pawn.Value.GetPlayerState() == PlayerState.InFinishingPath && pawn.Value.GetPlayerState() != PlayerState.HasFinished)
{
areAllPawnsInFinishingPath = true;
continue;
}
areAllPawnsInFinishingPath = false;
break;
}
return areAllPawnsInFinishingPath;
}
public void OnPawnSelected(PlayerPawn playerPawn) public void OnPawnSelected(PlayerPawn playerPawn)
{ {
EnablePlayerSelectionStates(false); EnablePlayerSelectionStates(false);
@ -294,21 +377,21 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
Debug.Log($"currentPlayerTurn: {currentPlayerTypeTurn}"); Debug.Log($"currentPlayerTurn: {currentPlayerTypeTurn}");
Debug.Log($"currentPlayerTurnIndex: {currentPlayerTurnIndex}"); Debug.Log($"currentPlayerTurnIndex: {currentPlayerTurnIndex}");
if (playerTypes.Count == 1) if (allPlayerTypes.Count == 1)
{ {
Debug.LogError($"GAME IS OVER"); Debug.LogError($"GAME IS OVER");
return; return;
} }
if (currentPlayerTypeTurn == playerTypes[playerTypes.Count - 1]) if (currentPlayerTypeTurn == allPlayerTypes[allPlayerTypes.Count - 1])
{ {
currentPlayerTurnIndex = 0; currentPlayerTurnIndex = 0;
currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; currentPlayerTypeTurn = allPlayerTypes[currentPlayerTurnIndex];
} }
else else
{ {
currentPlayerTurnIndex++; currentPlayerTurnIndex++;
currentPlayerTypeTurn = playerTypes[currentPlayerTurnIndex]; currentPlayerTypeTurn = allPlayerTypes[currentPlayerTurnIndex];
} }
diceText.text = $"{0}"; diceText.text = $"{0}";
@ -323,6 +406,64 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
if (playerPawn) if (playerPawn)
playerPawn.SetPlayerState(tilesManager.RetrieveTileBasedOnIndex(playerPawn.CurrentTileIndex).IsSafeZone ? PlayerState.InSafeZone : PlayerState.Moving); playerPawn.SetPlayerState(tilesManager.RetrieveTileBasedOnIndex(playerPawn.CurrentTileIndex).IsSafeZone ? PlayerState.InSafeZone : PlayerState.Moving);
Debug.Log($"botTypesInGame.Contains(currentPlayerTypeTurn): {botTypesInGame.Contains(currentPlayerTypeTurn)}");
if (!CanRollDiceAgain && botTypesInGame.Contains(currentPlayerTypeTurn)) // TODO :: Double check calling of the function
{
Invoke(nameof(RollDiceForBot), 1f);
}
}
private void RollDiceForBot()
{
Debug.Log($"RollDiceForBot");
OnDiceRolled(UnityEngine.Random.Range(1, Ludo_3D_Constants.Max_Dice_Rolls + 1));
}
// TODO :: Call right after the dice is rolled with the animation
// What happens when you get a 6
private void SelectPawnFromBotBase()
{
var botPawnsDict = botRuntimeMovementData[currentPlayerTypeTurn]; // set the data inside this dict
// check the data from the playerGameDataDict
// change playerPawnsDict to list
// only select the pawns that are active
// if no pawns have left the home select the first pawn inside the base
//
foreach (var keyValuePair in playerGameDatasDict[currentPlayerTypeTurn].playerPawnsDict)
{
var playerPawn = keyValuePair.Value;
var possibleLandingIndex = playerPawn.CurrentTileIndex + diceRolledValue;
if (playerPawn.GetPlayerState() == PlayerState.HasFinished)
{
botPawnsDict.Remove(playerPawn.PlayerId); // TODO :: Double check logic
continue;
}
Tile tileData = tilesManager.RetrieveTileBasedOnIndex(possibleLandingIndex);
if (playerPawn.GetPlayerState() == PlayerState.InFinishingPath || possibleLandingIndex > playerGameDatasDict[currentPlayerTypeTurn].endIndex)
{
botPawnsDict[playerPawn.PlayerId] = BotMove.FinishingPathMove;
continue;
}
else if (tileData.IsSafeZone || playerPawn.GetPlayerState() == PlayerState.InHome)
{
botPawnsDict[playerPawn.PlayerId] = BotMove.SafeMove;
}
else if (tileData.PlayerPawn != null)
{
botPawnsDict[playerPawn.PlayerId] = BotMove.AttackMove;
}
else
{
botPawnsDict[playerPawn.PlayerId] = BotMove.NormalMove;
}
}
} }
private void MoveThroughTiles(PlayerPawn playerPawn, int index, int targetIndex) private void MoveThroughTiles(PlayerPawn playerPawn, int index, int targetIndex)
@ -486,10 +627,10 @@ public class GameplayManager : MonoBehaviour, IBase, IBootLoader, IDataLoader
CanRollDiceAgain = false; CanRollDiceAgain = false;
SwitchPlayer(); SwitchPlayer();
if (playerTypes.Contains(currentPlayerTypeTurn)) if (allPlayerTypes.Contains(currentPlayerTypeTurn))
playerTypes.Remove(currentPlayerTypeTurn); allPlayerTypes.Remove(currentPlayerTypeTurn);
Debug.Log($"PlayerTypes: {playerTypes.Count}"); Debug.Log($"PlayerTypes: {allPlayerTypes.Count}");
} }
else else
{ {

View File

@ -13,6 +13,11 @@ public class PlayerBase : MonoBehaviour
[SerializeField] private BasePlacementData[] basePlacementDatas; [SerializeField] private BasePlacementData[] basePlacementDatas;
[SerializeField] private PlayerPawn[] playerPawns; [SerializeField] private PlayerPawn[] playerPawns;
public bool IsBotBase
{
get; private set;
}
public PlayerType GetPlayerType() => playerType; public PlayerType GetPlayerType() => playerType;
public void InitPlayerData() public void InitPlayerData()
@ -23,6 +28,15 @@ public class PlayerBase : MonoBehaviour
} }
} }
public void AssignBotState(PawnType pawnType)
{
IsBotBase = pawnType == PawnType.Bot;
for (int idx = 0; idx < playerPawns.Length; idx++)
{
playerPawns[idx].AssignBotPlayerState(IsBotBase);
}
}
public Transform GetBasePlacementDataPosition(int idx) public Transform GetBasePlacementDataPosition(int idx)
{ {
return basePlacementDatas[idx].placementTransform; return basePlacementDatas[idx].placementTransform;

View File

@ -11,6 +11,12 @@ public enum PlayerState
HasFinished HasFinished
} }
public enum PawnType
{
User,
Bot
}
public class PlayerPawn : MonoBehaviour public class PlayerPawn : MonoBehaviour
{ {
[SerializeField] private PlayerState playerState; [SerializeField] private PlayerState playerState;
@ -33,6 +39,16 @@ public class PlayerPawn : MonoBehaviour
get; private set; get; private set;
} }
public bool IsBotPlayer
{
get; private set;
}
public void AssignBotPlayerState(bool state)
{
IsBotPlayer = state;
}
public void SetPlayerSelectionState(bool state) public void SetPlayerSelectionState(bool state)
{ {
canSelectPlayerFromHome = state; canSelectPlayerFromHome = state;
@ -68,7 +84,7 @@ public class PlayerPawn : MonoBehaviour
private void OnMouseDown() private void OnMouseDown()
{ {
if (!canSelectPlayerFromHome) return; if (IsBotPlayer || !canSelectPlayerFromHome) return;
SetGameplayManager(); SetGameplayManager();
gameplayManager.OnPawnSelected(this); gameplayManager.OnPawnSelected(this);