private bool GetMoveNormal() { Console.WriteLine("GetMoveNormal"); Debug.Assert(threatsequence == null); // If only one field, skip sequence search ArrayList intFields = fieldagent.ReallyInterestingFields(board, 1); if (intFields.Count > 1) { // Search for winning threat sequence threatsequence = FindWinning(board); lastAppliedThreatSequence = null; if (threatsequence != null && threatsequence.attacker == null) { threatsequence = null; } if (threatsequence != null) { state = MoveState.Attack; statusStr.Append("ATTACK!, "); return(true); } } // No threat sequence for us found, so lets proceed bestfield = DoABMove(); return(false); }
private static int[,] ExtraGoalSquares(GBThreatSequence seq) { ArrayList squares = new ArrayList(); // Add only the defender moves for the moves just made foreach (GBMove defMove in seq.defender) { squares.Add(new int[] { defMove.X, defMove.Y }); } // Add everything below. seq = seq.next; while (seq != null) { squares.Add(new int[] { seq.attacker.X, seq.attacker.Y }); foreach (GBMove defMove in seq.defender) { squares.Add(new int[] { defMove.X, defMove.Y }); } seq = seq.next; } int[,] sqA = new int[squares.Count, 2]; for (int n = 0; n < squares.Count; ++n) { sqA[n, 0] = ((int[])squares[n])[0]; sqA[n, 1] = ((int[])squares[n])[1]; } return(sqA); }
public override string ToString() { StringBuilder sb = new StringBuilder(); GBThreatSequence seq = this; sb.Append("\n"); sb.Append("Sequence:\n"); while (seq != null) { if (seq.attacker != null) { sb.AppendFormat(" move: {0}, threat class {1}\n", seq.attacker, seq.attackerThreatClass); foreach (GBMove reply in seq.defender) { sb.AppendFormat(" def-reply: {0}\n", reply); } } else { sb.AppendFormat(" already GOAL"); } seq = seq.next; } return(sb.ToString()); }
/** Try to find a winning threat sequence for the opponent. * * @param board The current game board to be checked. * @param dbDefenseTimeLimit The limit in milliseconds to be allowed for * the search. * * @returns The winning threat sequence found null if none has been found. */ private GBThreatSequence GetOpponentSequence(int[,] board, int dbDefenseTimeLimit) { int[,] boardRev = new int[board.GetLength(1), board.GetLength(0)]; for (int y = 0; y < boardRev.GetLength(0); ++y) { for (int x = 0; x < boardRev.GetLength(1); ++x) { // Reverse, so we check for "us", where us is, in fact, the // opponent. boardRev[y, x] = -board[x, y]; } } Console.WriteLine("Searching for opponent attackability of future board:"); Console.WriteLine(new GoBangBoard(boardRev)); GBThreatSequence seq = GBThreatSearch.FindWinning(boardRev, dbDefenseTimeLimit * 1000, true); if (seq != null) { Console.WriteLine("Opponent sequence: {0}", seq); } return(seq); }
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 }
// TODO: docs public ArrayList ThreatDefensiveInterestingFields(int[,] board, int dbDefenseTimeLimit) { GBThreatSequence seq = GetOpponentSequence(board, dbDefenseTimeLimit); if (seq == null) { return(null); } // Only sequences which have defending moves (that is, category 1 and // above) do count. The special case STRAIGHT FOUR is a GOAL and does // not have any defending moves. if (seq.defender == null || seq.defender.Length == 0) { return(null); } ArrayList defFields = new ArrayList(); // Add the attack and the defending moves of the first sequence ply to // the "defending interesting fields". defFields.Add(new Coordinate(seq.attacker.X, seq.attacker.Y)); if (seq.defender != null) { foreach (GBMove gbm in seq.defender) { defFields.Add(new Coordinate(gbm.X, gbm.Y)); } } // Now only keep the most attacking ones, that is the ones that // creates the highest number of distinct operators. ArrayList fields = GetDefensePriorityFields(board, defFields); Console.WriteLine("ThreatDefensiveInterestingFields:"); Console.Write(" defFields: "); foreach (Coordinate c in defFields) { Console.Write("{0}, ", c); } Console.WriteLine(); Console.Write(" to max filtered: "); foreach (Coordinate c in fields) { Console.Write("{0}, ", c); } Console.WriteLine(); return(fields); }
// 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); }
public static GBThreatSequence[] BuildAllGoalPathes(GBSearchModule module, DBNode root) { ArrayList goalpathes = GoalPathes(module, root); Console.WriteLine("I have {0} goal pathes", goalpathes.Count); // Create the threat sequences. ArrayList threatSeqs = new ArrayList(); // Build one individual threat sequence for each goalpath. foreach (ArrayList gp in goalpathes) { GBThreatSequence ts = BuildThreatSequence(gp); threatSeqs.Add(ts); } // DEBUG output Console.WriteLine("{0} threat seqs lists", threatSeqs.Count); int seqNo = 1; foreach (GBThreatSequence seqW in threatSeqs) { GBThreatSequence seq = seqW; Console.WriteLine(); Console.WriteLine(((GBSpaceState)root.State).GB); Console.WriteLine(); Console.WriteLine("Sequence {0}: ", seqNo); int m = 0; while (seq != null) { Console.WriteLine(" move{0}: {1}, threat class {2}", m, seq.attacker, seq.attackerThreatClass); m += 1; foreach (GBMove reply in seq.defender) { Console.WriteLine(" def-reply{0}: {1}", m, reply); m += 1; } seq = seq.next; } seqNo += 1; } return((GBThreatSequence[]) threatSeqs.ToArray(typeof(GBThreatSequence))); }
/** 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); }
// @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); }
// timeoutMS: zero or timeout limit in ms public static GBThreatSequence FindWinning(int[,] board, int timeoutMS, bool defOpponentSearch) { GBThreatSearch gts = new GBThreatSearch(new GoBangBoard(board), defOpponentSearch); GBThreatSequence seq = gts.FindWinningThreatSeqOTF(timeoutMS); if (seq != null) { Console.WriteLine("Board:"); Console.WriteLine(new GoBangBoard(board)); Console.WriteLine(); Console.WriteLine("Winning threat sequence found:"); Console.WriteLine(seq); } else { Console.WriteLine("No winning threat sequence found."); } return(seq); }
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 bool GetMoveAttack() { Console.WriteLine("GetMoveAttack"); Debug.Assert(threatsequence != null); // Check the threat sequence is still valid on the current board. // This is the rare magic case that could defeat us (C-3, C-4) if (threatsequence.CheckValidRevBoard(board) == false) { statusStr.Append("magic :-(, "); Console.WriteLine("Winning threat sequence not valid anymore, magic case."); threatsequence = null; state = MoveState.Normal; return(true); } // Also check if there is still attacker moves left if (threatsequence.attacker == null) { threatsequence = null; statusStr.Append("seqEnd reached"); state = MoveState.Normal; return(true); } // Check the last move of the opponent is one of the expected defender // moves in our threat sequence. If it is not, fall back on // alpha-beta search. if (lastAppliedThreatSequence != null && lastAppliedThreatSequence.defender != null) { bool defendedThreat = false; foreach (GBMove gbm in lastAppliedThreatSequence.defender) { if (gbm.X == lastoppmove.X && gbm.Y == lastoppmove.Y) { defendedThreat = true; break; } } // If the threat was not defended against, fall back to alpha-beta if (defendedThreat == false) { statusStr.Append("not defended, "); threatsequence = null; state = MoveState.Normal; return(true); } statusStr.Append("defended, "); } // Check if the move is in the interesting fields, which basically // asserts us there is no opponent threat we have to respond to. ArrayList fields = fieldagent.ReallyInterestingFields(board, 1); Coordinate wantmove = new Coordinate(threatsequence.attacker.X, threatsequence.attacker.Y); if (fields.Contains(wantmove)) { Console.WriteLine("Move is chosen from sequence"); bestfield = wantmove; statusStr.Append("move ({0},{1}) from seq, ", wantmove.X, wantmove.Y); sequence_used = true; lastAppliedThreatSequence = threatsequence; // Advance to next threat threatsequence = threatsequence.next; // End of sequence -> fall back to alpha beta, for the _next_ // move. if (threatsequence == null) { state = MoveState.Normal; } } else { Console.WriteLine("Move from sequence is not in interesting fields"); sequence_used = false; statusStr.Append("move not from seq, "); bestfield = DoABMove(); } // Move made return(false); }
/** Check for refutability. * * @param seq The potential winning threat sequence to be checked. * @param curBoard The current board against which the sequence shall be * checked. * * @returns Negative value if the sequence is not refutable, otherwise the * value returned is the refutation depth. */ private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard) { return (DefenseRefutes (seq, curBoard, 0)); }
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[,] ExtraGoalSquares(GBThreatSequence seq) { ArrayList squares = new ArrayList (); // Add only the defender moves for the moves just made foreach (GBMove defMove in seq.defender) squares.Add (new int[] { defMove.X, defMove.Y }); // Add everything below. seq = seq.next; while (seq != null) { squares.Add (new int[] { seq.attacker.X, seq.attacker.Y }); foreach (GBMove defMove in seq.defender) squares.Add (new int[] { defMove.X, defMove.Y }); seq = seq.next; } int[,] sqA = new int[squares.Count, 2]; for (int n = 0 ; n < squares.Count ; ++n) { sqA[n,0] = ((int[]) squares[n])[0]; sqA[n,1] = ((int[]) squares[n])[1]; } return (sqA); }
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)); }
/** Check for refutability. * * @param seq The potential winning threat sequence to be checked. * @param curBoard The current board against which the sequence shall be * checked. * * @returns Negative value if the sequence is not refutable, otherwise the * value returned is the refutation depth. */ private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard) { return(DefenseRefutes(seq, curBoard, 0)); }
public GBWinningThreatSequenceFoundException(GBThreatSequence seq) { this.seq = seq; }
// @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); }
private bool GetMoveNormal() { Console.WriteLine ("GetMoveNormal"); Debug.Assert (threatsequence == null); // If only one field, skip sequence search ArrayList intFields = fieldagent.ReallyInterestingFields (board, 1); if (intFields.Count > 1) { // Search for winning threat sequence threatsequence = FindWinning(board); lastAppliedThreatSequence = null; if (threatsequence != null && threatsequence.attacker == null) threatsequence = null; if (threatsequence != null) { state = MoveState.Attack; statusStr.Append ("ATTACK!, "); return (true); } } // No threat sequence for us found, so lets proceed bestfield = DoABMove (); return (false); }
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 static void Main(string[] args) { 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; */ /* * // Testcase A * gb.board[4,9] = gb.board[5,6] = gb.board[6,8] = 1; * gb.board[7,7] = gb.board[8,6] = gb.board[9,5] = gb.board[9,6] = 1; * gb.board[9,9] = gb.board[10,11] = 1; * * gb.board[5,9] = gb.board[5,10] = -1; * gb.board[6,7] = gb.board[6,9] = -1; * gb.board[7,8] = gb.board[7,9] = gb.board[8,9] = -1; * gb.board[9,10] = gb.board[10,4] = -1; */ /* * // Testcase B * gb.board[5,5] = gb.board[4,6] = gb.board[7,5] = gb.board[7,6] = 1; * gb.board[5,6] = gb.board[6,6] = -1; */ /* * gb.board[5,6] = gb.board[8,6] = gb.board[9,6] = 1; */ /* * // FIXME: this sequence is not found as un-refutable, while it is in * // fact (magic case, we know exactly what it is caused by and this is * // unlikely to appear in a real game and difficult to fix (though * // possible)) * // 138, figure 5.5 * gb.board[0,0] = gb.board[1,1] = gb.board[2,2] = gb.board[5,13] = -1; * gb.board[6,9] = gb.board[7,7] = gb.board[7,8] = -1; * gb.board[8,6] = gb.board[8,12] = -1; * gb.board[10,7] = gb.board[10,9] = -1; * gb.board[10,2] = gb.board[11,1] = gb.board[12,0] = -1; * * gb.board[2,12] = gb.board[3,12] = 1; * gb.board[6,6] = gb.board[6,7] = gb.board[7,6] = gb.board[7,10] = 1; * gb.board[8,8] = gb.board[8,11] = gb.board[9,8] = 1; */ // 127b gb.board[5, 6] = gb.board[6, 9] = gb.board[7, 7] = gb.board[8, 8] = 1; gb.board[8, 9] = 1; gb.board[6, 6] = gb.board[6, 8] = gb.board[7, 6] = gb.board[7, 10] = -1; gb.board[8, 6] = -1; /* * // 127a * 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; */ Console.WriteLine("Starting winning threat sequence search..."); GBThreatSearch gts = new GBThreatSearch(gb, false); GBThreatSequence seq = gts.FindWinningThreatSeqOTF(); if (seq != null) { Console.WriteLine("Board:"); Console.WriteLine(gb); Console.WriteLine(); Console.WriteLine("winning threat sequence found:"); Console.WriteLine(seq); } GBThreatSearch gts2 = new GBThreatSearch(gb, true); GBThreatSequence seq2 = gts2.FindWinningThreatSeqOTF(); if (seq2 != null) { Console.WriteLine("Board:"); Console.WriteLine(gb); Console.WriteLine(); Console.WriteLine("winning threat sequence found:"); Console.WriteLine(seq2); } }
private bool GetMoveAttack() { Console.WriteLine ("GetMoveAttack"); Debug.Assert (threatsequence != null); // Check the threat sequence is still valid on the current board. // This is the rare magic case that could defeat us (C-3, C-4) if (threatsequence.CheckValidRevBoard (board) == false) { statusStr.Append ("magic :-(, "); Console.WriteLine ("Winning threat sequence not valid anymore, magic case."); threatsequence = null; state = MoveState.Normal; return (true); } // Also check if there is still attacker moves left if (threatsequence.attacker == null) { threatsequence = null; statusStr.Append ("seqEnd reached"); state = MoveState.Normal; return (true); } // Check the last move of the opponent is one of the expected defender // moves in our threat sequence. If it is not, fall back on // alpha-beta search. if (lastAppliedThreatSequence != null && lastAppliedThreatSequence.defender != null) { bool defendedThreat = false; foreach (GBMove gbm in lastAppliedThreatSequence.defender) { if (gbm.X == lastoppmove.X && gbm.Y == lastoppmove.Y) { defendedThreat = true; break; } } // If the threat was not defended against, fall back to alpha-beta if (defendedThreat == false) { statusStr.Append ("not defended, "); threatsequence = null; state = MoveState.Normal; return (true); } statusStr.Append ("defended, "); } // Check if the move is in the interesting fields, which basically // asserts us there is no opponent threat we have to respond to. ArrayList fields = fieldagent.ReallyInterestingFields(board, 1); Coordinate wantmove = new Coordinate (threatsequence.attacker.X, threatsequence.attacker.Y); if (fields.Contains (wantmove)) { Console.WriteLine("Move is chosen from sequence"); bestfield = wantmove; statusStr.Append ("move ({0},{1}) from seq, ", wantmove.X, wantmove.Y); sequence_used = true; lastAppliedThreatSequence = threatsequence; // Advance to next threat threatsequence = threatsequence.next; // End of sequence -> fall back to alpha beta, for the _next_ // move. if (threatsequence == null) state = MoveState.Normal; } else { Console.WriteLine("Move from sequence is not in interesting fields"); sequence_used = false; statusStr.Append ("move not from seq, "); bestfield = DoABMove (); } // Move made return (false); }