public ETriadGameState SolverPlayRandomGame(TriadGameData gameData, Random random) { while (SolverPlayRandomTurn(gameData, random)) { } return(gameData.state); }
public static void RunSolverStressTest() { int numIterations = 1000 * 1000; Logger.WriteLine("Solver testing start, numIterations:" + numIterations); Stopwatch timer = new Stopwatch(); timer.Start(); TriadDeck testDeck = new TriadDeck(new int[] { 10, 20, 30, 40, 50 }); TriadNpc testNpc = TriadNpcDB.Get().Find("Garima"); Random randStream = new Random(); TriadGameSession solver = new TriadGameSession(); solver.modifiers.AddRange(testNpc.Rules); solver.UpdateSpecialRules(); for (int Idx = 0; Idx < numIterations; Idx++) { TriadGameData testData = solver.StartGame(testDeck, testNpc.Deck, ETriadGameState.InProgressBlue); Random sessionRand = new Random(randStream.Next()); solver.SolverPlayRandomGame(testData, sessionRand); } timer.Stop(); Logger.WriteLine("Solver testing finished, time taken:" + timer.ElapsedMilliseconds + "ms"); if (Debugger.IsAttached) { Debugger.Break(); } }
// special logic, covered by GUI public static void StaticSwapCards(TriadGameData gameData, TriadCard swapFromBlue, int blueSlotIdx, TriadCard swapFromRed, int redSlotIdx) { // implement this rule only for manual mode, screen captures get everything automatically TriadDeckInstanceManual deckBlueEx = gameData.deckBlue as TriadDeckInstanceManual; TriadDeckInstanceManual deckRedEx = gameData.deckRed as TriadDeckInstanceManual; if (deckBlueEx != null && deckRedEx != null) { bool bIsRedFromKnown = redSlotIdx < deckRedEx.deck.knownCards.Count; if (gameData.bDebugRules) { TriadGameModifierSwap DummyOb = new TriadGameModifierSwap(); Logger.WriteLine(">> " + DummyOb.RuleName + "! blue[" + blueSlotIdx + "]:" + swapFromBlue.Name + " <-> red[" + redSlotIdx + (bIsRedFromKnown ? "" : ":Opt") + "]:" + swapFromRed.Name); } TriadDeck blueDeckSwapped = new TriadDeck(deckBlueEx.deck.knownCards, deckBlueEx.deck.unknownCardPool); TriadDeck redDeckSwapped = new TriadDeck(deckRedEx.deck.knownCards, deckRedEx.deck.unknownCardPool); // ignore order in red deck redDeckSwapped.knownCards.Add(swapFromBlue); redDeckSwapped.knownCards.Remove(swapFromRed); redDeckSwapped.unknownCardPool.Remove(swapFromRed); // preserve order in blue deck blueDeckSwapped.knownCards[blueSlotIdx] = swapFromRed; gameData.deckBlue = new TriadDeckInstanceManual(blueDeckSwapped); gameData.deckRed = new TriadDeckInstanceManual(redDeckSwapped); } }
public bool VerifyState(TriadGameData gameState, bool debugMode) { if (expectedState != null) { for (int idx = 0; idx < expectedState.Length; idx++) { if (gameState.board[idx].owner != expectedState[idx]) { if (debugMode) { string expectedCode = ""; string currentCode = ""; Func <ETriadCardOwner, char> GetOwnerCode = (owner) => (owner == ETriadCardOwner.Blue) ? 'B' : (owner == ETriadCardOwner.Red) ? 'R' : '.'; for (int codeIdx = 0; codeIdx < 9; codeIdx++) { if (codeIdx == 3 || codeIdx == 6) { expectedCode += ' '; currentCode += ' '; } expectedCode += GetOwnerCode(gameState.board[codeIdx].owner); currentCode += GetOwnerCode(expectedState[codeIdx]); } Logger.WriteLine("Failed, mismatch at [{0}]! Expected:{1}, got{2}", idx, expectedCode, currentCode); } return(false); } } } return(true); }
public override void OnScreenUpdate(TriadGameData gameData) { for (int Idx = 0; Idx < gameData.typeMods.Length; Idx++) { gameData.typeMods[Idx] = 0; } for (int Idx = 0; Idx < gameData.board.Length; Idx++) { TriadCardInstance checkCard = gameData.board[Idx]; if (checkCard != null && checkCard.card.Type != ETriadCardType.None) { gameData.typeMods[(int)checkCard.card.Type] -= 1; } } for (int Idx = 0; Idx < gameData.board.Length; Idx++) { TriadCardInstance checkCard = gameData.board[Idx]; if (checkCard != null && checkCard.card.Type != ETriadCardType.None) { checkCard.scoreModifier = gameData.typeMods[(int)checkCard.card.Type]; } } }
public bool PlaceCard(TriadGameData gameData, TriadCard card, ETriadCardOwner owner, int boardPos) { TriadDeckInstance useDeck = (owner == ETriadCardOwner.Blue) ? gameData.deckBlue : gameData.deckRed; int cardIdx = useDeck.GetCardIndex(card); return(PlaceCard(gameData, cardIdx, useDeck, owner, boardPos)); }
public override void OnCheckCaptureNeis(TriadGameData gameData, int boardPos, int[] neiPos, List <int> captureList) { TriadCardInstance checkCard = gameData.board[boardPos]; for (int sideIdx = 0; sideIdx < 4; sideIdx++) { int testNeiPos = neiPos[sideIdx]; if (testNeiPos >= 0 && gameData.board[testNeiPos] != null) { TriadCardInstance neiCard = gameData.board[testNeiPos]; if (checkCard.owner != neiCard.owner) { int numPosPattern = checkCard.GetNumber((ETriadGameSide)sideIdx); int numOtherPattern = neiCard.GetOppositeNumber((ETriadGameSide)sideIdx); int sumPattern = numPosPattern + numOtherPattern; bool bIsCaptured = false; for (int vsSideIdx = 0; vsSideIdx < 4; vsSideIdx++) { int vsNeiPos = neiPos[vsSideIdx]; if (vsNeiPos >= 0 && sideIdx != vsSideIdx && gameData.board[vsNeiPos] != null) { TriadCardInstance vsCard = gameData.board[vsNeiPos]; int numPosVs = checkCard.GetNumber((ETriadGameSide)vsSideIdx); int numOtherVs = vsCard.GetOppositeNumber((ETriadGameSide)vsSideIdx); int sumVs = numPosVs + numOtherVs; if (sumPattern == sumVs) { bIsCaptured = true; if (vsCard.owner != checkCard.owner) { vsCard.owner = checkCard.owner; captureList.Add(vsNeiPos); if (gameData.bDebugRules) { Logger.WriteLine(">> " + RuleName + "! [" + vsNeiPos + "] " + vsCard.card.Name + " => " + vsCard.owner); } } } } } if (bIsCaptured) { neiCard.owner = checkCard.owner; captureList.Add(testNeiPos); if (gameData.bDebugRules) { Logger.WriteLine(">> " + RuleName + "! [" + testNeiPos + "] " + neiCard.card.Name + " => " + neiCard.owner); } } } } } }
public override void OnAllCardsPlaced(TriadGameData gameData) { if (RuleInst != null) { RuleInst.OnAllCardsPlaced(gameData); } }
public override void OnFilterNextCards(TriadGameData gameData, ref int allowedCardsMask) { if (RuleInst != null) { RuleInst.OnFilterNextCards(gameData, ref allowedCardsMask); } }
public override void OnCheckCaptureCardMath(TriadGameData gameData, int boardPos, int neiPos, int cardNum, int neiNum, ref bool isCaptured) { if (RuleInst != null) { RuleInst.OnCheckCaptureCardMath(gameData, boardPos, neiPos, cardNum, neiNum, ref isCaptured); } }
public override void OnPostCaptures(TriadGameData gameData, int boardPos) { if (RuleInst != null) { RuleInst.OnPostCaptures(gameData, boardPos); } }
public override void OnCheckCaptureCardWeights(TriadGameData gameData, int boardPos, int neiPos, ref int cardNum, ref int neiNum) { if (RuleInst != null) { RuleInst.OnCheckCaptureCardWeights(gameData, boardPos, neiPos, ref cardNum, ref neiNum); } }
public override void OnCheckCaptureNeis(TriadGameData gameData, int boardPos, int[] neiPos, List <int> captureList) { if (RuleInst != null) { RuleInst.OnCheckCaptureNeis(gameData, boardPos, neiPos, captureList); } }
public override void OnCardPlaced(TriadGameData gameData, int boardPos) { if (RuleInst != null) { RuleInst.OnCardPlaced(gameData, boardPos); } }
private void OnAllCardsPlaced(TriadGameData gameData) { int numBlue = (gameData.deckBlue.availableCardMask != 0) ? 1 : 0; foreach (TriadCardInstance card in gameData.board) { if (card.owner == ETriadCardOwner.Blue) { numBlue++; } } int numBlueToWin = (gameData.board.Length / 2) + 1; gameData.state = (numBlue > numBlueToWin) ? ETriadGameState.BlueWins : (numBlue == numBlueToWin) ? ETriadGameState.BlueDraw : ETriadGameState.BlueLost; if (gameData.bDebugRules) { TriadCard availBlueCard = gameData.deckBlue.GetFirstAvailableCard(); Logger.WriteLine(">> blue:" + numBlue + " (in deck:" + ((availBlueCard != null) ? availBlueCard.Name.GetCodeName() : "none") + "), required:" + numBlueToWin + " => " + gameData.state); } if ((modFeatures & TriadGameModifier.EFeature.AllPlaced) != 0) { foreach (TriadGameModifier mod in modifiers) { mod.OnAllCardsPlaced(gameData); } } }
// special logic, covered by GUI public static void StaticRandomized(TriadGameData gameData) { if (gameData.bDebugRules) { TriadGameModifierRandom DummyOb = new TriadGameModifierRandom(); Logger.WriteLine(">> " + DummyOb.RuleName + "! blue deck:" + gameData.deckBlue); } }
public bool SolverPlayRandomTurn(TriadGameData gameData, Random random) { #if DEBUG Stopwatch timer = new Stopwatch(); timer.Start(); #endif // DEBUG const int boardPosMax = TriadGameData.boardSize * TriadGameData.boardSize; int boardPos = -1; if (gameData.numCardsPlaced < boardPosMax) { int testPos = random.Next(boardPosMax); for (int passIdx = 0; passIdx < boardPosMax; passIdx++) { testPos = (testPos + 1) % boardPosMax; if (gameData.board[testPos] == null) { boardPos = testPos; break; } } } #if DEBUG perfStats.PlayRandomTurnSelectSpot += timer.ElapsedTicks; timer.Restart(); #endif // DEBUG int cardIdx = -1; TriadDeckInstance useDeck = (gameData.state == ETriadGameState.InProgressBlue) ? gameData.deckBlue : gameData.deckRed; if (useDeck.availableCardMask > 0) { int testIdx = random.Next(TriadDeckInstance.maxAvailableCards); for (int passIdx = 0; passIdx < TriadDeckInstance.maxAvailableCards; passIdx++) { testIdx = (testIdx + 1) % TriadDeckInstance.maxAvailableCards; if ((useDeck.availableCardMask & (1 << testIdx)) != 0) { cardIdx = testIdx; break; } } } #if DEBUG perfStats.PlayRandomTurnSelectCard += timer.ElapsedTicks; timer.Restart(); #endif // DEBUG bool bResult = false; if (cardIdx >= 0) { bResult = PlaceCard(gameData, cardIdx, useDeck, (gameData.state == ETriadGameState.InProgressBlue) ? ETriadCardOwner.Blue : ETriadCardOwner.Red, boardPos); } #if DEBUG perfStats.PlayRandomTurnPlaceCard += timer.ElapsedTicks; timer.Stop(); #endif // DEBUG return(bResult); }
public override void OnCheckCaptureCardWeights(TriadGameData gameData, int boardPos, int neiPos, ref int cardNum, ref int neiNum) { if ((cardNum == 10) && (neiNum == 1)) { cardNum = 0; } else if ((cardNum == 1) && (neiNum == 10)) { neiNum = 0; } }
public TriadGameScreenMemory() { gameSession = new TriadGameSession(); gameState = new TriadGameData(); deckBlue = new TriadDeckInstanceScreen(); deckRed = new TriadDeckInstanceScreen(); blueDeckHistory = new List <TriadCard[]>(); bHasSwapRule = false; swappedBlueCardIdx = -1; lastScanNpc = null; }
public TriadGameData StartGame(TriadDeck deckBlue, TriadDeck deckRed, ETriadGameState state) { TriadGameData gameData = new TriadGameData { state = state, deckBlue = new TriadDeckInstanceManual(deckBlue), deckRed = new TriadDeckInstanceManual(deckRed) }; currentProgress = 0; return(gameData); }
public override void OnFilterNextCards(TriadGameData gameData, ref int allowedCardsMask) { if ((gameData.state == ETriadGameState.InProgressBlue) && (allowedCardsMask != 0)) { int firstBlueIdx = gameData.deckBlue.GetFirstAvailableCardFast(); allowedCardsMask = (firstBlueIdx < 0) ? 0 : (1 << firstBlueIdx); if (gameData.bDebugRules) { TriadCard firstBlueCard = gameData.deckBlue.GetCard(firstBlueIdx); Logger.WriteLine(">> " + RuleName + "! next card: " + (firstBlueCard != null ? firstBlueCard.Name : "none")); } } }
private void CalcWinChance() { if (session != null && deck != null && npc != null) { calcId++; Task.Run(() => { TriadGameData gameState = session.StartGame(deck, npc.Deck, ETriadGameState.InProgressRed); session.SolverFindBestMove(gameState, out int bestNextPos, out TriadCard bestNextCard, out TriadGameResultChance bestChance); OnSolved(contextId, bestChance); }); } }
public static int[] GetNeighbors(TriadGameData gameData, int boardPos) { int boardPosX = 0; int boardPosY = 0; GetBoardXY(boardPos, out boardPosX, out boardPosY); int[] resultNeis = new int[4]; resultNeis[(int)ETriadGameSide.Up] = (boardPosY > 0) ? GetBoardPos(boardPosX, boardPosY - 1) : -1; resultNeis[(int)ETriadGameSide.Down] = (boardPosY < (TriadGameData.boardSize - 1)) ? GetBoardPos(boardPosX, boardPosY + 1) : -1; resultNeis[(int)ETriadGameSide.Right] = (boardPosX > 0) ? GetBoardPos(boardPosX - 1, boardPosY) : -1; resultNeis[(int)ETriadGameSide.Left] = (boardPosX < (TriadGameData.boardSize - 1)) ? GetBoardPos(boardPosX + 1, boardPosY) : -1; return(resultNeis); }
public override void OnCheckCaptureNeis(TriadGameData gameData, int boardPos, int[] neiPos, List <int> captureList) { TriadCardInstance checkCard = gameData.board[boardPos]; int numSame = 0; int neiCaptureMask = 0; for (int sideIdx = 0; sideIdx < 4; sideIdx++) { int testNeiPos = neiPos[sideIdx]; if (testNeiPos >= 0 && gameData.board[testNeiPos] != null) { TriadCardInstance neiCard = gameData.board[testNeiPos]; int numPos = checkCard.GetNumber((ETriadGameSide)sideIdx); int numOther = neiCard.GetOppositeNumber((ETriadGameSide)sideIdx); if (numPos == numOther) { numSame++; if (neiCard.owner != checkCard.owner) { neiCaptureMask |= (1 << sideIdx); } } } } if (numSame >= 2) { for (int sideIdx = 0; sideIdx < 4; sideIdx++) { int testNeiPos = neiPos[sideIdx]; if ((neiCaptureMask & (1 << sideIdx)) != 0) { TriadCardInstance neiCard = gameData.board[testNeiPos]; neiCard.owner = checkCard.owner; captureList.Add(testNeiPos); if (gameData.bDebugRules) { Logger.WriteLine(">> " + RuleName + "! [" + testNeiPos + "] " + neiCard.card.Name.GetCodeName() + " => " + neiCard.owner); } } } } }
// shared with three open public static void StaticMakeKnown(TriadGameData gameData, List <int> redIndices) { const int deckSize = 5; TriadDeckInstanceManual deckRedEx = gameData.deckRed as TriadDeckInstanceManual; if (deckRedEx != null && redIndices.Count <= deckSize) { if (gameData.bDebugRules) { Logger.WriteLine(">> Open:{0}! red indices:{1}", redIndices.Count, string.Join(", ", redIndices)); } TriadDeck redDeckVisible = new TriadDeck(deckRedEx.deck.knownCards, deckRedEx.deck.unknownCardPool); for (int idx = 0; idx < redIndices.Count; idx++) { int cardIdx = redIndices[idx]; if (cardIdx < deckRedEx.deck.knownCards.Count) { // already known, ignore } else { int idxU = cardIdx - deckRedEx.deck.knownCards.Count; var cardOb = deckRedEx.deck.unknownCardPool[idxU]; redDeckVisible.knownCards.Add(cardOb); redDeckVisible.unknownCardPool.Remove(cardOb); } } // safety for impossible state for (int idx = 0; (idx < redDeckVisible.knownCards.Count) && (redDeckVisible.knownCards.Count > deckSize); idx++) { var cardOb = redDeckVisible.knownCards[idx]; int orgIdx = deckRedEx.GetCardIndex(cardOb); if (!redIndices.Contains(orgIdx)) { redDeckVisible.knownCards.RemoveAt(idx); idx--; } } gameData.deckRed = new TriadDeckInstanceManual(redDeckVisible); } }
public bool SolverPlayRandomTurn(TriadGameData gameData, Random random) { const int boardPosMax = TriadGameData.boardSize * TriadGameData.boardSize; int boardPos = -1; if (gameData.numCardsPlaced < boardPosMax) { int testPos = random.Next(boardPosMax); for (int passIdx = 0; passIdx < boardPosMax; passIdx++) { testPos = (testPos + 1) % boardPosMax; if (gameData.board[testPos] == null) { boardPos = testPos; break; } } } int cardIdx = -1; TriadDeckInstance useDeck = (gameData.state == ETriadGameState.InProgressBlue) ? gameData.deckBlue : gameData.deckRed; if (useDeck.availableCardMask > 0) { int testIdx = random.Next(TriadDeckInstance.maxAvailableCards); for (int passIdx = 0; passIdx < TriadDeckInstance.maxAvailableCards; passIdx++) { testIdx = (testIdx + 1) % TriadDeckInstance.maxAvailableCards; if ((useDeck.availableCardMask & (1 << testIdx)) != 0) { cardIdx = testIdx; break; } } } bool bResult = false; if (cardIdx >= 0) { bResult = PlaceCard(gameData, cardIdx, useDeck, (gameData.state == ETriadGameState.InProgressBlue) ? ETriadCardOwner.Blue : ETriadCardOwner.Red, boardPos); } return(bResult); }
public override void OnCardPlaced(TriadGameData gameData, int boardPos) { TriadCardInstance checkCard = gameData.board[boardPos]; if (checkCard.card.Type != ETriadCardType.None) { int scoreMod = gameData.typeMods[(int)checkCard.card.Type]; if (scoreMod != 0) { checkCard.scoreModifier = scoreMod; if (gameData.bDebugRules) { Logger.WriteLine(">> " + RuleName + "! [" + boardPos + "] " + checkCard.card.Name + " is: " + ((scoreMod > 0) ? "+" : "") + scoreMod); } } } }
private int GetDeckScore(TriadGameSession solver, TriadDeck testDeck, Random randomGen, int numGamesDiv) { int deckScore = 0; int maxGames = (numGamesToPlay / numGamesDiv) / 2; for (int IdxGame = 0; IdxGame < maxGames; IdxGame++) { TriadGameData gameDataR = solver.StartGame(testDeck, npc.Deck, ETriadGameState.InProgressRed); ETriadGameState gameRState = solver.SolverPlayRandomGame(gameDataR, randomGen); deckScore += (gameRState == ETriadGameState.BlueWins) ? 2 : (gameRState == ETriadGameState.BlueDraw) ? 1 : 0; TriadGameData gameDataB = solver.StartGame(testDeck, npc.Deck, ETriadGameState.InProgressBlue); ETriadGameState gameBState = solver.SolverPlayRandomGame(gameDataB, randomGen); deckScore += (gameBState == ETriadGameState.BlueWins) ? 2 : (gameBState == ETriadGameState.BlueDraw) ? 1 : 0; } return(deckScore); }
public static void RunSolverStressTest() { #if DEBUG int numIterations = 1000 * 100; Logger.WriteLine("Solver testing start, numIterations:" + numIterations); perfStats = new SolverPerfStats(); Stopwatch timer = new Stopwatch(); timer.Start(); TriadDeck testDeck = new TriadDeck(new int[] { 10, 20, 30, 40, 50 }); TriadNpc testNpc = TriadNpcDB.Get().Find("Garima"); Random randStream = new Random(); TriadGameSession solver = new TriadGameSession(); solver.modifiers.AddRange(testNpc.Rules); solver.UpdateSpecialRules(); for (int Idx = 0; Idx < numIterations; Idx++) { TriadGameData testData = solver.StartGame(testDeck, testNpc.Deck, ETriadGameState.InProgressBlue); Random sessionRand = new Random(randStream.Next()); solver.SolverPlayRandomGame(testData, sessionRand); } timer.Stop(); Logger.WriteLine("Solver testing finished, time taken:" + timer.ElapsedMilliseconds + "ms"); float TicksToMs = 1000.0f / Stopwatch.Frequency; Logger.WriteLine(">> PlayRandomTurn.SelectSpot: " + (perfStats.PlayRandomTurnSelectSpot * TicksToMs) + "ms"); Logger.WriteLine(">> PlayRandomTurn.SelectCard: " + (perfStats.PlayRandomTurnSelectCard * TicksToMs) + "ms"); Logger.WriteLine(">> PlayRandomTurn.PlaceCard: " + (perfStats.PlayRandomTurnPlaceCard * TicksToMs) + "ms"); Logger.WriteLine(" >> PlaceCard.OnPlaced: " + (perfStats.PlaceCardOnPlaced * TicksToMs) + "ms"); Logger.WriteLine(" >> PlaceCard.OnPlacedMods: " + (perfStats.PlaceCardOnPlacedMods * TicksToMs) + "ms"); Logger.WriteLine(" >> PlaceCard.Captures: " + (perfStats.PlaceCardCaptures * TicksToMs) + "ms"); Logger.WriteLine(" >> PlaceCard.CapturesCombo: " + (perfStats.PlaceCardCapturesCombo * TicksToMs) + "ms"); Logger.WriteLine(" >> PlaceCard.PostCaptures: " + (perfStats.PlaceCardPostCaptures * TicksToMs) + "ms"); Logger.WriteLine(" >> PlaceCard.AllPlaced: " + (perfStats.PlaceCardAllPlaced * TicksToMs) + "ms"); #endif // DEBUG }
public TriadGameData(TriadGameData copyFrom) { board = new TriadCardInstance[copyFrom.board.Length]; for (int Idx = 0; Idx < board.Length; Idx++) { board[Idx] = (copyFrom.board[Idx] == null) ? null : new TriadCardInstance(copyFrom.board[Idx]); } typeMods = new int[copyFrom.typeMods.Length]; for (int Idx = 0; Idx < typeMods.Length; Idx++) { typeMods[Idx] = copyFrom.typeMods[Idx]; } deckBlue = copyFrom.deckBlue.CreateCopy(); deckRed = copyFrom.deckRed.CreateCopy(); state = copyFrom.state; numCardsPlaced = copyFrom.numCardsPlaced; numRestarts = copyFrom.numRestarts; resolvedSpecial = copyFrom.resolvedSpecial; // bDebugRules not copied, only first step needs it }