需求簡單分析。玩過連連看的都知道,連連看其實就是測試能不能用少於等於3條相連的線,連接兩個點(圖片)。線的條數為0~3條。 先只實現了邏輯,並做了智能測試,程序自己可以演示玩連連看的過程,界面還沒有做,准備用QT(不熟),源碼下載 0條線表示兩個點相鄰,在坐標軸上看就是X軸相同,Y軸值相差1,或是Y軸相同X軸值相差1。 1條線表示兩個點同X或是同Y軸,且兩點之間沒有其他點。 2條線表示兩個點確定的長方形,存在連接兩個點的兩邊上都沒有其他任何點 3條線復雜點,經過反復的思考,決定將3條線的情況分成5類:上下左右中(東南西北中)。這樣的分類主要是想讓代碼更好理解,思路看起來更簡單點。 上:連接兩個點的3條線的第二條線在兩個點的上面 下: 下 左: 左 右: 右 中:連接兩個點的線在這兩個點確定的長方體內(包括長方體的邊)
所有的情況都包括在0~3條線的情況中,實現這三種情況,連連看的最主要功能就算是實現了。在實現方面用得最多的就是判斷兩個點是否在同一條直線上,且直線上沒有其他點,兩條線和三條線的情況都可以轉化成兩組或是三組兩個點是否在同一條直線上的問題。 在實現上,我用了一個二維數組A[row][column]來標示某個點上是否有圖片,且這兩個點是否是同一種圖片,0表示有沒有圖片,其他值表示有圖片,如果兩個點的值相同表示是相同的圖片,讓後我們就至於要測試這兩個點是否想通了。所有的實現都是基於這個二維數組A的。在這一版裡,我主要是實現了所有的邏輯,沒有做界面,界面准備用QT實現,但由於我跟QT還不熟,所以還得等一段時間,先實現了大致的邏輯,並模擬了連連看的過程,模擬的過程發現了一些錯誤,但最後也證明我的實現是正確的,也沒有任何性能問題,當然也跟連連看的特殊有關,因為連連看的最主要的功能其實就是測試兩個點是否能連接成功,而判斷指令對於計算機來說執行是非常快的。 拐點:最多有3條線,最多也就兩個拐點,知道了端點及拐點,我就知道連接兩個點的線了。 判斷是否成功:開始的時候記下點的個數,沒成功連接兩個點,點的個數就減2,點的剩余個數為0時就是成功了,當然也可以每次都掃描一次看剩余的點數是否大於0,個人認為用一個變量記下剩余的點的個數會快些。 判斷是否無解:如果沒有兩個點能夠相連,就表示無解了。實現這個功能同樣有兩種方式。方法一是每次判斷是否無解時都掃描一次,看是否存在兩個點能夠相連,存在表示有解,否則表示無解。方法二是完整的掃描一次,記下能夠相連的點對數,沒成功相連一次,就讓這個數減一,當然不一定是減一,可能是減二,也可能是增一增二,至於為什麼是這樣大家應該知道,在這裡我采取的是減一,如果相連的點的對數小於1時就在掃描一次,我們就得到了剩余能夠相連的點對的個數,如果還是小於1,表示無解。 實現的簡單解釋 Point表示坐標軸上的一個點,所有的實現都是基於點的概念,用戶點擊的圖片或者說是按鈕都表示一個點,而一個點就是兩個坐標X軸和Y軸,並附帶了一些其他的輔助方法。 #pragma once //表示坐標軸上的一個點[X,Y] struct Point { Point():X(-1),Y(-1){} Point(int _x,int _y):X(_x),Y(_y){} Point(const Point& p):X(p.X),Y(p.Y){} Point& operator=(const Point& p){ X=p.X; Y=p.Y; return *this; } inline bool operator==(const Point& p) { return X==p.X && Y==p.Y; } inline bool operator==(Point& p) { return X==p.X && Y==p.Y; } inline bool operator!=(const Point& p) { return X!=p.X || Y!=p.Y; } inline bool operator!=(Point& p) { return X!=p.X || Y!=p.Y; } int X;//X軸 int Y;//Y軸 }; TwoPoint其實是對兩個點的一層簡單的包裝。用戶需要點擊兩個點,我就用這個類來包裝這兩個點,連接兩個點,最多可能出現2兩個拐點(在需要三條線連接兩個點的時候),我們還是可以用TwoPoint來包裝這兩個拐點,並附帶了一些其他的輔助方法。 #pragma once //用於記錄用戶點擊了哪兩個點 class TwoPoint { public: TwoPoint(); ~TwoPoint(); //添加點 bool AddPoint(const Point& p); //點的個數 int Count() const; Point First() const; void First(const Point& p); Point Second() const; void Sort(); void Clear(); private: TwoPoint(const TwoPoint& p); TwoPoint& operator=(const TwoPoint& p); Point* first; Point* second; int count; }; PathRecord將用戶點擊的兩個點擊拐點,按順序連接起來經過的所有的點集合,開始在做的時候主要是為了測試用的。 #pragma once //存放連線經過的所有點 class PathRecord { public: //清除集合中所有的元素 void Clear(); void AddPoint(const Point& p);//添加一個點 void AddPointLine(const Point& first,const Point& second);//添加兩個點確定的直線上所有的點 void AddPoint(const Point& first,const Point& center1,const Point& second); void AddPoint(const Point& first,const Point& center1,const Point& center2,const Point& second); ~PathRecord(); Point* operator[](int index); int Size(); private: vector<Point*> pointVector; }; PathFind主要是測試兩個點是否能成功連接,練練的核心實現就是他了。 #pragma once extern DType A[row][column]; //尋路,主要的邏輯實現 class PathFind { public: PathFind(); ~PathFind(); bool Left(const Point& first,const Point& second); bool Right(const Point& first,const Point& second); bool Top(const Point& first,const Point& second); bool Bottom(const Point& first,const Point& second); bool Center(const Point& first,const Point& second); bool OneLine(TwoPoint& endPoint); bool OneLine(const Point& first,const Point& second); bool MoreLine(TwoPoint& endPoint); bool MoreLine(const Point& first,const Point& second); bool Near(TwoPoint& endPoint); bool Near(const Point& first,const Point& second); bool Search(TwoPoint& endPoint); bool Search(const Point& first,const Point& second); private: //判斷兩個點是否在一條豎線上即同Y bool SameY(const Point& first,const Point& second); //判斷兩次點擊是否在一條橫線上即同X bool SameX(const Point& first,const Point& second); }; CheckResult判斷是否成功或是無解。 #pragma once //結果判定-判定是死局或是已通關 class CheckResult { public: CheckResult(); CheckResult(int _leftPoint); int LeftPoint() const;//返回剩下的點 int LeftLinkLine();//可連接的點對數 void SearchLeftLinkLine();//查詢可連接的點對數 void SearchLeftPoint();//查詢剩下的點個數 bool Reduce2Point();//減少兩個剩余的點 bool IsSuccess();//判斷是否成功 bool IsNoSolution();//是否是無解 private: int leftPoint;//剩下的點用於判斷是否結束 volatile int leftLinkLine;//可連接的點對數 用於判斷是否死局 }; IntelligentTest智能測試,就是讓電腦來玩連連看,事實證明電腦玩得很快啊,主要是用來測試的,經過他的測試就知道程序實現是否有問題了。這也是很關鍵的一個環節,測試發現了一些問題,這也是我少有的寫測試相關的代碼,個人認為自己寫測試代碼比手動測試效果好很多啊。 #pragma once //智能測試 class IntelligentTest { public: IntelligentTest(); void Remove(const Point& p);//刪除已經連接成功的點 Point GetFirstPoint();//獲取第一個點 Point GetSecondPoint();//獲取第二個點 void AddHasReadPoint(const Point& p);//將已經檢索過得點(暫時沒有找到跟他匹配的點)加入hasReadList中 private: void InitDataBind();//將所有非空的點的坐標(某個值)加入集合noReadList中 int firstIndex;//第一個點在集合中的索引 int secondIndex;//第二個點在集合中的索引 vector<int> hasReadList;//存放已經讀了的點信息 vector<int> noReadList;//存放剩下的點信息 }; 開始圖形及運行時間如下:智能測試,程序自動玩連連看步驟如下: