// Global function public GBThreatSequence FindWinningThreatSeq() { // First find a number of possibly winning threat trees. GBSearchModule gbSearch = new GBSearchModule(GoBangBoard.boardDim); GBSpaceState rootState = new GBSpaceState((GoBangBoard)gb.Clone()); rootState.UpdateIsGoal(gbSearch); // HEURISTIC: use category reduction (page 140-141) gbSearch.categoryReductionHeuristicOn = true; DBSearch db = new DBSearch(gbSearch, breadthFirst); db.Search(rootState); //db.DumpDOTGoalsOnly (); // Now, enumerate all the possibly winning threat trees found GBThreatSequence[] potentialWinningSeqs = GBThreatSequence.BuildAllGoalPathes(gbSearch, db.Root); Console.WriteLine("{0} potential winning threat sequences.", potentialWinningSeqs.Length); // Check them one by one until a surely winning threat tree is found GoBangBoard gbFlipped = (GoBangBoard)gb.Clone(); gbFlipped.Flip(); int DEBUGwinningFound = 0; GBThreatSequence DEBUGwinning = null; foreach (GBThreatSequence threatSeq in potentialWinningSeqs) { if (DefenseRefutes(threatSeq, (GoBangBoard)gbFlipped.Clone()) < 0) { // Found a sure win, return early // FIXME: for debugging we count all winning sequences found, // but we should return as early as possible. DEBUGwinningFound += 1; DEBUGwinning = threatSeq; //Console.WriteLine ("WINNING:\n{0}", threatSeq); // FIXME //return (threatSeq); } } Console.WriteLine("{0} winning of {1} potential winning threat sequences identified", DEBUGwinningFound, potentialWinningSeqs.Length); // Found no unrefuted threat sequence return(DEBUGwinning); }
/** Try to find a winning threat sequence by dependency based search and * on the fly refutation for goal nodes. * * Return early, as soon as a sure candidate has been found. * * @param timeoutMS If non-zero, a maximum time spend in db-search is * given in milliseconds. At least 1000 (1s) is meaningful to do * something, though. * * @returns The first winning threat sequence on success, null otherwise. */ public GBThreatSequence FindWinningThreatSeqOTF(int timeoutMS) { // First find a number of possibly winning threat trees. GBSearchModule gbSearch = new GBSearchModule(GoBangBoard.boardDim); GBSpaceState rootState = new GBSpaceState((GoBangBoard)gb.Clone()); rootState.UpdateIsGoal(gbSearch); // HEURISTIC: use category reduction (page 140-141) // FIXME: re-enable as soon as three3 bug is fixed. // FIXME: test if this is good in the real ai, otherwise disable again. //gbSearch.categoryReductionHeuristicOn = true; // Do on-the-fly refutation checking. gbSearch.doDefenseRefutationCheck = true; if (timeoutMS != 0) { gbSearch.doExpirationCheck = true; gbSearch.expireTime = DateTime.Now.AddMilliseconds(timeoutMS); } DBSearch db = new DBSearch(gbSearch, breadthFirst); try { Console.WriteLine("Board:\n{0}\n", gb); db.Search(rootState); //db.DumpDOT (); } catch (GBSearchModule.GBSearchTimeoutException) { // We timed out... Console.WriteLine("FindWinningThreatSeqOTF: timeouted..."); } catch (GBWinningThreatSequenceFoundException gex) { //db.DumpDOT (); return(gex.seq); } return(null); }
/** Test a potential winning threat sequence for refutability. * * @param curBoard The original board the original attacker db-search was * started with (root.State.GB). * @param root The root node of the search tree. * @param goalnode The new goal node identified by db-search. * * @returns null if the sequence is refutable, otherwise the sequence * itself is returned. */ public static GBThreatSequence DefenseRefutable(GoBangBoard curBoard, DBNode root, DBNode goalnode) { // First create the goal path, that is, all nodes that lie on the // direct child path to the node. ArrayList gp = GBThreatSequence.GoalPath(root, goalnode); // Check if there is a clear refutable path node in the path and // return early if this is the case. //Console.WriteLine ("Path:"); foreach (DBNode gpN in gp) { //Console.WriteLine (" {0}", gpN); if (gpN.IsRefutePathRoot) { //Console.WriteLine ("DEBUG: node in refutepath"); return(null); } } // Now combine the operators and build a one-by-one threat sequence. GBThreatSequence seq = GBThreatSequence.BuildThreatSequence(gp); // Clone and flip board GoBangBoard gbFlipped = (GoBangBoard)curBoard.Clone(); gbFlipped.Flip(); Console.Write(" checking potential {0} pair-move sequence: ", gp.Count); Console.Out.Flush(); int refutes = DefenseRefutes(seq, gbFlipped); if (refutes < 0) { Console.WriteLine("un-refutable"); return(seq); } Console.WriteLine("refutable at pair-move {0}", refutes); // Mark root of refutation if (refutes < (gp.Count - 1)) { DBNode refutePathRoot = (DBNode)gp[refutes + 1]; refutePathRoot.IsRefutePathRoot = true; } return(null); }
public void UpdateLegalOperators(int maxCat, bool doCatReduction) { // If we do category reduction, we shall proceed like this: // "... if in a node N of the db-search DAG, the defender has a threat // of category c_1, for each descendent of N the attacker is // restricted to threats of categories less than c_1". // // So we first find out the threats of the defender if (maxCat >= 1 && doCatReduction) { // Reverse attacker/defender GoBangBoard gbFlip = (GoBangBoard)gb.Clone(); gbFlip.Flip(); int lowestCat = GBOperator.LowestOperatorCategory(gbFlip); //Console.WriteLine ("lowestCat = {0}, maxCat = {1}", lowestCat, maxCat); // If the defender has a five already, there is no need to search. if (lowestCat == 0) { this.legalOperators = null; return; } // Now, we have to add one to the lowest category. This is // because the operators judge what they can create in one move, // not what is already there on the board. lowestCat += 1; // Otherwise, if the defenders lowest category threat is below or // equal to the current maximum category, we decrease the maximum // category to allow only those attacker threats that can still // force. if (lowestCat <= maxCat) { Console.WriteLine("doCatRed: from {0} to {1}", maxCat, lowestCat - 1); maxCat = lowestCat - 1; } else { Console.WriteLine("maxCat = {0}", maxCat); } } this.legalOperators = GBOperator.LegalOperators(this, maxCat); }
private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard, int depth) { // If either we reached the end of the sequence (seq is null) or we // have a class zero threat, we consider the sequence to be // un-refutable and return a negative number. if (seq == null || seq.attackerThreatClass == 0) { return(-1); } // Make the attackers move (with -1 now, as the board is flipped) curBoard.board[seq.attacker.Y, seq.attacker.X] = -1; /*Console.WriteLine ("move at ({0},{1})", * "abcdefghijklmnopqrstuvwxyz"[seq.attacker.X], seq.attacker.Y); * * Console.WriteLine ("DEFENSE board is:\n{0}", curBoard); * Console.WriteLine (" attacker threats with {0}", seq.attackerThreatClass);*/ // Now search for possibly winning threat sequences that cover the // goals. To do this, first build the goalsquares int[,] extraGoalSquares = ExtraGoalSquares(seq); // TODO GBSpaceState rootState = new GBSpaceState((GoBangBoard)curBoard.Clone(), seq.attackerThreatClass); GBSearchModule gbSearch = new GBSearchModule(GoBangBoard.boardDim); // Extra constraints (page 137) // // 1. "The goal set U_g for player B should be extended with singleton // goals for occupying any square in threat a_j or reply d_j with j // \geq i." // // 2. "If B find a potential winning threat sequence, ... this threat // sequence is not investigated for counter play of player A. Instead // in such a case we always assume that A's potential winning threat // sequence has been refuted." // // 3. "Thus in a db-search for player B, only threats having replies // consisting of a single move are applied." gbSearch.goalIfOccupied = extraGoalSquares; gbSearch.OneGoalStopsSearch = true; gbSearch.maximumCategory = seq.attackerThreatClass - 1; //Console.WriteLine (" maxCat = {0}", gbSearch.maximumCategory); // Limit the root node's legal operators to only the one with the // appropiate category below the maximum one. Disable the category // reduction heuristics, as we do the exhaustive defense search. rootState.UpdateLegalOperators(gbSearch.maximumCategory, false); // Do the db-search for the defender DBSearch db = new DBSearch(gbSearch, false); db.Search(rootState); if (db.GoalCount > 0) { /* * Console.WriteLine ("Threat below class {0} or goal square occupied.", * gbSearch.maximumCategory); * db.DumpDOT (); * Console.ReadLine ();*/ return(depth); } /*Console.WriteLine ("No class {0} or below threats for the defender found yet", * gbSearch.maximumCategory);*/ // Make defenders move (now attacker 1, with advantage) foreach (GBMove defMove in seq.defender) { curBoard.board[defMove.Y, defMove.X] = 1; } //Console.WriteLine ("BOARD:\n{0}", curBoard); return(DefenseRefutes(seq.next, curBoard, depth + 1)); }
private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard, int depth) { // If either we reached the end of the sequence (seq is null) or we // have a class zero threat, we consider the sequence to be // un-refutable and return a negative number. if (seq == null || seq.attackerThreatClass == 0) return (-1); // Make the attackers move (with -1 now, as the board is flipped) curBoard.board[seq.attacker.Y, seq.attacker.X] = -1; /*Console.WriteLine ("move at ({0},{1})", "abcdefghijklmnopqrstuvwxyz"[seq.attacker.X], seq.attacker.Y); Console.WriteLine ("DEFENSE board is:\n{0}", curBoard); Console.WriteLine (" attacker threats with {0}", seq.attackerThreatClass);*/ // Now search for possibly winning threat sequences that cover the // goals. To do this, first build the goalsquares int[,] extraGoalSquares = ExtraGoalSquares (seq); // TODO GBSpaceState rootState = new GBSpaceState ((GoBangBoard) curBoard.Clone (), seq.attackerThreatClass); GBSearchModule gbSearch = new GBSearchModule (GoBangBoard.boardDim); // Extra constraints (page 137) // // 1. "The goal set U_g for player B should be extended with singleton // goals for occupying any square in threat a_j or reply d_j with j // \geq i." // // 2. "If B find a potential winning threat sequence, ... this threat // sequence is not investigated for counter play of player A. Instead // in such a case we always assume that A's potential winning threat // sequence has been refuted." // // 3. "Thus in a db-search for player B, only threats having replies // consisting of a single move are applied." gbSearch.goalIfOccupied = extraGoalSquares; gbSearch.OneGoalStopsSearch = true; gbSearch.maximumCategory = seq.attackerThreatClass - 1; //Console.WriteLine (" maxCat = {0}", gbSearch.maximumCategory); // Limit the root node's legal operators to only the one with the // appropiate category below the maximum one. Disable the category // reduction heuristics, as we do the exhaustive defense search. rootState.UpdateLegalOperators (gbSearch.maximumCategory, false); // Do the db-search for the defender DBSearch db = new DBSearch (gbSearch, false); db.Search (rootState); if (db.GoalCount > 0) { /* Console.WriteLine ("Threat below class {0} or goal square occupied.", gbSearch.maximumCategory); db.DumpDOT (); Console.ReadLine ();*/ return (depth); } /*Console.WriteLine ("No class {0} or below threats for the defender found yet", gbSearch.maximumCategory);*/ // Make defenders move (now attacker 1, with advantage) foreach (GBMove defMove in seq.defender) curBoard.board[defMove.Y, defMove.X] = 1; //Console.WriteLine ("BOARD:\n{0}", curBoard); return (DefenseRefutes (seq.next, curBoard, depth+1)); }
/** Test a potential winning threat sequence for refutability. * * @param curBoard The original board the original attacker db-search was * started with (root.State.GB). * @param root The root node of the search tree. * @param goalnode The new goal node identified by db-search. * * @returns null if the sequence is refutable, otherwise the sequence * itself is returned. */ public static GBThreatSequence DefenseRefutable(GoBangBoard curBoard, DBNode root, DBNode goalnode) { // First create the goal path, that is, all nodes that lie on the // direct child path to the node. ArrayList gp = GBThreatSequence.GoalPath (root, goalnode); // Check if there is a clear refutable path node in the path and // return early if this is the case. //Console.WriteLine ("Path:"); foreach (DBNode gpN in gp) { //Console.WriteLine (" {0}", gpN); if (gpN.IsRefutePathRoot) { //Console.WriteLine ("DEBUG: node in refutepath"); return (null); } } // Now combine the operators and build a one-by-one threat sequence. GBThreatSequence seq = GBThreatSequence.BuildThreatSequence (gp); // Clone and flip board GoBangBoard gbFlipped = (GoBangBoard) curBoard.Clone (); gbFlipped.Flip (); Console.Write (" checking potential {0} pair-move sequence: ", gp.Count); Console.Out.Flush (); int refutes = DefenseRefutes (seq, gbFlipped); if (refutes < 0) { Console.WriteLine ("un-refutable"); return (seq); } Console.WriteLine ("refutable at pair-move {0}", refutes); // Mark root of refutation if (refutes < (gp.Count-1)) { DBNode refutePathRoot = (DBNode) gp[refutes+1]; refutePathRoot.IsRefutePathRoot = true; } return (null); }