第一篇我們用數組實現了洗牌的步驟。當然還有其他的方法。這也就是編程的帶給我們的樂趣——靈活,自由。只要你想不得到,沒有你做不到的。
今天我們就來實現第二步。擲色子,發牌。
擲色子的問題,其實很簡單,但涉及到動畫制作的原理。所以我們先來補充些這方面的知識。
我們知道,人的視覺都有一種現象,比如當你在黑暗中看燈泡時,忽然燈泡熄滅了,但燈泡發光的影像還會在我們眼前停幾秒鐘,這就是我們熟知的“視覺暫留效果”。我們所看到的電視、電影,包括我們說熟知的flash,Director等動畫制作軟件,都是依靠這個效果才實現的。當我們在高速狀態下(一般是每秒24幀),按一定的順序切換內容相近和連貫的一組圖片時,我們看到的不是一張張單獨的圖片影像,而是一段連貫的動畫。
根據這個原理。我們擲色子的動畫也就很輕易實現了。只要我們快速的變換不同點數的色子6個面,就會讓人覺得好象是真實的轉動著的色子了。你不用擔心,運行速度的問題,只要你的機子能看vcd,一般都沒問題。先看看下面的代碼框架:
void zhisaizi()
{
int i;
i= (int)5*rand()+1;
switch(i)
case 1:
//載入第一張圖片
case 2:
//載入第二張圖片
case 3:
。。。。。。//省略中間過程
case 6:
//載入第六張圖片
}
至於怎樣載入和現實圖像不是我們本篇的主要內容,大家可以學習《windows程序設計》這本書。
對於發牌的問題,我想大家應該不會在有問題了。這裡我只給出代碼,其中有具體的注釋,就不解釋了。下面是代碼:
int apai[13],bpai[13];
//apai,bpai分別代表兩玩家手中的牌,假如你做的是四個人
//的麻將,可你在自由擴展
void fapai()
{
int a,b;
int i;//i代表發牌的起始位置
if((i/2)= =0)//是否為偶數
apai[a++]=pai[i++]; //雙數牌發給a玩家
else
bpai[b++]=pai[i++];//單數牌發給b玩家
}
下面是我們本篇要講的重點:就是在玩家都拿到牌之後,我們需要按順序擺放好牌。這裡我們會更加深入的談到數據結構方面的一些知識,希望大家專心。
對於這個問題實質上也就是排序的問題。我們前面用136個整型數來代表136張牌,可以用1~136分別表示1~9萬,1~9條,1~9餅,以及東南西北紅中發財,也可以用1~39中的個位數分別對應著和他相等的1~9萬,如3、13、23、33分別代表四張3萬。其他以此類推。剩余的數字來表示東南西北紅中發財。這個就可以由自己決定了。在此我們選擇第一種方法來描述問題。
那麼就請怎樣排序呢?最輕易想到的方法是:取得一個數,和他前面的所有數比較,直到找到一個前面的數比它小,後面的數比他大的位置,並將其放入其中。再取得下一個數,繼續上面的步驟。
示例如下:
8 7 4 3 6 1 //是要排序的數值
7 8 4 3 6 1 //第一次,取得7,小於前面的8,交換位置
7 4 8 3 6 1 //第二次,取得4,小於前面的8,交換位置
4 7 8 3 6 1 //第三次,小於再前面的7,交換位置
4 7 3 8 6 1 //第四次,取得3,小於前面的8,交換位置
4 3 7 8 6 1 //第五次,小於再前面的7,交換位置
3 4 7 8 6 1 //第六次,小於再前面的4,交換位置
3 4 7 6 8 1 //第七次,取得6,小於前面的8,交換位置
3 4 6 7 8 1 //第八次,小於再前面的7,交換位置
3 4 6 7 1 8 //第九次,取得1,小於前面的8,交換位置
3 4 6 1 7 8 //第十次,小於再前面的7,交換位置
3 4 1 6 7 8 //第十一次,小於再前面的6,交換位置
3 1 4 6 7 8 //第十二次,小於在前面的4,交換位置
1 3 4 6 7 8 //第十三次,小於再前面的3,交換位置
我們把這種方法叫做“插入排序法”。下面是源代碼。
void paixu ()// 用插入排序法
{
for (int i = 1; i <=13; i++)
{
int temp = apai[i];//取得一個數
int j;
for ( j = i; j > 0 && temp < apai[j - 1]; j--) //和他前面的每一個數進行比較,
apai[j] = apai[j - 1]; //假如這個數小於她前面的數就交換
apai[j] = temp;//插入到正確位置
}//end for
}//end
我們先來分析一下這種算法。可以看到,為了找到合適的插入位置,我們要用取得的數值與他前面的所有數值進行比較,並將進行多次的交換位置。尤其是當較小的數值放得越靠後時,交換到合適的位置所需的時間越長,如示例中的6和1,位置都靠後且相鄰,但6放置到合適的位置只用了兩步,而1放置到合適的位置卻用了五步。
有沒有其他的方法呢?針對比較次數和交換次數較多,我們可以用另一種“折半插入排序法”。基本思路是:為了減少比較的次數,我們不用每個數都比較,而是先找到所取得的數值在數組中的位置,並找到它前面已排好順序的數組的中間的一個數值,比較兩數,假如取得的數值大,就與後面的數值的中間數值比較,一直下去,直到找到合適的位置;同理假如所取得的數值小,就與前面的數值的中間數值比較,一直下去,直到找到合適的位置。
下面是示例:
8 7 4 3 6 1 //是要排序的數值
7 8 4 3 6 1 //第一次,取得7,小於前面的8,交換位置
7 4 8 3 6 1 //第二次,取得4,小於前面的8,交換位置
4 7 8 3 6 1 //第三次,小於再前面的7,交換位置
3 4 7 8 6 1 //第四次,取得3,小於前面的數組(4 7 8)中的中間數值7比較,小於7,所以比較數組(4 7)中間數值4,大於4,並交換到合適位置
3 4 6 7 8 1 //第五次,取得6,小於前面的數組(3 4 7 8)中的7,再與再靠前的數組(3 4 7)中間值4比較,大於4,交換位置
1 3 4 7 8 6 //第六次,取得1,小於再前面的數組(3 4 6 7 8)中間值6,再與數組
(3 4 6)中間值4比較,小於4,最後與數組(3 4)中間值3 比較,
小於3,交換位置
很明顯這種方法的減少了比較的次數,但交換次數仍沒減少。下面是其代碼:
void paixu()//用折半排序法
{
for (int i = 1; i < =13; i++)
{
int temp = apai[i]; //取得數值
int low = 0, high = i - 1;//low,high,分別表示比較范圍的上下限
while (low <= high)//折半查找
{
int mid = (low + high) / 2; //取得中間位置
if ( temp < apai[mid]) //假如小於中間位置的值
high = mid - 1; //從新確定下限
else //假如大於中間位置的值
low = mid + 1; //從新確定上限
}//end while
for (int j = i - 1; j >= low; j--)
apai[j + 1] = apai[j]; //記錄後移
apai[low] = temp; //插入到合適位置
}//end for
}//end