/** Apply the operator to the given state, returning a new state. * * @param state The source state. * * @returns The newly created destination state. */ public IDBSpaceState Apply(DBSearchModule module, IDBSpaceState stateDB) { GBSearchModule gmod = (GBSearchModule)module; GBSpaceState state = (GBSpaceState)stateDB; // TODO: remove later, we already checked this previously if (Valid(state) == false) { throw (new ArgumentException("Operator not applicable!")); } GoBangBoard newBoard = (GoBangBoard)state.GB.Clone(); // Apply all the f_{add} stones for (int n = 0; n < fAdd.GetLength(0); ++n) { newBoard.board[fAdd[n, 1], fAdd[n, 0]] = fAdd[n, 2]; } GBSpaceState newState = new GBSpaceState(newBoard, this, state, gmod.maximumCategory); newState.UpdateIsGoal(gmod); return(newState); }
public bool Applicable(IDBOperator oper, DBNode node) { GBOperator goper = (GBOperator)oper; GBSpaceState gstate = (GBSpaceState)node.State; return(goper.Applicable(gstate)); }
public void RegisterNewNode(DBNode node, DBNode root) { DoExpirationCheck(); if (node.IsGoal == false) { return; } // For goal nodes, we check if on-the-fly refutation is wanted, and if // so, we try to refute the goal node. if (doDefenseRefutationCheck) { GBSpaceState gRootState = (GBSpaceState)root.State; GBThreatSequence seq = GBThreatSearch.DefenseRefutable (gRootState.GB, root, node); if (seq != null) { throw (new GBThreatSearch.GBWinningThreatSequenceFoundException(seq)); } } // This is old code #if false // Now the last node did modify something, but we only want to cache // dependency nodes, as combination nodes will not be combined // themselves. if (node.Type != DBNode.NodeType.Dependency) { return; } GBSpaceState state = (GBSpaceState)node.State; GBOperator last = state.LastOperator; if (last == null) { return; } // Now, we add this node to the array at all its influencing places, // but only where it sets a stone of the attacker (which is the only // position that can create new possibilities for attack). for (int n = 0; n < last.fAdd.GetLength(0); ++n) { // Ignore everything except the attacker stones. if (last.fAdd[n, 2] != 1) { continue; } // Add coordinate nodesThatAffectSquares[last.fAdd[n, 1], last.fAdd[n, 0]].Add(node); return; } #endif }
public GBSpaceState(GoBangBoard gb, GBOperator lastOperator, GBSpaceState lastState, GBOperator[] legalOperators) { this.gb = gb; this.lastOperator = lastOperator; this.lastState = lastState; this.legalOperators = legalOperators; }
// TODO: maybe remove this, its useless now public bool Applicable(GBSpaceState state) { if (Valid(state) == false) { return(false); } return(true); }
public static void Main(string[] args) { GoBangBoard gb = new GoBangBoard(); Random rnd = new Random(); // Initialize board randomly for (int n = 0; n < 130; ++n) { gb.board[rnd.Next(0, boardDim), rnd.Next(0, boardDim)] = rnd.Next(0, 3) - 1; } int count = 0; foreach (StoneSet ss in gb.G5) { Console.Write("ss at ({0},{1}) to ({2},{3}), len {4}: ", ss.x, ss.y, ss.ax, ss.ay, ss.stones.Length); foreach (int stone in ss.stones) { Console.Write("{0}", (stone == 0) ? "." : ((stone == 1) ? "O" : "X")); } Console.WriteLine(); count += 1; } Console.WriteLine("|G5| = {0}", count); count = 0; foreach (StoneSet ss in gb.G6) { count += 1; } Console.WriteLine("|G6| = {0}", count); count = 0; foreach (StoneSet ss in gb.G7) { count += 1; } Console.WriteLine("|G7| = {0}", count); // Test operators a little gb.DumpBoard(); GBSpaceState state = new GBSpaceState(gb); GBOperator[] legalOpers = GBOperator.LegalOperators(state, 2); foreach (GBOperator gop in legalOpers) { Console.WriteLine("oper: {0}", gop); } }
public void BuildCombinedOperatorPath(Stack node1Path, Stack node2Path) { // Root is at the end of the path'es /* * foreach (DBNode dn in node1Path) * Console.WriteLine ("node1Path: {0}", dn); * foreach (DBNode dn in node2Path) * Console.WriteLine ("node2Path: {0}", dn); */ // We need to keep everything in node2Path until we meet a node of // node1 foreach (DBNode pN in node2Path) { if (node1Path.Contains(pN)) { break; } // Now the node pN was reached by applying an operator. // We have two kinds of operators to stack up: the ones in the // 'last' variable, which is the direct last operator applied. // And, because the node can have combinedOperPath operators, too, // we also need to add them. The come chronologically first, but // we build this list in reverse, so we have to reverse them here, // too. GBSpaceState state = (GBSpaceState)pN.State; if (combinedOperPath == null) { combinedOperPath = new ArrayList(); } combinedOperPath.Add(state.LastOperator); // If there are combined operator in the path already, reverse // them. if (state.combinedOperPath != null) { ArrayList combRev = (ArrayList)state.combinedOperPath.Clone(); combRev.Reverse(); combinedOperPath.AddRange(combRev); } } // Make it a chronological chain that comes before 'LastOperator' if (combinedOperPath != null) { combinedOperPath.Reverse(); } }
/** Check if the operator is formally valid. * * Note: we only check if the squares the operator will occupy are * currently free. The logical check if the operator is applicable must * have preceeded this one already. * * @param state The state the operator will be tested for validity on. * * @returns True if the operator is valid, false otherwise. */ public bool Valid(GBSpaceState state) { for (int n = 0; n < fAdd.GetLength(0); ++n) { if (state.GB.board[fAdd[n, 1], fAdd[n, 0]] != 0) { return(false); } } return(true); }
// 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); }
// @param gp Goal path. public static GBThreatSequence BuildThreatSequence(ArrayList gp) { GBThreatSequence ts = new GBThreatSequence(); GBThreatSequence now = ts; for (int n = 0; n < gp.Count; ++n) { DBNode pathnode = (DBNode)gp[n]; GBSpaceState state = (GBSpaceState)pathnode.State; // First add the nodes of the path. if (state.CombinedOperPath != null) { /*Console.WriteLine ("DEBUG CombinedOperPath, {0} moves", * state.CombinedOperPath.Count);*/ foreach (GBOperator oper in state.CombinedOperPath) { //Console.WriteLine (" oper: {0}", oper); FillInMoves(now, oper); now.next = new GBThreatSequence(); now = now.next; } } //Console.WriteLine ("DEBUG LastOperator"); // Now the last operator GBOperator last = (GBOperator)state.LastOperator; if (last == null) { continue; } //Console.WriteLine (" last: {0}", last); FillInMoves(now, last); // Chain it up, baby. now.next = null; if (n < (gp.Count - 1)) { now.next = new GBThreatSequence(); now = now.next; } } return(ts); }
public bool NotInConflict(GBSpaceState gstate1, GBSpaceState gstate2) { // We only need to check the last operator chain, as all previous have // been checked in the previous stages/levels while (gstate2 != null && gstate2.LastOperator != null) { if (Applicable(gstate2.LastOperator, gstate1) == false) { return(false); } gstate2 = gstate2.LastState; } return(true); }
public static int[,] BuildOperatorMap(GoBangBoard board, out int maxCount) { // Get legal operators for the state represented by the board GBSpaceState state = new GBSpaceState(board); GBOperator[] opers = GBOperator.LegalOperators(state, 2); // Now build an attack count map and keep track of the maximum number // of operators a single attack stone creates. maxCount = 0; int[,] attackMap = new int[GoBangBoard.boardDim, GoBangBoard.boardDim]; foreach (GBOperator op in opers) { if (op is GBOperatorThree3) { continue; } for (int n = 0; n < op.fAdd.GetLength(0); ++n) { // We are not (yet) interested in the defending stones. if (op.fAdd[n, 2] == -1) { continue; } int x = op.fAdd[n, 0]; int y = op.fAdd[n, 1]; attackMap[y, x] += 1; Console.WriteLine("at ({0}, {1}) oper: {2}", x, y, op); if (attackMap[y, x] > maxCount) { maxCount = attackMap[y, x]; } } } return(attackMap); }
/** 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); }
public static GBOperator[] LegalOperators(GBSpaceState state, int maxCat) { ArrayList opersGoal = new ArrayList (); ArrayList opersFours = new ArrayList (); ArrayList opersThrees = new ArrayList (); // Check G_5 for operators foreach (GoBangBoard.StoneSet ss in state.GB.G5) { if (IsLastOperatorDependent (ss, state.LastOperator) == false) continue; GBOperatorFive[] fives = null; if (maxCat >= 0) fives = GBOperatorFive.GetOperatorsIfValid (ss); GBOperatorFour[] fours = null; if (maxCat >= 1) fours = GBOperatorFour.GetOperatorsIfValid (ss); if (fives != null) opersGoal.AddRange (fives); if (fours != null) opersFours.AddRange (fours); } bool Three2Applicable = false; if (maxCat >= 2) { // Check G_7 for operators foreach (GoBangBoard.StoneSet ss in state.GB.G7) { if (IsLastOperatorDependent (ss, state.LastOperator) == false) continue; /* Console.Write ("7ss: "); for (int n = 0 ; n < ss.stones.Length ; ++n) Console.Write ("{0} ", ss.stones[n] == 1 ? "O" : (ss.stones[n] == -1 ? "X" : ".")); Console.WriteLine (); */ GBOperatorThree2[] three2 = GBOperatorThree2.GetOperatorsIfValid (ss); if (three2 != null) { Three2Applicable = true; opersThrees.AddRange (three2); } } } // Check G_6 for operators if (maxCat >= 1) { foreach (GoBangBoard.StoneSet ss in state.GB.G6) { if (IsLastOperatorDependent (ss, state.LastOperator) == false) continue; GBOperatorStraightFour[] sfours = null; if (maxCat >= 1) sfours = GBOperatorStraightFour.GetOperatorsIfValid (ss); GBOperatorBrokenThree[] bthrees = null; GBOperatorThree3[] three3s = null; if (maxCat >= 2) { bthrees = GBOperatorBrokenThree.GetOperatorsIfValid (ss); } // Heuristic "restricted trees", page 141. // HEURISTIC // FIXME: re-enable this after testing. if (maxCat >= 2 /*&& Three2Applicable == false*/) { three3s = GBOperatorThree3.GetOperatorsIfValid (ss); } if (sfours != null) opersGoal.AddRange (sfours); if (bthrees != null) opersThrees.AddRange (bthrees); if (three3s != null) opersThrees.AddRange (three3s); } } // Order: goal, fours, threes opersGoal.AddRange (opersFours); opersGoal.AddRange (opersThrees); if (opersGoal.Count > 0) return ((GBOperator[]) opersGoal.ToArray (typeof (GBOperator))); return (null); }
public IDBOperator[] LegalOperators(DBNode node) { GBSpaceState gstate = (GBSpaceState)node.State; return(gstate.LegalOperators); }
/** Test the GBSearchModule */ public static void Main(string[] args) { // Initialize a board randomly GoBangBoard gb = new GoBangBoard(); Random rnd = new Random(); /* * for (int n = 0 ; n < 23 ; ++n) * gb.board[rnd.Next (0, gb.boardDim), rnd.Next (0, gb.boardDim)] = * rnd.Next (0, 3) - 1; */ /* * gb.board[5,5] = gb.board[5,8] = -1; * gb.board[7,4] = gb.board[7,9] = -1; * * gb.board[5,4] = gb.board[5,6] = gb.board[5,7] = gb.board[5,9] = 1; * gb.board[7,6] = gb.board[7,7] = 1; */ gb.board[6, 6] = gb.board[6, 7] = gb.board[6, 8] = 1; gb.board[7, 7] = gb.board[8, 6] = gb.board[8, 7] = gb.board[8, 8] = 1; gb.board[9, 6] = gb.board[10, 5] = gb.board[10, 6] = gb.board[10, 7] = 1; gb.board[6, 5] = gb.board[7, 5] = gb.board[7, 6] = gb.board[7, 8] = -1; gb.board[8, 5] = gb.board[8, 9] = gb.board[9, 5] = gb.board[9, 7] = gb.board[9, 9] = -1; gb.board[10, 4] = gb.board[11, 6] = -1; gb.board[5, 5] = 1; gb.board[4, 4] = -1; gb.board[6, 9] = 1; gb.board[6, 10] = -1; gb.board[4, 7] = 1; gb.board[5, 7] = -1; /* * gb.board[6,10] = 1; * gb.board[6,9] = -1; */ /* * gb.board[6,6] = gb.board[6,7] = gb.board[6,8] = 1; * gb.board[7,7] = gb.board[8,6] = gb.board[8,7] = gb.board[8,8] = 1; * gb.board[9,6] = gb.board[10,5] = gb.board[10,6] = gb.board[10,7] = 1; * * gb.board[6,5] = gb.board[7,5] = gb.board[7,6] = gb.board[7,8] = -1; * gb.board[8,5] = gb.board[8,9] = gb.board[9,5] = gb.board[9,7] = gb.board[9,9] = -1; * gb.board[10,4] = gb.board[11,6] = -1; * * // Move 1/2 * gb.board[5,5] = 1; * gb.board[4,4] = -1; * * // Move 3/4 * gb.board[5,7] = 1; * gb.board[4,7] = -1; * * // Move 5/6 * gb.board[5,9] = 1; * gb.board[4,10] = -1; * * // Move 7/8 * gb.board[5,8] = 1; * gb.board[5,6] = -1; * * // Move 9/10 * gb.board[5,11] = 1; * gb.board[5,10] = -1; * * // Move 11/12 * gb.board[6,10] = 1; * gb.board[6,9] = -1; * * // Move 13/14 * gb.board[7,9] = 1; * gb.board[4,12] = -1; * * // Move 15/16 * gb.board[4,6] = 1; * gb.board[3,5] = -1; */ /* TODO: check this, ask marco * gb.board[4,4] = gb.board[6,6] = gb.board[7,7] = 1; */ GBSearchModule gbSearch = new GBSearchModule(GoBangBoard.boardDim); GBSpaceState rootState = new GBSpaceState(gb); rootState.UpdateIsGoal(gbSearch); DBSearch db = new DBSearch(gbSearch, false); db.Search(rootState); db.DumpDOT(); //db.DumpDOTGoalsOnly (); gbSearch.DEBUGnodeArray(); /* * foreach (DLPSpaceState state in db.GoalStates ()) { * DumpOperatorChain (state); * } */ }
/** Check if the operator is formally valid. * * Note: we only check if the squares the operator will occupy are * currently free. The logical check if the operator is applicable must * have preceeded this one already. * * @param state The state the operator will be tested for validity on. * * @returns True if the operator is valid, false otherwise. */ public bool Valid(GBSpaceState state) { for (int n = 0 ; n < fAdd.GetLength (0) ; ++n) { if (state.GB.board[fAdd[n,1], fAdd[n,0]] != 0) return (false); } return (true); }
public GBSpaceState(GoBangBoard gb, GBOperator lastOperator, GBSpaceState lastState, int maxCat) : this(gb, lastOperator, lastState, null) { this.legalOperators = GBOperator.LegalOperators(this, maxCat); }
// 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); }
// 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); }
public static int[,] BuildOperatorMap(GoBangBoard board, out int maxCount) { // Get legal operators for the state represented by the board GBSpaceState state = new GBSpaceState (board); GBOperator[] opers = GBOperator.LegalOperators (state, 2); // Now build an attack count map and keep track of the maximum number // of operators a single attack stone creates. maxCount = 0; int[,] attackMap = new int[GoBangBoard.boardDim, GoBangBoard.boardDim]; foreach (GBOperator op in opers) { if (op is GBOperatorThree3) continue; for (int n = 0 ; n < op.fAdd.GetLength (0) ; ++n) { // We are not (yet) interested in the defending stones. if (op.fAdd[n,2] == -1) continue; int x = op.fAdd[n,0]; int y = op.fAdd[n,1]; attackMap[y, x] += 1; Console.WriteLine ("at ({0}, {1}) oper: {2}", x, y, op); if (attackMap[y, x] > maxCount) maxCount = attackMap[y, x]; } } return (attackMap); }
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)); }
/** 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); }
public bool Applicable(IDBOperator oper, GBSpaceState state) { GBOperator goper = (GBOperator)oper; return(goper.Applicable(state)); }
public int CompareTo(object o2) { GBSpaceState gbs2 = (GBSpaceState)o2; return(gb.CompareTo(gbs2.gb)); }
public GBSpaceState(GoBangBoard gb, GBOperator lastOperator, GBSpaceState lastState, int maxCat) : this(gb, lastOperator, lastState, null) { this.legalOperators = GBOperator.LegalOperators (this, maxCat); }
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); } } }
/** Check the goal conditions on page 135. * * @param state The space state to be checked for goal'iness. * * @returns True in case it is a goal state, false otherwise. */ public bool IsGoal(IDBSpaceState stateDB) { GBSpaceState state = (GBSpaceState)stateDB; return(state.IsGoal); }
/** Apply the operator to the given state, returning a new state. * * @param state The source state. * * @returns The newly created destination state. */ public IDBSpaceState Apply(DBSearchModule module, IDBSpaceState stateDB) { GBSearchModule gmod = (GBSearchModule) module; GBSpaceState state = (GBSpaceState) stateDB; // TODO: remove later, we already checked this previously if (Valid (state) == false) throw (new ArgumentException ("Operator not applicable!")); GoBangBoard newBoard = (GoBangBoard) state.GB.Clone (); // Apply all the f_{add} stones for (int n = 0 ; n < fAdd.GetLength (0) ; ++n) newBoard.board[fAdd[n,1], fAdd[n,0]] = fAdd[n,2]; GBSpaceState newState = new GBSpaceState (newBoard, this, state, gmod.maximumCategory); newState.UpdateIsGoal (gmod); return (newState); }
public bool Applicable(IDBOperator oper, GBSpaceState state) { GBOperator goper = (GBOperator) oper; return (goper.Applicable (state)); }
public bool NotInConflict(GBSpaceState gstate1, GBSpaceState gstate2) { // We only need to check the last operator chain, as all previous have // been checked in the previous stages/levels while (gstate2 != null && gstate2.LastOperator != null) { if (Applicable (gstate2.LastOperator, gstate1) == false) return (false); gstate2 = gstate2.LastState; } return (true); }
/** Test the GBSearchModule */ public static void Main(string[] args) { // Initialize a board randomly GoBangBoard gb = new GoBangBoard (); Random rnd = new Random (); /* for (int n = 0 ; n < 23 ; ++n) gb.board[rnd.Next (0, gb.boardDim), rnd.Next (0, gb.boardDim)] = rnd.Next (0, 3) - 1; */ /* gb.board[5,5] = gb.board[5,8] = -1; gb.board[7,4] = gb.board[7,9] = -1; gb.board[5,4] = gb.board[5,6] = gb.board[5,7] = gb.board[5,9] = 1; gb.board[7,6] = gb.board[7,7] = 1; */ gb.board[6,6] = gb.board[6,7] = gb.board[6,8] = 1; gb.board[7,7] = gb.board[8,6] = gb.board[8,7] = gb.board[8,8] = 1; gb.board[9,6] = gb.board[10,5] = gb.board[10,6] = gb.board[10,7] = 1; gb.board[6,5] = gb.board[7,5] = gb.board[7,6] = gb.board[7,8] = -1; gb.board[8,5] = gb.board[8,9] = gb.board[9,5] = gb.board[9,7] = gb.board[9,9] = -1; gb.board[10,4] = gb.board[11,6] = -1; gb.board[5,5] = 1; gb.board[4,4] = -1; gb.board[6,9] = 1; gb.board[6,10] = -1; gb.board[4,7] = 1; gb.board[5,7] = -1; /* gb.board[6,10] = 1; gb.board[6,9] = -1; */ /* gb.board[6,6] = gb.board[6,7] = gb.board[6,8] = 1; gb.board[7,7] = gb.board[8,6] = gb.board[8,7] = gb.board[8,8] = 1; gb.board[9,6] = gb.board[10,5] = gb.board[10,6] = gb.board[10,7] = 1; gb.board[6,5] = gb.board[7,5] = gb.board[7,6] = gb.board[7,8] = -1; gb.board[8,5] = gb.board[8,9] = gb.board[9,5] = gb.board[9,7] = gb.board[9,9] = -1; gb.board[10,4] = gb.board[11,6] = -1; // Move 1/2 gb.board[5,5] = 1; gb.board[4,4] = -1; // Move 3/4 gb.board[5,7] = 1; gb.board[4,7] = -1; // Move 5/6 gb.board[5,9] = 1; gb.board[4,10] = -1; // Move 7/8 gb.board[5,8] = 1; gb.board[5,6] = -1; // Move 9/10 gb.board[5,11] = 1; gb.board[5,10] = -1; // Move 11/12 gb.board[6,10] = 1; gb.board[6,9] = -1; // Move 13/14 gb.board[7,9] = 1; gb.board[4,12] = -1; // Move 15/16 gb.board[4,6] = 1; gb.board[3,5] = -1; */ /* TODO: check this, ask marco gb.board[4,4] = gb.board[6,6] = gb.board[7,7] = 1; */ GBSearchModule gbSearch = new GBSearchModule (GoBangBoard.boardDim); GBSpaceState rootState = new GBSpaceState (gb); rootState.UpdateIsGoal (gbSearch); DBSearch db = new DBSearch (gbSearch, false); db.Search (rootState); db.DumpDOT (); //db.DumpDOTGoalsOnly (); gbSearch.DEBUGnodeArray (); /* foreach (DLPSpaceState state in db.GoalStates ()) { DumpOperatorChain (state); } */ }
public static GBOperator[] LegalOperators(GBSpaceState state, int maxCat) { ArrayList opersGoal = new ArrayList(); ArrayList opersFours = new ArrayList(); ArrayList opersThrees = new ArrayList(); // Check G_5 for operators foreach (GoBangBoard.StoneSet ss in state.GB.G5) { if (IsLastOperatorDependent(ss, state.LastOperator) == false) { continue; } GBOperatorFive[] fives = null; if (maxCat >= 0) { fives = GBOperatorFive.GetOperatorsIfValid(ss); } GBOperatorFour[] fours = null; if (maxCat >= 1) { fours = GBOperatorFour.GetOperatorsIfValid(ss); } if (fives != null) { opersGoal.AddRange(fives); } if (fours != null) { opersFours.AddRange(fours); } } bool Three2Applicable = false; if (maxCat >= 2) { // Check G_7 for operators foreach (GoBangBoard.StoneSet ss in state.GB.G7) { if (IsLastOperatorDependent(ss, state.LastOperator) == false) { continue; } /* * Console.Write ("7ss: "); * for (int n = 0 ; n < ss.stones.Length ; ++n) * Console.Write ("{0} ", ss.stones[n] == 1 ? "O" : * (ss.stones[n] == -1 ? "X" : ".")); * Console.WriteLine (); */ GBOperatorThree2[] three2 = GBOperatorThree2.GetOperatorsIfValid(ss); if (three2 != null) { Three2Applicable = true; opersThrees.AddRange(three2); } } } // Check G_6 for operators if (maxCat >= 1) { foreach (GoBangBoard.StoneSet ss in state.GB.G6) { if (IsLastOperatorDependent(ss, state.LastOperator) == false) { continue; } GBOperatorStraightFour[] sfours = null; if (maxCat >= 1) { sfours = GBOperatorStraightFour.GetOperatorsIfValid(ss); } GBOperatorBrokenThree[] bthrees = null; GBOperatorThree3[] three3s = null; if (maxCat >= 2) { bthrees = GBOperatorBrokenThree.GetOperatorsIfValid(ss); } // Heuristic "restricted trees", page 141. // HEURISTIC // FIXME: re-enable this after testing. if (maxCat >= 2 /*&& Three2Applicable == false*/) { three3s = GBOperatorThree3.GetOperatorsIfValid(ss); } if (sfours != null) { opersGoal.AddRange(sfours); } if (bthrees != null) { opersThrees.AddRange(bthrees); } if (three3s != null) { opersThrees.AddRange(three3s); } } } // Order: goal, fours, threes opersGoal.AddRange(opersFours); opersGoal.AddRange(opersThrees); if (opersGoal.Count > 0) { return((GBOperator[])opersGoal.ToArray(typeof(GBOperator))); } return(null); }
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)); }
public static void Main(string[] args) { GoBangBoard gb = new GoBangBoard (); Random rnd = new Random (); // Initialize board randomly for (int n = 0 ; n < 130 ; ++n) gb.board[rnd.Next (0, boardDim), rnd.Next (0, boardDim)] = rnd.Next (0, 3) - 1; int count = 0; foreach (StoneSet ss in gb.G5) { Console.Write ("ss at ({0},{1}) to ({2},{3}), len {4}: ", ss.x, ss.y, ss.ax, ss.ay, ss.stones.Length); foreach (int stone in ss.stones) { Console.Write ("{0}", (stone == 0) ? "." : ((stone == 1) ? "O" : "X")); } Console.WriteLine (); count += 1; } Console.WriteLine ("|G5| = {0}", count); count = 0; foreach (StoneSet ss in gb.G6) count += 1; Console.WriteLine ("|G6| = {0}", count); count = 0; foreach (StoneSet ss in gb.G7) count += 1; Console.WriteLine ("|G7| = {0}", count); // Test operators a little gb.DumpBoard (); GBSpaceState state = new GBSpaceState (gb); GBOperator[] legalOpers = GBOperator.LegalOperators (state, 2); foreach (GBOperator gop in legalOpers) Console.WriteLine ("oper: {0}", gop); }
public static GBOperator[] LegalOperators(DBNode node, int maxCat) { GBSpaceState dst = (GBSpaceState)node.State; return(LegalOperators(dst, maxCat)); }
// TODO: maybe remove this, its useless now public bool Applicable(GBSpaceState state) { if (Valid (state) == false) return (false); return (true); }