这里给链接: 自动拼图 http://pan.baidu.com/s/1sjoNv7f
其实呢, 也没多少技术含量. 当然, 我比较谦虚.
源码
1 #include "Game.h" 2 3 #include <map> 4 5 static const auto PUZZLE_COUNT = 16; 6 static const auto PUZZLE_WIDTH = WINDOW_WIDTH / PUZZLE_COUNT; 7 static const auto PUZZLE_HEIGHT = WINDOW_HEIGHT / PUZZLE_COUNT; 8 9 static const auto RAND_COUNT = 2000; 10 11 inline bool operator <(const D2D1_POINT_2F &pos1, const D2D1_POINT_2F &pos2) 12 { 13 if (pos1.x == pos2.x) 14 { 15 return pos1.y < pos2.y; 16 } 17 return pos1.x < pos2.x; 18 } 19 20 enum EnumDirect { 21 UP, 22 RIGHT, 23 DOWN, 24 LEFT 25 }; 26 27 namespace Utils { 28 inline EnumDirect flipDirect(EnumDirect direct) 29 { 30 return direct == EnumDirect::UP ? EnumDirect::DOWN 31 : direct == EnumDirect::DOWN ? EnumDirect::UP 32 : direct == EnumDirect::LEFT ? EnumDirect::RIGHT 33 : EnumDirect::LEFT; 34 } 35 } 36 37 class Puzzle : public mmc::LImage { 38 public: 39 Puzzle(int x, int y, int width, int height, const std::wstring &filename) : LImage(filename) 40 { 41 _cliprc = D2D1::RectF(x * 1.0f, y * 1.0f, x + width * 1.0f, y + height * 1.0f); 42 } 43 44 ~Puzzle() 45 { 46 47 } 48 49 virtual void doDraw(ID2D1RenderTarget *pRT, float opacity) override 50 { 51 auto pImg = getD2dBitmap(); 52 if (pImg) 53 { 54 pRT->DrawBitmap(pImg, 55 D2D1::RectF(0.0f, 0.0f, PUZZLE_WIDTH * 1.0f, PUZZLE_HEIGHT * 1.0f), 56 opacity, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, _cliprc); 57 } 58 } 59 60 private: 61 D2D1_RECT_F _cliprc; 62 }; 63 64 class Game::impl { 65 public: 66 impl(Game *pGame) : _pGame(pGame) 67 { 68 srand(time(0)); 69 } 70 71 ~impl() 72 { 73 74 } 75 76 void init() 77 { 78 for (auto x = 0; x != PUZZLE_COUNT; ++x) 79 { 80 for (auto y = 0; y != PUZZLE_COUNT; ++y) 81 { 82 auto pos = D2D1::Point2F(x * PUZZLE_WIDTH * 1.0f, y * PUZZLE_HEIGHT * 1.0f); 83 84 auto pChild = new Puzzle((int)pos.x, (int)pos.y, PUZZLE_WIDTH, PUZZLE_HEIGHT, L"res/img.png"); 85 pChild->setParent(_pGame); 86 pChild->setPosition(pos.x, pos.y); 87 88 _posMap.insert(std::make_pair(pos, pChild)); 89 } 90 } 91 92 mmc::LRootWindow::getInstance()->redrawWindow(); 93 94 auto hWnd = mmc::LRootWindow::getInstance()->getWindowHandle(); 95 rand(RAND_COUNT); 96 } 97 98 void rand(u_int count) 99 { 100 for (auto i = 0; i != count; ++i) 101 { 102 auto direct = EnumDirect(::rand() % 4); 103 if (next(direct)) 104 { 105 _directs.push(direct); 106 } 107 } 108 mmc::LRootWindow::getInstance()->redrawWindow(); 109 } 110 111 void next() 112 { 113 static auto isIgnore = [](EnumDirect direct, std::stack<EnumDirect> &directs) { 114 return Utils::flipDirect(direct) == directs.top(); 115 }; 116 117 if (_directs.empty()) 118 { 119 //rand(RAND_COUNT); 120 return; 121 } 122 123 auto isOk = false; 124 auto direct = EnumDirect::UP; 125 126 while (!_directs.empty() && !isOk) 127 { 128 isOk = true; 129 direct = _directs.top(); 130 _directs.pop(); 131 132 if (!_directs.empty() && isIgnore(direct, _directs)) 133 { 134 _directs.pop(); 135 isOk = false; 136 } 137 138 if (_directs.size() > 1 && isOk) 139 { 140 auto direct2 = _directs.top(); 141 _directs.pop(); 142 if (isIgnore(direct2, _directs)) 143 { 144 _directs.pop(); 145 _directs.push(direct); 146 isOk = false; 147 } 148 else 149 { 150 _directs.push(direct2); 151 } 152 } 153 } 154 155 if (isOk) 156 { 157 next(Utils::flipDirect(direct)); 158 mmc::LRootWindow::getInstance()->redrawWindow(); 159 } 160 } 161 162 void doDraw(ID2D1RenderTarget *pRT, float opacity) 163 { 164 165 } 166 167 void doInput(UINT uMsg, WPARAM wParam, LPARAM lParam) 168 { 169 switch (uMsg) 170 { 171 case WM_CREATE: 172 { 173 init(); 174 } 175 break; 176 case WM_TIMER: 177 { 178 if (wParam == 1001) 179 { 180 next(); 181 mmc::LRootWindow::getInstance()->redrawWindow(); 182 } 183 } 184 break; 185 case WM_KEYUP: 186 { 187 if (wParam == VK_RETURN) 188 { 189 SetTimer(mmc::LRootWindow::getInstance()->getWindowHandle(), 1001, 16, nullptr); 190 } 191 } 192 break; 193 } 194 } 195 private: 196 bool next(EnumDirect direct) 197 { 198 auto pGap = getGap(); 199 auto pMoveObj = find(direct); 200 if (pMoveObj != nullptr) 201 { 202 swap(pMoveObj); 203 } 204 return pMoveObj != nullptr; 205 } 206 207 mmc::LNode *find(EnumDirect direct) 208 { 209 auto pGap = getGap(); 210 auto pos = pGap->getPosition(); 211 switch (direct) 212 { 213 case EnumDirect::UP: 214 pos.y += PUZZLE_HEIGHT; 215 break; 216 case EnumDirect::RIGHT: 217 pos.x -= PUZZLE_WIDTH; 218 break; 219 case EnumDirect::DOWN: 220 pos.y -= PUZZLE_HEIGHT; 221 break; 222 case EnumDirect::LEFT: 223 pos.x += PUZZLE_WIDTH; 224 break; 225 } 226 auto iter = _posMap.find(pos); 227 return iter != std::end(_posMap) ? iter->second : nullptr; 228 } 229 230 mmc::LNode *getGap() 231 { 232 return _pGame->getChilds().back(); 233 } 234 235 void swap(mmc::LNode *pNode) 236 { 237 auto pGap = getGap(); 238 auto pos1 = pGap->getPosition(); 239 auto pos2 = pNode->getPosition(); 240 pNode->setPosition(pos1.x, pos1.y); 241 pGap->setPosition(pos2.x, pos2.y); 242 243 _posMap.at(pos1) = pNode; 244 _posMap.at(pos2) = pGap; 245 } 246 247 std::stack<EnumDirect> _directs; 248 std::map<D2D1_POINT_2F, mmc::LNode*> _posMap; 249 Game *_pGame; 250 }; 251 252 Game::Game() : _pimpl(new impl(this)) 253 { 254 255 } 256 257 Game::~Game() 258 { 259 260 } 261 262 void Game::doDraw(ID2D1RenderTarget *pRT, float opacity) 263 { 264 _pimpl->doDraw(pRT, opacity); 265 } 266 267 void Game::doInput(UINT uMsg, WPARAM wParam, LPARAM lParam) 268 { 269 _pimpl->doInput(uMsg, wParam, lParam); 270 }
这里是最关键的代码, 里面用了一些我封装的dx库. 在我给的连接里有源码. 代码写的潦草, 大伙也不要见笑.
我没有加入用户控制的功能, 因为呢, 我不想加. 呵呵!
咱们说说这个程序的效果, 启动之后按下回车键, 然后眼睁睁看着就好了.
原理是这样的.
把图片切成若干块, 沿着x, y轴对称铺好.
然后将其位置"打乱", rand 这个这个函数实现了打乱的功能, 随机生成方向, 然后图块移动, 并且在移动生效之后记录方向. 重复 count 次结束.
这里有一个槽点, 如果你是一个急性子, 也许你就忽略了. 本作者估计在这里断句, 让你有时间发现槽点.
在打乱的时候保存了方向, 在还原的时候反方向走一遍就OK了. 哈哈哈哈哈, 就是这么简单.
但是, 这是不可能的.
这样做会导致一个看起来很傻逼的效果.
因为打乱是随机生成的, 也就是说极有可能出现生成 up, down, up, down, 这种方向.
那么在还原的时候就出现了, down, up, down, up.. 走了好几步, 位置都没变.
那如何解决这一问题, 参见 next(), 这个next 不带参数, 看仔细..
它的实现过程是:
1, 如果当前的方向跟下一个方向造成冲突, 那就把这2个方向pop掉.
2, 如果当前的方向跟下一个方向不造成冲突, 那就检测下一个方向跟下下一个方向是否造成冲突.
如果你是一个菜鸟, 同时很有想法, 那就跟我学做菜吧. 呵呵!
开个玩笑, 如果你是一个菜鸟, 同时很有想法, 你大概会觉得第二条有些莫名其妙.
如果有 down, left, right, up 这种情况.
down作为当前方向, 跟下一个方向 left 做比较, 明显不会造成冲突, 因此第一条是不成立的, down顺利执行.
left作为当前方向, 跟下一个方向right 做比较, 这里造成了冲突, 这2个方向都被pop, 接下来 up 成了当前方向.
up已经是最后一个方向或者跟下一个方向不造成冲突, 那它就被顺利执行了.
问题就来了, 前面执行了down, 这里又执行up, 那就移动了位置也没变化..
晒晒博主的背影