void Update() { if (Main.isInitialised) { if (Main.Instance.mMachine.CurrentState.MyTurn == myTurn && !Main.isComplete) { if (!Main.Instance.mMachine.CurrentState.hasDrawn) //if has not drawn { StartCoroutine(DrawingDelay()); if (draw == false) { AiAnim.SetTrigger("Drawing"); MCTSIterate(); //Get state that matches the current game state, then expand and simulate treeNode = treeNode.select(); //Based on calculation, select the best node to proceed Main.Instance.AIDraw(treeNode.state.lastDrawDeck); //Draw from a deck according to the node we just selected Main.Instance.lastDrawDeck = treeNode.state.lastDrawDeck; //Update game/board information if (CardsInHand.CheckVictory(Main.Instance.computerCardsInHand)) { treeNode.state.stateResult = MCTSState.Result.AIWin; } } } else if (Main.Instance.mMachine.CurrentState.hasDrawn) //If has drawn { //AiAnim.SetBool("Thinking", true); StartCoroutine(DiscardingDelay()); if (draw == true) { AiAnim.SetTrigger("Discarded"); //AiAnim.SetTrigger("Discarding"); //AiAnim.SetBool("Thinking", false); //AiAnim.SetTrigger("AIDiscard"); MCTSIterate(); //Get state that matches the current game state, then expand and simulate treeNode = treeNode.select(); //Based on calculation, select the best node to proceed Main.turnCounter += 1; Main.Instance.AIDiscard(treeNode.state.lastDiscard); //Draw a card according to the node selected Main.Instance.lastDiscardCard = treeNode.state.lastDiscard; //Update game/board information MCTSIterate(); //Now the children number has became zero, we need to iterate to get children } } } } }
public void HumanExecute() //This function is used to draw or discard when the button clicked { if (!CardsInHand.CheckVictory(playerCardsInHand) && mMachine.CurrentState.GetName == "Player Turn State" && move == true) { DetectSelection(); playerCardsInHand.ResetScore();//to show the potential score in the current hand right now (resets and displays at the start of every turn) playerCardsInHand.AnalyseHand(playerCardsInHand); if (!mMachine.CurrentState.hasDrawn) //if has not drawn, then it will execute draw action { DeckAnimator.SetTrigger("Draw"); mMachine.Execute(deckToDraw); lastDrawDeck = deckToDraw; mAI.MatchAndIterate(); //The AI will do its calculation even it's in player's turn, keeping track of the current state is necessary or the AI will be very dumb if (CardsInHand.CheckVictory(playerCardsInHand)) { UpdateEverything(); endCanvas.SetActive(true); endText.text = "Player Win!"; isComplete = true; playerWin = true; PlayerScore.text = playerScore; //Setting player score for (int i = 0; i < displayCards_AI.Length; i++) { displayCards_AI[i].GetComponentInChildren <CardImage>().enabled = true; } } } else //if has drawn, then it will execute discard action { if (mMachine.Execute(cardToDiscard)) { lastDiscardCard = cardToDiscard; turnCounter += 1; mAI.MatchAndIterate(); move = false; } } UpdateEverything(); } }
public void expand() //This function takes into account of every possibility of the game and make them children of current treeNode { //In draw phase, there are 2 possibilities, draw from drawDeck or discardDeck //In discard phase, there are 9 possibilities, because you can discard any of the 9 cards if (state.hasDrawn) { #region DiscardPhase //Discard phase expand, 9 possibilities #region HumanTurnDiscardExpand if (state.currentTurn == Main.Turn.Human) { state.humanCards.LeftShiftElement(); foreach (Card cardToDiscard in state.humanCards.GetCards()) { TreeNode childNode = new TreeNode(new MCTSState(state.currentTurn, state.drawDeck, state.discardDeck, state.humanCards, state.AICards, state.hasDrawn, state.lastDiscard, state.lastDrawDeck)); if (cardToDiscard != null) { childNode.state.discardDeck.Add(cardToDiscard); childNode.state.humanCards.Remove(cardToDiscard); childNode.state.discardDeck.LeftShiftElement(); childNode.state.humanCards.LeftShiftElement(); childNode.state.lastDiscard = cardToDiscard; childNode.state.stateResult = MCTSState.Result.None; childNode.state.hasDrawn = false; childNode.state.currentTurn = Main.Turn.AI; children.Add(childNode); } } } #endregion #region AITurnDiscardExpand else if (state.currentTurn == Main.Turn.AI) { // Debug.Log("Expand AI turn, discard"); state.humanCards.LeftShiftElement(); foreach (Card cardToDiscard in state.AICards.GetCards()) { TreeNode childNode = new TreeNode(new MCTSState(state.currentTurn, state.drawDeck, state.discardDeck, state.humanCards, state.AICards, state.hasDrawn, state.lastDiscard, state.lastDrawDeck)); // Card cardToDiscard = childNode.state.AICards.GetCardByMeldType((MeldType)i); if (cardToDiscard != null) { childNode.state.discardDeck.Add(cardToDiscard); childNode.state.AICards.Remove(cardToDiscard); childNode.state.discardDeck.LeftShiftElement(); childNode.state.AICards.LeftShiftElement(); childNode.state.lastDiscard = cardToDiscard; childNode.state.currentTurn = Main.Turn.Human; childNode.state.stateResult = MCTSState.Result.None; childNode.state.hasDrawn = false; children.Add(childNode); } } } #endregion #endregion } else { #region DrawPhase //Draw phase expand, 2 possibilities (1 possibility when discard deck is empty) #region HumanTurnDrawExpand if (state.currentTurn == Main.Turn.Human) { //Debug.Log("Expand human turn, draw"); for (int i = 0; i < 2; i++) { TreeNode childNode = new TreeNode(new MCTSState(state.currentTurn, state.drawDeck, state.discardDeck, state.humanCards, state.AICards, state.hasDrawn, state.lastDiscard, state.lastDrawDeck)); if (i == 0) { if (childNode.state.discardDeck.Count > 0) { childNode.state.humanCards.Add(childNode.state.discardDeck.Pop()); childNode.state.discardDeck.LeftShiftElement(); childNode.state.lastDrawDeck = CherkiMachineState.SourceDeck.DiscardDeck; } } else { childNode.state.humanCards.Add(childNode.state.drawDeck.Pop()); childNode.state.lastDrawDeck = CherkiMachineState.SourceDeck.DrawDeck; } childNode.state.humanCards.LeftShiftElement(); childNode.state.hasDrawn = true; if (CardsInHand.CheckVictory(childNode.state.humanCards)) { childNode.state.stateResult = MCTSState.Result.HumanWin; } children.Add(childNode); } } #endregion #region AITurnDrawExpand else if (state.currentTurn == Main.Turn.AI) { //Debug.Log("Expand AI turn, draw"); for (int i = 0; i < 2; i++) { TreeNode childNode = new TreeNode(new MCTSState(state.currentTurn, state.drawDeck, state.discardDeck, state.humanCards, state.AICards, state.hasDrawn, state.lastDiscard, state.lastDrawDeck)); if (i == 0) { if (childNode.state.discardDeck.Count > 0) { childNode.state.AICards.Add(childNode.state.discardDeck.Pop()); childNode.state.discardDeck.LeftShiftElement(); childNode.state.lastDrawDeck = CherkiMachineState.SourceDeck.DiscardDeck; } } else { childNode.state.AICards.Add(childNode.state.drawDeck.Pop()); childNode.state.lastDrawDeck = CherkiMachineState.SourceDeck.DrawDeck; } childNode.state.AICards.LeftShiftElement(); childNode.state.hasDrawn = true; if (CardsInHand.CheckVictory(childNode.state.AICards)) { childNode.state.stateResult = MCTSState.Result.AIWin; } children.Add(childNode); } } #endregion #endregion } }
public double simulate() //It simulates all the way to the end of the game, from currentNode { //Based on the result of the simulation(Win/lose), it return different sim value, used to judge whether its a good node or not MCTSState simState = new MCTSState(state.currentTurn, state.drawDeck, state.discardDeck, state.humanCards, state.AICards, state.hasDrawn, state.lastDiscard, state.lastDrawDeck); simState.stateResult = state.stateResult; int simValue = int.MinValue; while (simState.stateResult == MCTSState.Result.None && simState.drawDeck.Count > 0) { if (!simState.hasDrawn)//In Draw phase { simState.hasDrawn = true; if (simState.currentTurn == Main.Turn.AI) //AI's turn { if (simState.discardDeck.Count > 0) { if (simState.AICards.NumOfCardsOfType(simState.discardDeck.GetTop().MeldType) == 2) //if drawing from discard deck can form a meld { simState.AICards.Add(simState.discardDeck.Pop()); //Draw from discard deck } else { simState.AICards.Add(simState.drawDeck.Pop()); //Other wise just take from draw deck } } else //If no card in discard deck, draw from draw deck { simState.AICards.Add(simState.drawDeck.Pop()); } if (simState.drawDeck.Count <= 0) //After draw finish, if the draw deck is empty, mark the game result as draw { simState.stateResult = MCTSState.Result.Draw; break; } if (CardsInHand.CheckVictory(simState.AICards)) //If victory goal met, mark the game result as AIWIN { simState.stateResult = MCTSState.Result.AIWin; break; } } else if (simState.currentTurn == Main.Turn.Human) //Human's turn,everything same as above, basically duplicated code { if (simState.discardDeck.Count > 0) { if (simState.humanCards.NumOfCardsOfType(simState.discardDeck.GetTop().MeldType) == 2) //if drawing from discard deck can form a meld { simState.humanCards.Add(simState.discardDeck.Pop()); } else { simState.humanCards.Add(simState.drawDeck.Pop()); } } else { simState.humanCards.Add(simState.drawDeck.Pop()); } if (simState.drawDeck.Count <= 0) { simState.stateResult = MCTSState.Result.Draw; break; } if (CardsInHand.CheckVictory(simState.humanCards)) { simState.stateResult = MCTSState.Result.HumanWin; break; } } } else if (simState.hasDrawn)//In discard phase { simState.hasDrawn = false; if (simState.currentTurn == Main.Turn.AI) //AI's turn { MeldType typeToDiscard = MostUselessType(simState.AICards); //Get the least valuable meld type among the cards in hand Card cardToDiscard = simState.AICards.GetCardByMeldType(typeToDiscard); //get a card of that meld type, then discard it simState.AICards.Remove(cardToDiscard); simState.AICards.LeftShiftElement(); simState.discardDeck.Add(cardToDiscard); simState.discardDeck.LeftShiftElement(); simState.currentTurn = Main.Turn.Human; } else //duplicated code { MeldType typeToDiscard = MostUselessType(simState.humanCards); Card cardToDiscard = simState.humanCards.GetCardByMeldType(typeToDiscard); simState.humanCards.Remove(cardToDiscard); simState.humanCards.LeftShiftElement(); simState.discardDeck.Add(cardToDiscard); simState.discardDeck.LeftShiftElement(); simState.currentTurn = Main.Turn.AI; } } } switch (simState.stateResult) { case MCTSState.Result.Draw: { // Debug.Log("Draw result simed"); simValue = 0; break; } case MCTSState.Result.HumanWin: { // Debug.Log("HumanWin result simed"); simValue = -1; //1 means victory, -1 means defeat break; } case MCTSState.Result.AIWin: { //Debug.Log("AIWin result simed"); simValue = 1; break; } default: { // Debug.LogError("illegal simStateResult value"); break; } } return(simValue); }
public override bool CheckVictory() { return(CardsInHand.CheckVictory(mCards)); }