/// <summary> /// Executes random actions with the next pieces over the state of node /// </summary> /// <param name="node"></param> /// <returns></returns> protected virtual float Rollout(MCTSNode node) { TetrisState newState = node.state.CloneState(); int nPieces = 0; float totalScore = node.state.GetScore(); float weight = 1f; float totalWeight = weight; //node.height identifies the height of the node in the MCTreeSearch, but also identifies the index of the piece inside the history of all the pieces played //So, if that node.height plus the number of pieces played in the rollout are bigger than the number of known pieces, then the rollout must stop. //Also it stops if an action has caused a game over while ((node.height + nPieces) < pieces.Count && !newState.IsTerminal()) { weight *= rolloutScoreWeightReduction; totalWeight += weight; PieceModel piece; piece = new PieceModel(pieces[node.height + nPieces]); newState.DoAction(piece, newState.GetRandomAction(piece)); nPieces++; totalScore += newState.GetScore() * weight; } float score = totalScore / totalWeight; rollouts++; return(score); }
/// <summary> /// Uses also the budget in order to make a fair calculation: if the budget is consumed checking the Tetris, then it stops /// </summary> /// <param name="nextPiece"></param> /// <param name="currentTetrisState"></param> /// <param name="possibleActions"></param> /// <param name="budget"></param> /// <returns></returns> protected IEnumerator CheckTetris(PieceModel nextPiece, TetrisState currentTetrisState, List <PieceAction> possibleActions, float budget) { int i = 0; while (t0 < budget && i < possibleActions.Count) { if (!TBController.pausedGame) { t0 += Time.deltaTime; PieceAction action = possibleActions[i]; if (action.rotationIndex == 0) //The I piece is horizontal, not vertical, so it can't be a Tetris { i++; continue; } TetrisState newState = currentTetrisState.CloneState(); newState.DoAction(nextPiece, action); nextPiece.ResetCoordinates(); if (newState.IsTetris()) { bestAction = action; } i++; } yield return(null); } }
/// <summary> /// Creates the children of this node with a given new piece /// </summary> /// <param name="newCurrentPiece"></param> public void ExtendNode(PieceModel newCurrentPiece) { List <PieceAction> actions = state.GetActions(newCurrentPiece); foreach (PieceAction action in actions) //Each child is related with one of the possible actions for the newCurrentPiece played in the state of this node { TetrisState newState = state.CloneState(); newState.DoAction(newCurrentPiece, action); newCurrentPiece.ResetCoordinates(); MCTSNode newNode = new MCTSNode(MCTreeSearch.nNodes, this, newState, action, newCurrentPiece); children.Add(newNode); } }
/// <summary> /// Main loop of the bot. It's based on the same method of TetrisBot, but with the add-on that checks if a Tetris can be played /// </summary> /// <param name="nextPieceType"></param> /// <param name="budget"></param> /// <returns></returns> public override IEnumerator ActCoroutine(PieceType nextPieceType, float budget) { t0 = 0.0f; PieceModel nextPiece = new PieceModel(nextPieceType); List <PieceAction> possibleActions = currentTetrisState.GetActions(nextPiece); bestAction = null; yield return(null); t0 += Time.deltaTime; //If the currentPiece is a I piece, the Tetris is checked since there is no possibility to make a Tetris with another type of piece if (nextPieceType == PieceType.I) { CheckTetris(nextPiece, currentTetrisState, possibleActions, budget); } //If there is no possibility of Tetris, the same algorithm than TetrisBot is played if (bestAction == null) { float bestScore = -float.MaxValue; int i = Random.Range(0, possibleActions.Count); int initialIndex = i; while (t0 < budget) { if (!TBController.pausedGame) { t0 += Time.deltaTime; TetrisState newState = currentTetrisState.CloneState(); newState.DoAction(nextPiece, possibleActions[i]); nextPiece.ResetCoordinates(); float score = newState.GetHumanizedScore(); if (score > bestScore) { bestScore = score; bestAction = possibleActions[i]; } i++; if (i == possibleActions.Count) { i = 0; } if (i == initialIndex) { break; } } yield return(null); } } else { Debug.Log("BOOM! Tetris for bot"); } //If there is no bestAction at this point, a random action is going to be played if (bestAction == null) { bestAction = currentTetrisState.GetRandomAction(nextPiece); } currentTetrisState.DoAction(nextPiece, bestAction); TBController.DoActionByBot(bestAction); }
/// <summary> /// Main bot method that searches for the best action to do with the current piece in the current state. /// It has a budget of time which is the max time it has to find that best action /// </summary> /// <param name="nextPieceType"></param> /// <param name="budget"></param> /// <returns></returns> public virtual IEnumerator ActCoroutine(PieceType nextPieceType, float budget) { float t0 = 0.0f; PieceModel nextPiece = new PieceModel(nextPieceType); List <PieceAction> possibleActions; //First of all, it gets the possible actions with the current piece in the current state if (!pieceActionDictionary.ContainsKey(nextPieceType)) { possibleActions = emptyTetrisState.GetActions(nextPiece); pieceActionDictionary.Add(nextPieceType, possibleActions); } else { possibleActions = pieceActionDictionary[nextPieceType]; } float bestScore = -float.MaxValue; PieceAction bestAction; int i = Random.Range(0, possibleActions.Count); //The first possible action to test is chosen randomly int initialIndex = i; bestAction = possibleActions[i]; yield return(null); t0 += Time.deltaTime; //In a while loop that goes until the time ends while (t0 < budget && i < possibleActions.Count) { if (!TBController.pausedGame) { t0 += Time.deltaTime; TetrisState newState = currentTetrisState.CloneState(); //The TetrisState is cloned newState.DoAction(nextPiece, possibleActions[i]); //One of the possible actions is played in the cloned state nextPiece.ResetCoordinates(); float score = newState.GetScore(); //And its score is got if (score > bestScore) { bestScore = score; bestAction = possibleActions[i]; } i++; if (i == possibleActions.Count) { i = 0; } if (i == initialIndex) { break; //If all the possible actions have been tested, the while loop ends } } yield return(null); } currentTetrisState.DoAction(nextPiece, bestAction); //The bestAction is played in the real state TBController.DoActionByBot(bestAction); //And also, it is said to the TetrisBoardController to play that action in the real board }