public static void RunTest(string configPath, bool debugMode) { string testName = System.IO.Path.GetFileNameWithoutExtension(configPath); string configText = System.IO.File.ReadAllText(configPath); JsonParser.ObjectValue rootOb = JsonParser.ParseJson(configText); if (rootOb["type"] != "Screen") { return; } ScannerTriad.VerifyConfig configData = new ScannerTriad.VerifyConfig(); configData.Load(rootOb); // setup npc & modifiers TriadNpc testNpc = TriadNpcDB.Get().Find(configData.npc); if (testNpc == null) { string exceptionMsg = string.Format("Test {0} failed! Can't find npc: {1}", testName, configData.npc); throw new Exception(exceptionMsg); } ScannerTriad.GameState screenGame = new ScannerTriad.GameState(); if (mapValidationRules == null) { mapValidationRules = new Dictionary <string, TriadGameModifier>(); foreach (TriadGameModifier mod in ImageHashDB.Get().modObjects) { mapValidationRules.Add(mod.GetCodeName(), mod); } } foreach (string modName in configData.rules) { screenGame.mods.Add(mapValidationRules[modName]); } Func <ScannerTriad.VerifyCard, TriadCard> ConvertToTriadCard = configCard => { if (configCard.state == ScannerTriad.ECardState.None) { return(null); } if (configCard.state == ScannerTriad.ECardState.Hidden) { return(TriadCardDB.Get().hiddenCard); } TriadCard matchingCard = !string.IsNullOrEmpty(configCard.name) ? TriadCardDB.Get().Find(configCard.name) : TriadCardDB.Get().Find(configCard.sides[0], configCard.sides[1], configCard.sides[2], configCard.sides[3]); if (matchingCard == null) { string exceptionMsg = string.Format("Test {0} failed! Can't match validation card: '{1}' [{2},{3},{4},{5}]", testName, configCard.name, configCard.sides[0], configCard.sides[1], configCard.sides[2], configCard.sides[3]); throw new Exception(exceptionMsg); } return(matchingCard); }; bool needsLockedBlue = false; for (int idx = 0; idx < 5; idx++) { screenGame.blueDeck[idx] = ConvertToTriadCard(configData.deckBlue[idx]); screenGame.redDeck[idx] = ConvertToTriadCard(configData.deckRed[idx]); if (configData.deckBlue[idx].state == ScannerTriad.ECardState.Locked) { needsLockedBlue = true; } } if (needsLockedBlue) { for (int idx = 0; idx < 5; idx++) { if (configData.deckBlue[idx].state == ScannerTriad.ECardState.Visible) { screenGame.forcedBlueCard = screenGame.blueDeck[idx]; break; } } } for (int idx = 0; idx < 9; idx++) { screenGame.board[idx] = ConvertToTriadCard(configData.board[idx]); screenGame.boardOwner[idx] = configData.board[idx].state == ScannerTriad.ECardState.PlacedBlue ? ETriadCardOwner.Blue : configData.board[idx].state == ScannerTriad.ECardState.PlacedRed ? ETriadCardOwner.Red : ETriadCardOwner.Unknown; } TriadGameScreenMemory screenMemory = new TriadGameScreenMemory { logScan = false }; screenMemory.OnNewScan(screenGame, testNpc); screenMemory.gameSession.SolverFindBestMove(screenMemory.gameState, out int solverBoardPos, out TriadCard solverTriadCard, out TriadGameResultChance bestChance); if (bestChance.expectedResult == ETriadGameState.BlueLost && bestChance.winChance <= 0.0f && bestChance.drawChance <= 0.0f) { string exceptionMsg = string.Format("Test {0} failed! Can't find move!", testName); throw new Exception(exceptionMsg); } }
public EUpdateFlags OnNewScan(ScannerTriad.GameState screenGame, TriadNpc selectedNpc) { EUpdateFlags updateFlags = EUpdateFlags.None; if (screenGame == null) { return(updateFlags); } // check if game from screenshot can be continuation of cached one // is current state a continuation of last one? // ideally each blue turn is a capture until game resets = any card disappears from board // guess work for adding sense of persistence to screen decks bool bContinuesPrevState = (deckRed.deck == selectedNpc.Deck) && (lastScanNpc == selectedNpc); if (bContinuesPrevState) { for (int Idx = 0; Idx < gameState.board.Length; Idx++) { bool bWasNull = gameState.board[Idx] == null; bool bIsNull = screenGame.board[Idx] == null; if (!bWasNull && bIsNull) { bContinuesPrevState = false; if (logScan) { Logger.WriteLine("Can't continue previous state: board[" + Idx + "] disappeared "); } } } } else { if (logScan) { Logger.WriteLine("Can't continue previous state: npc changed"); } } bool bModsChanged = (gameSession.modifiers.Count != screenGame.mods.Count) || !gameSession.modifiers.All(screenGame.mods.Contains); if (bModsChanged) { bHasSwapRule = false; bHasRestartRule = false; bHasOpenRule = false; gameSession.modifiers.Clear(); gameSession.modifiers.AddRange(screenGame.mods); gameSession.specialRules = ETriadGameSpecialMod.None; gameSession.modFeatures = TriadGameModifier.EFeature.None; foreach (TriadGameModifier mod in gameSession.modifiers) { gameSession.modFeatures |= mod.GetFeatures(); // swap rule is bad for screenshot based analysis, no good way of telling what is out of place if (mod is TriadGameModifierSwap) { bHasSwapRule = true; } else if (mod is TriadGameModifierSuddenDeath) { bHasRestartRule = true; } else if (mod is TriadGameModifierAllOpen) { bHasOpenRule = true; } } updateFlags |= EUpdateFlags.Modifiers; bContinuesPrevState = false; if (logScan) { Logger.WriteLine("Can't continue previous state: modifiers changed"); } deckRed.SetSwappedCard(null, -1); } // wipe blue deck history when playing with new npc (or region modifiers have changed) bool bRemoveBlueHistory = bModsChanged || (lastScanNpc != selectedNpc); if (bRemoveBlueHistory) { blueDeckHistory.Clear(); if (bHasSwapRule && logScan) { Logger.WriteLine("Blue deck history cleared"); } } bool bRedDeckChanged = (lastScanNpc != selectedNpc) || !IsDeckMatching(deckRed, screenGame.redDeck) || (deckRed.deck != selectedNpc.Deck); if (bRedDeckChanged) { updateFlags |= EUpdateFlags.RedDeck; deckRed.deck = selectedNpc.Deck; lastScanNpc = selectedNpc; // needs to happen before any changed to board (gameState) UpdateAvailableRedCards(deckRed, screenGame.redDeck, screenGame.blueDeck, screenGame.board, deckBlue.cards, gameState.board, bContinuesPrevState); } bool bBlueDeckChanged = !IsDeckMatching(deckBlue, screenGame.blueDeck); if (bBlueDeckChanged) { updateFlags |= EUpdateFlags.BlueDeck; deckBlue.UpdateAvailableCards(screenGame.blueDeck); } gameState.state = ETriadGameState.InProgressBlue; gameState.deckBlue = deckBlue; gameState.deckRed = deckRed; gameState.numCardsPlaced = 0; gameState.forcedCardIdx = deckBlue.GetCardIndex(screenGame.forcedBlueCard); bool bBoardChanged = false; for (int Idx = 0; Idx < gameState.board.Length; Idx++) { bool bWasNull = gameState.board[Idx] == null; bool bIsNull = screenGame.board[Idx] == null; if (bWasNull && !bIsNull) { bBoardChanged = true; gameState.board[Idx] = new TriadCardInstance(screenGame.board[Idx], screenGame.boardOwner[Idx]); if (logScan) { Logger.WriteLine(" board update: [" + Idx + "] " + gameState.board[Idx].owner + ": " + gameState.board[Idx].card.Name.GetCodeName()); } } else if (!bWasNull && bIsNull) { bBoardChanged = true; gameState.board[Idx] = null; } else if (!bWasNull && !bIsNull) { if (gameState.board[Idx].owner != screenGame.boardOwner[Idx] || gameState.board[Idx].card != screenGame.board[Idx]) { bBoardChanged = true; gameState.board[Idx] = new TriadCardInstance(screenGame.board[Idx], screenGame.boardOwner[Idx]); } } gameState.numCardsPlaced += (gameState.board[Idx] != null) ? 1 : 0; } if (bBoardChanged) { updateFlags |= EUpdateFlags.Board; foreach (TriadGameModifier mod in gameSession.modifiers) { mod.OnScreenUpdate(gameState); } } // start of game, do additional checks when swap rule is active if (bHasSwapRule && gameState.numCardsPlaced <= 1) { updateFlags |= DetectSwapOnGameStart(); } if (logScan) { Logger.WriteLine("OnNewScan> board:" + (bBoardChanged ? "changed" : "same") + ", blue:" + (bBlueDeckChanged ? "changed" : "same") + ", red:" + (bRedDeckChanged ? "changed" : "same") + ", mods:" + (bModsChanged ? "changed" : "same") + ", continuePrev:" + bContinuesPrevState + " => " + ((updateFlags != EUpdateFlags.None) ? ("UPDATE[" + updateFlags + "]") : "skip")); } return(updateFlags); }