三阶魔方还原程序心得
第一次寫技術性的博客啊,本人菜鳥,如果哪里說的不夠準確完善,歡迎大神前來點撥呀~
先放幾張效果圖吧~
這是我最近做的一個三階魔方還原的小程序。
用的環境是VC++6.0,其中也大量運用了easyx庫進行圖形繪制等。
這次寫程序深刻感受到了算法是程序的靈魂。當時寫魔方算法的時候,準備了兩種算法,:第一種,儲存每一步的步驟,再逆序還原,這方法實現起來到不太難;第二種,想用傳統層先法還原,這倒是真把我難倒了。本來想著魔方還原都是有公式的,還原起來用這現成公式的算法不就好了嗎?但是,寫起來才發現沒那么簡單,因為人腦進行判斷時可以迅速處理所看到的色塊信息,靈活的進行公式變換,而電腦卻不像人那樣思考??赡軐θ藖碚f,層先法第一步,底層十字還原,不難做到,因為實現的方法多種多樣,但正是這多種多樣,讓電腦判斷時也需逐條分析。我當初一直想尋找復原十字通法而無果(本人其實是魔方菜鳥......),后來在某魔方大神的指導下依次判斷每個棱塊進行檢測,遍歷96種情況,或許方法不是最簡捷的,但終于完成了底層十字的復原,這一步的完成可謂是歷史性的啊,正所謂萬事開頭難嘛,完成了十字,之后依次是底層角塊、側棱、頂層棱塊色相、頂層角塊色相、頂層角塊位置、頂層棱塊位置,這些步驟結合魔方公式到比十字得心應手得多了。運用魔方公式的算法具體實現方法就不贅述了,就是根據魔方公式與程序有機結合啦。
首先,魔方的面是用的二維數組,并且運用了C++的類。
class Face { public:Face();COLORREF color;void coordinate(int x1, int y1, int x2, int y2);void coloring();private:int iX1, iY1; //立方體小塊涂色坐標int iX2, iY2; //展開圖小塊涂色坐標 };Face::Face() {color = RGB(0, 250, 0); //初始化 }void Face::coordinate(int x1, int y1, int x2, int y2) { iX1 = x1;iY1 = y1;iX2 = x2;iY2 = y2; }void Face::coloring() {setfillstyle(0);setfillcolor(color);floodfill(iX1, iY1, RGB(233, 233, 233));floodfill(iX2, iY2, RGB(233, 233, 233)); }實例化了六個面:?
Face F[3][3]; Face U[3][3]; Face R[3][3]; Face L[3][3]; Face D[3][3]; Face B[3][3];
接下來說說圖形界面。首先是一個發光魔方的歡迎界面,點擊“Rubik”(魔方)后即可進入,進入后分為圖形顯示界面和步驟顯示操作界面。圖形顯示界面左下角有操作說明:
“你可以摁字母控制魔方轉動:大寫代表順時針,小寫代表逆時針。如:R r U u L l D d F f B b M m X x Y y Z z ??按數字0可退出,按*逆序還原,按+傳統還原”。
操作說明框具體實現代碼如下:
//提示框 setcolor(RGB(255, 174, 201)); setbkmode(TRANSPARENT); //字體透明背景setlinestyle(PS_SOLID, 10);roundrect(50, 300, 450, 480, 50, 50); //圓角矩形setcolor(RGB(192, 45, 204)); LOGFONT f;gettextstyle(&f); // 獲取當前字體設置f.lfHeight = 30; // 設置字體高度為 30<span style="color:#cc33cc;background-color: rgb(255, 255, 153);">_tcscpy(f.lfFaceName, _T("Ravie")); // 設置字體為“Ravie”f.lfQuality = ANTIALIASED_QUALITY; // 設置輸出效果為抗鋸齒 settextstyle(&f); // 設置字體樣式outtextxy(140, 310, "Rubik's Cube"); //提示框說明_tcscpy(f.lfFaceName, _T("華文行楷"));setcolor(RGB(184, 39, 254));f.lfHeight = 20; settextstyle(&f); outtextxy(60, 340, "你可以摁字母控制魔方轉動:");outtextxy(60, 390, "大寫代表順時針,小寫代表逆時針。"); //綠字提示_tcscpy(f.lfFaceName, _T("jokerman"));setcolor(GREEN);f.lfHeight = 30; settextstyle(&f); outtextxy(60, 360, "如:R r U u L l D d F f B b M m X x Y y Z z");outtextxy(60, 410, "按數字0可退出,按*逆序還原,按+傳統還原");coloring(); _tcscpy(f.lfFaceName, _T("jokerman"));setcolor(RGB(253, 2, 46));settextstyle(&f);那些漂亮的字體,事實上windows已經為我們準備了很多漂亮的字體了哦!你只要打開“C:\Windows\Fonts”就能找到了哦~(*^__^*) 嘻嘻~
圖形顯示界面的背景是彩虹漸變色的背景,所用的方法是不斷地畫橫線,從上到下填滿整個屏幕,而漸變效果的秘訣就在于線條顏色參數的變化。代碼如下:
// 畫漸變的背景//彩虹背景色float h = 0.0; // 色相float s = 1; // 飽和度float l = 0.7f; // 亮度for(int y = 0; y < 500; y++) {h += 0.72;l += 0.0005f;<span style="white-space:pre"> </span>setlinecolor( HSLtoRGB(h, s, l) );//畫線 line(0, y, 1149, y); }其中 HSLtoRGB(h, s, l)就是用來畫線色彩變化的,循環的畫線,同時不斷調整顏色參數:色相、飽和度、亮度。所畫的每根線都不一樣,一起顯示出來的效果自然就是漸變啦~
在這里總結一下調色的三種方法:
方法一:用預定義顏色常量
| ? | 常量值顏色|||||
| BLACK | 0 | 黑 | DARKGRAY | 0x555555 | 深灰 |
| BLUE | 0xAA0000 | 藍 | LIGHTBLUE | 0xFF5555 | 亮藍 |
| GREEN | 0x00AA00 | 綠 | LIGHTGREEN | 0x55FF55 | 亮綠 |
| CYAN | 0xAAAA00 | 青 | LIGHTCYAN | 0xFFFF55 | 亮青 |
| RED | 0x0000AA | 紅 | LIGHTRED | 0x5555FF | 亮紅 |
| MAGENTA | 0xAA00AA | 紫 | LIGHTMAGENTA | 0xFF55FF | 亮紫 |
| BROWN | 0x0055AA | 棕 | YELLOW | 0x55FFFF | 黃 |
| LIGHTGRAY | 0xAAAAAA | 淺灰 | WHITE | 0xFFFFFF | 白 |
方法二:?用 16 進制的顏色表示,形式為:0xbbggrr (bb=藍,gg=綠,rr=紅),具體參數如上圖所示。
方法三:用 RGB?宏合成顏色。這里顏色范圍是0~255,比如你可以寫setcolor(255, 0, 0);這就是設置成純紅的啦~
方法四:用 HSLtoRGB、HSVtoRGB 轉換其他色彩模型到 RGB 顏色。
我這里用的就是HSLtoRGB。
該函數用于轉換 HSL 顏色為 RGB 顏色。
COLORREF HSLtoRGB(float H,float S,float L );參數:
H
原 HSL 顏色模型的 Hue(色相) 分量,0 <= H < 360。
S
原 HSL 顏色模型的 Saturation(飽和度) 分量,0 <= S <= 1。
L
原 HSL 顏色模型的 Lightness(亮度) 分量,0 <= L <= 1。
返回值:
對應的 RGB 顏色。
說明:
HSL 又稱 HLS。
H 是英文 Hue 的首字母,表示色相,即組成可見光譜的單色。紅色在 0 度,綠色在 120 度,藍色在 240 度,以此方向過渡。
S 是英文 Saturation 的首字母,表示飽和度,等于 0 時為灰色。在最大飽和度 1 時,具有最純的色光。
L 是英文 Lightness 的首字母,表示亮度,等于 0 時為黑色,等于 0.5 時是色彩最鮮明的狀態,等于 1 時為白色。
然后再說說成功復原時界面吧~
看到那句"Congratulations!You win~(碰我重新開始哦^_^)"了嗎?在成功復原的時候句子就會悠悠的飄~出來,遇到邊框還會反彈哦~當鼠標光標觸碰字體時就可以重新開始操作。
當時為了實現這個功能,到也調試了挺久的(我是菜鳥,很菜的菜鳥......),先看代碼吧:
int word(char word[]) //運動的字 /*如果用string word(char word[])會有error*/ {void picture();//定義字符串 // string s(word); //使用TCHAR s[]=_T(word);會出現 error C2440: 'initializing' : cannot convert from 'char []' to 'char []'FlushMouseMsgBuffer(); //清除鼠標消息緩沖區必須有,不然多次勝利后可能無法接收到"光標觸字"指令!//定義字符串初始位置int x = 10;int y = 10;//獲取字符串高度、寬度int w = textwidth(word);int h = textheight(word);//判斷移動方向bool isLeft = false;bool isUp = false;BeginBatchDraw(); //開始批量繪圖bool flag = true;//繪制移動文字while(flag){FlushBatchDraw();picture(); //繪制背景//實現用鼠標達到退出動畫的效果while(MouseHit()){MOUSEMSG m = GetMouseMsg();if((m.x>x)&(m.x<(x+w))&(m.y>y)&(m.y<(y+h))){BeginBatchDraw(); //開始批量繪圖(必須有,不然圖像顯示會出問題)picture(); //picture()函數必須放word()函數內調用,防止光標觸碰后字體無法及時清除的問題FlushBatchDraw(); //批量繪圖顯示pictureEndBatchDraw(); //結束批量繪圖flag = false;return 0;}}if( x - 10 <= 0 ) isLeft = false;if( x + w + 10 >= 1150 ) isLeft = true;if( y - 10 <= 0 ) isUp = false;if( y + h + 10 >= 500) isUp = true;if( isLeft )x -= 10;else x += 10;if( isUp )y -= 10;else y += 10;outtextxy(x, y, word);Sleep(100);}//退出EndBatchDraw(); }其中char和string貌似有點不同 int word(char word[])如果用string word(char word[])會有error,本來定義所飄動的字符串的時候想用string s(word);用來接收傳入的字符串,然而會報錯......char和string貌似有點不同?
使用TCHAR s[]=_T(word);會出現 error C2440: 'initializing' : cannot convert from 'char []' to 'char []',不知道是什么原因?
值得一提的是
FlushMouseMsgBuffer();這個函數用于清除鼠標緩存區,因為程序是不斷地獲取鼠標位置來判斷是否跳出函數,當時我沒使用該函數時,由于之前也可能收到了鼠標消息,所以會導致消息滯留,鼠標光標觸碰字體的時候的消息很可能沒有被及時接受處理,導致消息處理延時失靈。我一開始還以為電腦不靈敏呢,現在看來,一條感悟:自己寫的代碼運行后沒有達到想要的結果時,99%是自己的原因,電腦出錯的概率很小!然后一定要說說批量繪圖函數!之前我寫一些小的圖像覺得電腦運行速度挺快的呀,批量繪圖用不用好像沒區別吧,然而,這次寫轉魔方的變幻時,卻出現了頻閃,這是有原因的,浮動字符串浮動所用的方法是不斷調用繪制整個界面的函數用以覆蓋之前的界面,從而達到“清除”效果,就好像是圖形的繪制是只能加不能減的感覺吧,要用“覆蓋”來模擬“擦除”。之前說到彩虹漸變的背景是通過循環改變顏色參數并重復畫線直到覆蓋整個窗口來實現的,而且我所用的又是整體重繪,所以工程量自然就比簡單的小圖形大得多啦!所以批量繪圖就至關重要啦~
所以說:
<span style="white-space:pre"> </span>BeginBatchDraw(); //開始批量繪圖(必須有,不然圖像顯示會出問題)picture(); //背景及魔方的顯示 EndBatchDraw(); //結束批量繪圖這段代碼很關鍵呀!然后以下是字符串撞擊屏幕反彈的效果:
其中 if( x + w + 10 >= 1150 ) isLeft = true;和 if( y + h + 10 >= 500) isUp = true;
判斷距離字符串邊緣10像素的地方是否碰到窗口邊框。用了變量w和h分別存字符串的寬和高:
//獲取字符串高度、寬度int w = textwidth(word);int h = textheight(word);轉魔方函數定義如下:
void turn() //轉魔方 {//void Do(char turn[]);char xx[2]; //不能用此法,不然輸入會有延遲效果void computer();void coloring();char turn[100] = "";bool ifwin();int word(char word[]);system("color BD"); //設置控制臺前景色和背景色for(int i = 0; i<100; i++){turn[i] = '1';}i = 0;turn[i] = getch(); //不能用getchar();不然每次都要輸入回車cout<<endl<<"你的步驟如下:"<<endl;cout<<turn[i]<<" ";while(turn[i]!='0'){switch(turn[i]){case'R':Right();break;case'r':right();break;case'U':Up();break;case'u':up();break;case'L':Left();break;case'l':left();break;case'D':Down();break;case'd':down();break;case'F':Front();break;case'f':front();break;case'B':Back();break;case'b':back();break;case'M':M();break;case'm':m();break;case'X':X();break;case'x':x();break;case'Y':Y();break;case'y':y();break;case'Z':Z();break;case'z':z();break;case'+':computer();break; //電腦控制(層先法)// case'*':break; //電腦控制(逆序法)case'0':exit(1);default:break; //不能是continue;不然輸入其他鍵,圖形無變化,但也會顯示win}if(ifwin()) //判斷是否復原{word("Congratulations!You win~(碰我重新開始哦^_^)");for(i = 0; i<100; i++) //重新開始,再次初始化{turn[i] = '1';}i = 0;cout<<endl<<"你的步驟如下:"<<endl;// turn[i] = getch(); //不能在這兒輸入,不然會漏一個// cout<<turn[i]<<" ";}if(turn[i]=='*'||turn[i+1]!='1') //按下‘*’后用逆序步驟的方法復原{if(turn[i]=='*'){cout<<endl<<"電腦逆序復原步驟如下:"<<endl;}i--;if(65<=turn[i]&&turn[i]<=90){turn[i] = turn[i] + 32;}else if(97<=turn[i]&&turn[i]<=122){turn[i] = turn[i] - 32;}cout<<turn[i]<<" ";Sleep(1000);}else //沒按‘*’或‘+’則繼續輸入,手工旋轉魔方{i++;turn[i] = getch(); cout<<turn[i]<<" ";}} }對了,還要說一下背景音樂。
首先要在程序頂部寫一下
#pragma comment(lib,"Winmm.lib")然后 mciSendString("open \".\\資源\\background.mp3\" alias mymusic", NULL, 0, NULL); // 打開背景音樂mciSendString("play mymusic repeat", NULL, 0, NULL); // 播放音樂其中“repeat”用于循環播放。
播放完音效后,記得停止并關閉音樂哦~ // 停止播放并關閉音樂mciSendString("stop mymusic", NULL, 0, NULL);mciSendString("close mymusic", NULL, 0, NULL);因為可以靈活控制音樂的開始、結束、循環等功能,所以用在游戲音效里還是不錯的哦~總之,這次寫這個程序還是挺有收獲的啦~
--------------------------------------------------------------------------------
最近看到有這么多人關注我的這篇文章,心里很感動,謝謝大家的支持,所以決定進一步分享一下魔方還原的算法設計。介于我自己魔方水平有限,是照著魔方還原說明書設計的算法,所以大神們若發現有哪個地方設計的不妥當,歡迎分享更優質的方案,請多指教(。ゝω?。)☆
魔方還原參考的說明書:
整體設計思路
?
層先法程序流程1——還原底層十字
?
層先法程序流程2——還原底層四角
?
層先法程序流程3——還原中層棱塊
?
層先法程序流程4——還原頂層棱塊色向
?
層先法程序流程5——還原頂層角塊色向
?
層先法程序流程6——還原頂層角塊位置
?
層先法程序流程7——還原頂層棱塊位置
?
OK~最后附上下載資源的地址(但其中用到了easyx庫用于渲染圖形界面,需要自己在VC++6.0上配置一下哦):
魔方資源?(該資源比上面介紹的界面進一步做了一丟丟美化哦~)
本人C幣較少,借此資源賺點積分(*/ω\*)
最后,謝謝大家的支持啦!
總結
以上是生活随笔為你收集整理的三阶魔方还原程序心得的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对抗打码平台的验证码思路
- 下一篇: 如何在listary中调用谷歌翻译