Exemple #1
0
 private void Search_clicked(object sender, RoutedEventArgs e)
 {
     DataContext       = new DBSearch(conn);
     Badd.IsEnabled    = true;
     Bsearch.IsEnabled = false;
     Bstats.IsEnabled  = true;
 }
Exemple #2
0
 public GBOperatorCombineFastCheck(DBSearch dbS, GBSearchModule gbS,
                                   ArrayList thisStageDependencies, int level)
 {
     this.dbS = dbS;
     this.gbS = gbS;
     this.thisStageDependencies = thisStageDependencies;
     this.level = level;
 }
    public GBOperatorCombineFastCheck(DBSearch dbS, GBSearchModule gbS,
		ArrayList thisStageDependencies, int level)
    {
        this.dbS = dbS;
        this.gbS = gbS;
        this.thisStageDependencies = thisStageDependencies;
        this.level = level;
    }
    public void CombinationStage(int level, DBNode root, DBSearch dbs)
    {
#if false
        ArrayList thisDepsStates = new ArrayList();
        CombinationStageCompileDeps(level, root, thisDepsStates);

        GBOperatorCombineFastCheck cfc =
            new GBOperatorCombineFastCheck(dbs, this, thisDepsStates, level);

        cfc.CombineCheck(nodesThatAffectSquares);
#endif
    }
    // Global function
    public GBThreatSequence FindWinningThreatSeq()
    {
        // First find a number of possibly winning threat trees.
        GBSearchModule gbSearch  = new GBSearchModule(GoBangBoard.boardDim);
        GBSpaceState   rootState = new GBSpaceState((GoBangBoard)gb.Clone());

        rootState.UpdateIsGoal(gbSearch);

        // HEURISTIC: use category reduction (page 140-141)
        gbSearch.categoryReductionHeuristicOn = true;

        DBSearch db = new DBSearch(gbSearch, breadthFirst);

        db.Search(rootState);
        //db.DumpDOTGoalsOnly ();

        // Now, enumerate all the possibly winning threat trees found
        GBThreatSequence[] potentialWinningSeqs =
            GBThreatSequence.BuildAllGoalPathes(gbSearch, db.Root);
        Console.WriteLine("{0} potential winning threat sequences.",
                          potentialWinningSeqs.Length);

        // Check them one by one until a surely winning threat tree is found
        GoBangBoard gbFlipped = (GoBangBoard)gb.Clone();

        gbFlipped.Flip();

        int DEBUGwinningFound         = 0;
        GBThreatSequence DEBUGwinning = null;

        foreach (GBThreatSequence threatSeq in potentialWinningSeqs)
        {
            if (DefenseRefutes(threatSeq,
                               (GoBangBoard)gbFlipped.Clone()) < 0)
            {
                // Found a sure win, return early
                // FIXME: for debugging we count all winning sequences found,
                // but we should return as early as possible.
                DEBUGwinningFound += 1;
                DEBUGwinning       = threatSeq;
                //Console.WriteLine ("WINNING:\n{0}", threatSeq);
                // FIXME
                //return (threatSeq);
            }
        }

        Console.WriteLine("{0} winning of {1} potential winning threat sequences identified",
                          DEBUGwinningFound, potentialWinningSeqs.Length);

        // Found no unrefuted threat sequence
        return(DEBUGwinning);
    }
    /** Try to find a winning threat sequence by dependency based search and
     * on the fly refutation for goal nodes.
     *
     * Return early, as soon as a sure candidate has been found.
     *
     * @param timeoutMS If non-zero, a maximum time spend in db-search is
     * given in milliseconds.  At least 1000 (1s) is meaningful to do
     * something, though.
     *
     * @returns The first winning threat sequence on success, null otherwise.
     */
    public GBThreatSequence FindWinningThreatSeqOTF(int timeoutMS)
    {
        // First find a number of possibly winning threat trees.
        GBSearchModule gbSearch  = new GBSearchModule(GoBangBoard.boardDim);
        GBSpaceState   rootState = new GBSpaceState((GoBangBoard)gb.Clone());

        rootState.UpdateIsGoal(gbSearch);

        // HEURISTIC: use category reduction (page 140-141)
        // FIXME: re-enable as soon as three3 bug is fixed.
        // FIXME: test if this is good in the real ai, otherwise disable again.
        //gbSearch.categoryReductionHeuristicOn = true;

        // Do on-the-fly refutation checking.
        gbSearch.doDefenseRefutationCheck = true;

        if (timeoutMS != 0)
        {
            gbSearch.doExpirationCheck = true;
            gbSearch.expireTime        = DateTime.Now.AddMilliseconds(timeoutMS);
        }

        DBSearch db = new DBSearch(gbSearch, breadthFirst);

        try {
            Console.WriteLine("Board:\n{0}\n", gb);
            db.Search(rootState);
            //db.DumpDOT ();
        } catch (GBSearchModule.GBSearchTimeoutException) {
            // We timed out...
            Console.WriteLine("FindWinningThreatSeqOTF: timeouted...");
        } catch (GBWinningThreatSequenceFoundException gex) {
            //db.DumpDOT ();
            return(gex.seq);
        }

        return(null);
    }
    /** Test 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);
         * }
         */
    }
Exemple #8
0
        public IHttpActionResult POST()
        {
            try
            {
                #region 取得一些基本資料
                //取得 http Post RawData(should be JSON)
                string postData = Request.Content.ReadAsStringAsync().Result;
                //剖析JSON
                var ReceivedMessage = isRock.LineBot.Utility.Parsing(postData);
                //取得訊息的種類
                var Type = ReceivedMessage.events[0].type;
                //要回覆的訊息內容
                string Message = "";
                //交通部API
                PTXApi getAPIData = new PTXApi();

                //取得 UID or GID
                //狀況1.單獨與機器人交談的使用者,talkToken及senderToken都取userid,因為機器人只會回覆該使用者
                //狀況2.在群組呼叫機器人,且加了機器人好友,talkToken取userid以利資料庫存取,sendeToken取groupid,因使用狀況是在群組,故應於群組回覆
                //狀況3.在群組呼叫機器人,但沒有加機器人好友,talkToken及senderToken都取groupid,因為機器人只看得到groupid,使用者也不會和機器人私下對談
                string talkToken   = "";
                string senderToken = "";
                if (ReceivedMessage.events[0].source.userId == null && ReceivedMessage.events[0].source.groupId != null)
                {
                    talkToken   = ReceivedMessage.events[0].source.groupId.ToString();
                    senderToken = ReceivedMessage.events[0].source.groupId.ToString();
                }
                else if (ReceivedMessage.events[0].source.userId != null && ReceivedMessage.events[0].source.groupId == null)
                {
                    talkToken   = ReceivedMessage.events[0].source.userId.ToString();
                    senderToken = ReceivedMessage.events[0].source.userId.ToString();
                }
                else
                {
                    talkToken   = ReceivedMessage.events[0].source.userId.ToString();
                    senderToken = ReceivedMessage.events[0].source.groupId.ToString();
                }
                #endregion

                #region 判斷使用者發送的文字訊息

                if (ReceivedMessage.events[0].message.text != null)
                {
                    //狀況1 取到完整的句子
                    switch (ReceivedMessage.events[0].message.text)
                    {
                    case "sendImageMap":
                        SendRequest.SendImageMap(senderToken);
                        break;

                    case "給我匿名網址":
                        Message = "講話不用負責任,網址給你 : https://linebotfortest.azurewebsites.net/home/index/" + senderToken;
                        break;

                    case "車子停哪":

                        DBSearch dbprocess = new DBSearch();
                        Dictionary <string, string> getParkingData = dbprocess.returnParkingData(talkToken);

                        if (getParkingData != null)
                        {
                            SendRequest.SendLocation(senderToken, getParkingData["parkingLat"], getParkingData["parkingLon"], "你車停在這啦!", "可能因天氣地形因素影響而有出入");
                        }
                        else
                        {
                            Message = "你沒有紀錄停車位置!";
                        }
                        break;
                    }
                    //狀況二,針對字串取重點字
                    if (ReceivedMessage.events[0].message.text.Length > 8 && (ReceivedMessage.events[0].message.text.Substring(0, 8) == "幫我google" || ReceivedMessage.events[0].message.text.Substring(0, 8) == "幫我GOOGLE" || ReceivedMessage.events[0].message.text.Substring(0, 8) == "幫我Google"))
                    {
                        Message = "我來幫你google:\n https://www.google.com.tw/search?hl=zh-TW&q=" + ReceivedMessage.events[0].message.text.Substring(9).Replace(" ", "%20");
                    }

                    if (ReceivedMessage.events[0].message.text.Length > 3 && ReceivedMessage.events[0].message.text.Substring(0, 3) == "我想去")
                    {
                        DBSearch dbprocess = new DBSearch();
                        Dictionary <string, string> getLoactionData = dbprocess.returnLocationData(talkToken);

                        if (getLoactionData != null)
                        {
                            string googleUrl = "https://www.google.com.tw/maps/dir/" + Uri.EscapeDataString(getLoactionData["locationLat"] + "," + getLoactionData["locationLon"]) + "/" + Uri.EscapeDataString(ReceivedMessage.events[0].message.text.Substring(3));

                            SendRequest.sendButtonTemplate(senderToken,
                                                           new List <isRock.LineBot.TemplateActionBase>()
                            {
                                new isRock.LineBot.UriAction()
                                {
                                    label = "點這邊開啟GoogleMap導航", uri = new Uri(googleUrl)
                                }
                            },
                                                           "幫您導航", "以下選項協助您抵達目的地", ChannelAccessToken);
                        }
                    }

                    if (ReceivedMessage.events[0].message.text.Length > 7 && ReceivedMessage.events[0].message.text.Substring(0, 7) == "send fb")
                    {
                        SendRequest.sendFacebookMsg(Geocoding.test, ReceivedMessage.events[0].message.text.Substring(8));
                    }
                }
                #endregion

                #region 如果使用者丟圖片 ...

                if (Type == "message" && ReceivedMessage.events[0].message.type.Trim().ToLower() == "image")
                {
                    isRock.LineBot.Bot bot = new isRock.LineBot.Bot(ChannelAccessToken);
                    //取得使用者上傳的圖片
                    var imageBytes = bot.GetUserUploadedContent(ReceivedMessage.events[0].message.id.ToString());
                    //非同步,送給VisionRecognize進行圖像辨識
                    VisionRecognize.MakeAnalysisRequest(imageBytes, senderToken);
                }

                #endregion


                #region CICtest 對話流程測試

                //ConversationEntity
                isRock.LineBot.Conversation.InformationCollector <CICClass> CIC = new isRock.LineBot.Conversation.InformationCollector <CICClass>(ChannelAccessToken);
                //取得流程的結果
                ProcessResult <CICClass> result;

                //篩選對話的資料,如果不符流程問題的格式則退回重問
                CIC.OnMessageTypeCheck += (s, e) =>
                {
                    string regex = @"^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$";
                    Regex  r     = new Regex(regex, RegexOptions.IgnoreCase);

                    switch (e.CurrentPropertyName)
                    {
                    case "Geocoding":
                        if (!r.IsMatch(e.ReceievedMessage))
                        {
                            e.isMismatch      = true;
                            e.ResponseMessage = "只接受座標資訊 ... \n請給我你的座標資訊,或者跟我說『取消』來結束流程 ...";
                        }
                        break;

                    case "process":
                        if (e.ReceievedMessage == "幫我導航" | e.ReceievedMessage == "幫我記錄停車位置" | e.ReceievedMessage == "最近的3個公車站" | e.ReceievedMessage == "最近的3個Youbike站")
                        {
                            e.isMismatch = false;
                        }
                        else
                        {
                            e.isMismatch      = true;
                            e.ResponseMessage = "請點選選單選項,或者說『取消』來結束流程";
                            // e.CurrentPropertyName = "";
                        }
                        break;
                    }
                };
                //輸入Hey!進入程序
                if (ReceivedMessage.events[0].message.text == "Hey!")
                {
                    result  = CIC.Process(ReceivedMessage.events[0], true);
                    Message = "開始程序\n";

                    SendRequest.sendButtonTemplate(senderToken,
                                                   new List <isRock.LineBot.TemplateActionBase>()
                    {
                        new isRock.LineBot.MessageAction()
                        {
                            label = "導航", text = "幫我導航"
                        },
                        new isRock.LineBot.MessageAction()
                        {
                            label = "記錄停車位置", text = "幫我記錄停車位置"
                        },
                        new isRock.LineBot.MessageAction()
                        {
                            label = "公車站資訊", text = "最近的3個公車站"
                        },
                        new isRock.LineBot.MessageAction()
                        {
                            label = "Youbike站資訊", text = "最近的3個Youbike站"
                        }
                    },
                                                   "Hello!", "想做些什麼?", ChannelAccessToken);
                }
                else
                {
                    if (ReceivedMessage.events[0].message.type.Trim().ToLower() != "text")
                    {
                        if (ReceivedMessage.events[0].message.latitude != 0)
                        {
                            ReceivedMessage.events[0].message.text = ReceivedMessage.events[0].message.latitude + "," + ReceivedMessage.events[0].message.longitude;
                            result = CIC.Process(ReceivedMessage.events[0]);
                        }
                        else
                        {
                            ReceivedMessage.events[0].message.text = " ";
                            result = CIC.Process(ReceivedMessage.events[0]);
                        }
                    }
                    else
                    {
                        result = CIC.Process(ReceivedMessage.events[0]);
                    }
                }


                switch (result.ProcessResultStatus)
                {
                case ProcessResultStatus.Processed:
                    Message += result.ResponseMessageCandidate;
                    break;

                case ProcessResultStatus.Done:
                    char[]   delimiterChars = { ',' };
                    string   geocoding      = result.ConversationState.ConversationEntity.Geocoding;
                    string[] latAndLon      = geocoding.Split(delimiterChars);

                    switch (result.ConversationState.ConversationEntity.process)
                    {
                    case "最近的3個Youbike站":
                        Message = getAPIData.returnBikeData(senderToken, latAndLon[0], latAndLon[1]);
                        break;

                    case "最近的3個公車站":
                        Message = getAPIData.returnBusData(senderToken, latAndLon[0], latAndLon[1]);
                        break;

                    case "幫我導航":
                        DBSearch dbprocess = new DBSearch();
                        dbprocess.SaveLocation(talkToken, ReceivedMessage);
                        Message = "想去嗎哪?格式:我想去OOXXOOXX";
                        break;

                    case "幫我記錄停車位置":
                        DBSearch dbparking = new DBSearch();
                        dbparking.testSaveParkingData(talkToken, latAndLon[0], latAndLon[1]);
                        Message = "座標已經記錄完畢!查詢請打『車子停哪』";
                        break;
                    }

                    break;

                case ProcessResultStatus.Pass:
                    //                        Message += "你說啥我看不懂";
                    break;

                case ProcessResultStatus.Exception:
                    Message += result.ResponseMessageCandidate;
                    break;

                case ProcessResultStatus.Break:
                    Message += result.ResponseMessageCandidate;
                    Message += "\n若需要我請再 Hey! 一次,我會出現的";
                    break;

                case ProcessResultStatus.InputDataFitError:
                    Message += "Oops!!\n";
                    Message += result.ResponseMessageCandidate;
                    break;

                default:
                    break;
                }
                #endregion


                if (!string.IsNullOrEmpty(Message))
                {
                    isRock.LineBot.Utility.ReplyMessage(ReceivedMessage.events[0].replyToken, Message, ChannelAccessToken);
                }

                return(Ok());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return(Ok());
            }
        }
    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));
    }
    /** 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);
    }
    // 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 void CombinationStage(int level, DBNode root, DBSearch dbs)
    {
        #if false
        ArrayList thisDepsStates = new ArrayList ();
        CombinationStageCompileDeps (level, root, thisDepsStates);

        GBOperatorCombineFastCheck cfc =
            new GBOperatorCombineFastCheck (dbs, this, thisDepsStates, level);

        cfc.CombineCheck (nodesThatAffectSquares);
        #endif
    }
    /** 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);
        }
        */
    }