public bool Applicable(IDBOperator oper, DBNode node) { GBOperator goper = (GBOperator)oper; GBSpaceState gstate = (GBSpaceState)node.State; return(goper.Applicable(gstate)); }
private static bool IsLastOperatorDependent(GoBangBoard.StoneSet ss, GBOperator last) { // Special case for root node if (last == null) { return(true); } int px = ss.x, py = ss.y; for (int n = 0; n < ss.stones.Length; ++n) { // Now check if it could theoretically depend. Then the // last operator must have touched (fAdd) stones available // in this stoneset. for (int k = 0; k < last.fAdd.GetLength(0); ++k) { if (last.fAdd[k, 0] == px && last.fAdd[k, 1] == py) { return(true); } } py += ss.ay; px += ss.ax; } return(false); }
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; }
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); } }
// @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 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); }
/** Test for operator equality. This is important as to identify common * operators in combination nodes. * * @param o2 The other operator to be compared. * * @returns True in case they are equal, false otherwise. */ public override bool Equals(object o2) { if (o2 == null) { return(false); } GBOperator op2 = (GBOperator)o2; if (Name != op2.Name) { return(false); } if (fAdd.GetLength(0) != op2.fAdd.GetLength(0)) { return(false); } // XXX: Tricky, as we have not specified an ordering for the f_{add} // set (which we maybe should do, as we could get rid of the inner // loop then). So, operator equality should be on board level, hence // we need to compare the stone coordinates, and not the relative // settings in the fAdd array. for (int n1 = 0; n1 < fAdd.GetLength(0); ++n1) { for (int n2 = 0; n2 < fAdd.GetLength(0); ++n2) { if (fAdd[n1, 0] == op2.fAdd[n2, 0] && fAdd[n1, 1] == op2.fAdd[n2, 1] && fAdd[n1, 2] == op2.fAdd[n2, 2]) { goto foundStone; } } // Stone was not found, operators are not equal return(false); foundStone: ; } // All stones have been found. 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); }
private static void FillInMoves(GBThreatSequence seq, GBOperator oper) { // Create attacker and defender moves. ArrayList defenderMoves = new ArrayList(); //Console.WriteLine ("DEBUG FillInMoves, {0} moves", oper.fAdd.GetLength (0)); for (int move = 0; move < oper.fAdd.GetLength(0); ++move) { // Attacker move if (oper.fAdd[move, 2] == 1) { seq.attacker = new GBMove(oper.fAdd[move, 0], oper.fAdd[move, 1], oper.fAdd[move, 2]); seq.attackerThreatClass = oper.Class; } else if (oper.fAdd[move, 2] == -1) { defenderMoves.Add(new GBMove(oper.fAdd[move, 0], oper.fAdd[move, 1], oper.fAdd[move, 2])); } } seq.defender = (GBMove[])defenderMoves.ToArray(typeof(GBMove)); }
private static bool IsLastOperatorDependent(GoBangBoard.StoneSet ss, GBOperator last) { // Special case for root node if (last == null) return (true); int px = ss.x, py = ss.y; for (int n = 0 ; n < ss.stones.Length ; ++n) { // Now check if it could theoretically depend. Then the // last operator must have touched (fAdd) stones available // in this stoneset. for (int k = 0 ; k < last.fAdd.GetLength (0) ; ++k) { if (last.fAdd[k,0] == px && last.fAdd[k,1] == py) return (true); } py += ss.ay; px += ss.ax; } return (false); }
public GBSpaceState(GoBangBoard gb, GBOperator lastOperator, GBSpaceState lastState, int maxCat) : this(gb, lastOperator, lastState, null) { this.legalOperators = GBOperator.LegalOperators(this, maxCat); }
private static void FillInMoves(GBThreatSequence seq, GBOperator oper) { // Create attacker and defender moves. ArrayList defenderMoves = new ArrayList (); //Console.WriteLine ("DEBUG FillInMoves, {0} moves", oper.fAdd.GetLength (0)); for (int move = 0 ; move < oper.fAdd.GetLength (0) ; ++move) { // Attacker move if (oper.fAdd[move, 2] == 1) { seq.attacker = new GBMove (oper.fAdd[move, 0], oper.fAdd[move, 1], oper.fAdd[move, 2]); seq.attackerThreatClass = oper.Class; } else if (oper.fAdd[move, 2] == -1) { defenderMoves.Add (new GBMove (oper.fAdd[move, 0], oper.fAdd[move, 1], oper.fAdd[move, 2])); } } seq.defender = (GBMove[]) defenderMoves.ToArray (typeof (GBMove)); }
public bool UpdateIsGoal(DBSearchModule dbS) { GBSearchModule gbS = (GBSearchModule)dbS; int white, hole; // 1. Five foreach (GoBangBoard.StoneSet ss in GB.G5) { GBOperator.CountStones(ss, out white, out hole); if (white == 5 && hole == 0) { /*Console.WriteLine (GB); * Console.WriteLine ("=> goal because of five");*/ isGoal = true; return(true); } } // 2. Straight four // FIXME/TODO: check if this is right, i mean the check here if (gbS.maximumCategory >= 1) { foreach (GoBangBoard.StoneSet ss in GB.G6) { if (ss.stones[0] != 0 || ss.stones[5] != 0) { continue; } GBOperator.CountStones(ss, out white, out hole); if (white == 4 && hole == 2) { /*Console.WriteLine (GB); * Console.WriteLine ("goal because of four");*/ isGoal = true; return(true); } } } // Check if there are extra fixed goal squares. if (gbS.goalIfOccupied == null) { isGoal = false; return(false); } // There extra squares, check if they are occupied by the defender // (-1, but in the flipped field its 1) in this state. for (int n = 0; n < gbS.goalIfOccupied.GetLength(0); ++n) { if (gb.board[gbS.goalIfOccupied[n, 1], gbS.goalIfOccupied[n, 0]] == 1) { /*Console.WriteLine (GB); * Console.WriteLine ("goal because occupation of ({0},{1})", * gbS.goalIfOccupied[n,0], gbS.goalIfOccupied[n,1]);*/ isGoal = true; return(true); } } // Neither a necessary field has been occupied, nor a five/four been isGoal = false; return(false); }
// 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 bool Applicable(IDBOperator oper, GBSpaceState state) { GBOperator goper = (GBOperator)oper; return(goper.Applicable(state)); }
public GBSpaceState(GoBangBoard gb, GBOperator lastOperator, GBSpaceState lastState, int maxCat) : this(gb, lastOperator, lastState, null) { this.legalOperators = GBOperator.LegalOperators (this, maxCat); }