// TODO: this needs to be changed fundamentally, including changes in // DBSearch.cs. // // GoMoku will need up to four nodes combined in one combination step, for // a situation like this (artificial, but illustrates the point): // // O O O // O O O // O3 O2 O1 // // Lets assume the two rows at the top already existed before, and the // states O1, O2 and O3 have been independently explored (as they create a // new application of an operator, this will be the case). However, then // the row O1, O2 and O3 create a new threat. This can only be "seen" by // db-search if they are allowed to be combined in one step. No single // combination will create a new dependent operator. // // We might do up-to-four combination efficiently by exploiting the board // geometry. To do that, we create an array the same dimensions as the // board of linked list, storing states. For each dependency node state, // store the state in a number of linked lists. Store them in that linked // lists that are behind the coordinates in the f_{add} set of the last // operator of the state. // // Then, for each coordinate, we do the following: extract all linked // lists in a G_7 environment centered at the coordinate into one big // linked list. Only check all the states refered in this big list for // 2-, 3- and 4-combinability. For each coordinate we have to do this // four times for the different G_7 environment. // // The pseudo code for the whole combination stage would look like: // // foreach (Coordinate coord in boardCoordinates) { // foreach (G_7 g7 centeredIn coord) { // ArrayList states = new ArrayList (); // foreach (Square sq in g7) // states.AddRange (sq.States); // // foreach (2-Combination (c1, c2) in states) // CheckCombinability (c1, c2); // foreach (3-Combination (c1, c2, c3) in states) // CheckCombinability (c1, c2, c3); // foreach (4-Combination (c1, c2, c3, c4) in states) // CheckCombinability (c1, c2, c3, c4); // } // } // // The numerical complexity is (n \over k) = \frac{n!}{k! (n - k)!} // where n = number of states collected in "states". k = number of // elements in the combination (2, 3, 4). // // So, for n states on the combined G_7 square list this would give // (n \over k) k-Combinations. // // States | 2-C | 3-C | 4-C // -------+------+--------+-------- // 10 | 45 | 120 | 210 // 20 | 190 | 1140 | 4845 // 100 | 4950 | 161700 | 3921225 // // So we should keep the number of states influencing a board square well // below 100, while <= 20 is certainly ok. // // XXX: for now, we only test with two-combinability public IDBSpaceState CombineIfResultIsNewOperators(DBNode node1, Stack node1Path, DBNode node2, Stack node2Path) { GBSpaceState gstate1 = (GBSpaceState)node1.State; GBSpaceState gstate2 = (GBSpaceState)node2.State; GBOperator last2 = gstate2.LastOperator; // First check the boards are not incompatible: // TODO: check if this is right (or necessary, does not seem to change // any results). if (gstate2.GB.CompatibleWith(((GBSpaceState)node1.State).GB) == false) { return(null); } // Build combined state by applying operator chain. ArrayList operatorChain = new ArrayList(); foreach (DBNode pN in node2Path) { if (node1Path.Contains(pN)) { break; } GBSpaceState nstate = (GBSpaceState)pN.State; operatorChain.Add(nstate.LastOperator); if (nstate.CombinedOperPath != null) { ArrayList combRev = (ArrayList)nstate.CombinedOperPath.Clone(); combRev.Reverse(); operatorChain.AddRange(combRev); } } operatorChain.Reverse(); operatorChain.Add(last2); GBSpaceState gComb = (GBSpaceState)node1.State; foreach (GBOperator oper in operatorChain) { gComb = (GBSpaceState)oper.Apply(this, gComb); } //GBSpaceState gComb = (GBSpaceState) last2.Apply (this, node1.State); //gComb.GB.AddExtraStones (gstate2.GB); gComb.UpdateLegalOperators(maximumCategory, categoryReductionHeuristicOn); // Now check if the new state results in new operators GBOperator[] n1o = gstate1.LegalOperators; GBOperator[] n2o = gstate2.LegalOperators; GBOperator[] nCo = gComb.LegalOperators; if (nCo == null) { return(null); } // Check: nCo \setminus (n1o \cup n2o) \neq \emptyset foreach (GBOperator gbo in nCo) { if (n1o != null && Array.IndexOf(n1o, gbo) >= 0) { return(null); } if (n2o != null && Array.IndexOf(n2o, gbo) >= 0) { return(null); } } gComb.UpdateIsGoal(this); // Now that the combination succeeded, we still need to copy over all // the operators we applied 'at once' when copying the field content. // We need to do this for the threat sequence search later, which // needs operator-level granularity for the defense search. gComb.BuildCombinedOperatorPath(node1Path, node2Path); return(gComb); }
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)); }
private void CheckC2(ArrayList[] affecting) { int[] n = new int[2]; for (n[0] = 0; n[0] < affecting.Length; ++n[0]) { for (n[1] = (n[0] + 1); n[1] < affecting.Length; ++n[1]) { int[] count = new int[2]; int countN; if (affecting[n[0]].Count == 0 || affecting[n[1]].Count == 0) { continue; } do { // Enumerate all possible combinations at position (n0, n1) DBNode node1 = (DBNode)affecting[n[0]][count[0]]; DBNode node2 = (DBNode)affecting[n[1]][count[1]]; GBSpaceState state1 = (GBSpaceState)node1.State; GBSpaceState state2 = (GBSpaceState)node2.State; // Check that there is a appropiate dependency node in it and // then for combinability for state1/state2 if ((thisStageDependencies.Contains(state1) == true || thisStageDependencies.Contains(state2) == true) && state2.GB.CompatibleWith(state1.GB) && gbS.NotInConflict(state1, state2)) { GBSpaceState gComb = (GBSpaceState)state2.LastOperator.Apply(gbS, state1); gComb.GB.AddExtraStones(state2.GB); gComb.UpdateLegalOperators(gbS.maximumCategory); // Now check if the new state results in new operators GBOperator[] n1o = state1.LegalOperators; GBOperator[] n2o = state2.LegalOperators; GBOperator[] nCo = gComb.LegalOperators; if (nCo == null) { goto nextComb; } //Console.WriteLine ("nCo.Length = {0}", nCo.Length); // Check: nCo \setminus (n1o \cup n2o) \neq \emptyset bool seenUnique = false; foreach (GBOperator gbo in nCo) { if (n1o != null && Array.IndexOf(n1o, gbo) >= 0) { continue; } if (n2o != null && Array.IndexOf(n2o, gbo) >= 0) { continue; } seenUnique = true; //Console.WriteLine ("unique: {0}", gbo); } if (seenUnique == false) { goto nextComb; } // We have found a new combination /* * Console.WriteLine ("TODO: found found found:"); * Console.WriteLine ("From 1: {0}", state1.GB); * Console.WriteLine ("From 2: {0}", state2.GB); * Console.WriteLine ("To: {0}", gComb.GB); * throw (new ArgumentException ("DEBUG")); */ DBNode combNode = new DBNode(DBNode.NodeType.Combination, gComb, level); combNode.IsGoal = gbS.IsGoal(gComb); /* TODO: have to get this back to db-search somehow, * or throw exception here. * if (combNode.IsGoal) * goalCount += 1; */ dbS.AddCombinationNode(node1, combNode); node2.CombinedChildren.Add(combNode); gbS.RegisterNewNode(combNode); } nextComb: for (countN = 0; countN < count.Length; ++countN) { count[countN] += 1; if (count[countN] < affecting[n[countN]].Count) { break; } count[countN] = 0; } } while (countN < count.Length); } } }