2014年11月8日 星期六

Reduce C++ code size

如果拿程式執行速度和code size二者來比的話,我個人會先偏好較小的code size,而不會特別先去關注較快的執行速度,除非效能問題是個問題。很多時候,較小的code size,也意謂著較快的速度。C++程式最讓人經常誤解的一點是,會產生較大的code size,較慢的執行速度是另一個常見的迷思。不過事實上,在使用STL之類的template library或template class時,的確使用不當的話是特別容易產生較大的code size。下面提供幾個簡單的要點,可以幫忙稍減code size。

  1. 使用compiler的最佳化選項。一般分為針對執行速度最佳化或code size最佳化。以Visual Studio為例,/O1是minimize size對code size最佳化,/O2是maximize speed對執行速度作最佳化。
  2. 避免重複。把common code抽出來,作成function,library或之類的東西。減少source size也減少code size,也減少bug發生的可能性。
  3. 砍掉沒用的code。整支程式裡面若是包含了大量的無用程式,除了增加code size外,也可能會隱藏潛在的bug。花點時間,整理一個你的程式把無用的程式碼,短期內或可見的未來內用不到的程式碼清一清。不要過早加入用不到的功能,減少code size也減少bug。
  4. 不要include <iostream>,假如你沒有用使用cin/cout/cerr/clog之類的內建物件。用include <iosfwd>來取代,避免包含使用不到的物件來減少code size。
  5. 避免類似型別參數的template類。例如:vector<int>,vector<unsigned int>等,這些類別的參數可以替換成同一種型別,例如vector<unsigned int>。使用同一類別的template容器,也能減少code size。
  6. 減少使用exception及RTTI。沒必要的話,減少使用exception及RTTI也能減少code size。

以上是幾個大方向上,如何減少code size的要點。當然還有一些手法,但都是較細節的小技巧,不一定有什麼幫助。就好像在作最佳化時,只要先針對瓶頸作最佳化,其它的小地方不去特別理會也不會有太大影響。

2014年8月31日 星期日

HTML5 互動式象棋譜

十多年前在舊書攤上挖到寶,發現一本曾經在某處讀到得到高度評價的棋譜”佈局津梁”,雖然只有下集,立刻把它買下來,印象中花了100塊左右,現在倒是漲了不少,可惜的是一直找不到上集...不過和許多買過的書的下場同樣,後來大多變成收藏用,只有有時候心血來潮會把它拿出來翻看。因為這本書的年紀比我大,怕把它給翻壞了,事實上封面皮都快掉了,好像隨時都要碎掉的感覺,所以後來花了不少時間將內容手工輸入電腦建檔。最近因為玩些HTML5的東西,所以順便就把它作成網頁版本,實作一個簡易版的互動式棋盤幫助看譜,順便當作練習題。程式很簡單,倒是花了非常大量的時間在校對棋譜內容改正原書的錯誤,不過因為個人能力有限還是有許多錯誤還沒更正...

這個程式主要在Chrome及Firefox測過,手機版viewport寬限定為320。

(玩玩看)


整個程式很簡單,因為棋譜格式是制式的,所以只需要利用幾個簡單的re的幫忙,很簡單的可以用來找出那一段是文字那一段是棋譜,然後再parse出棋譜內容走步。基本上棋譜還是以字串方式作處理,只要棋譜格式及內容沒有錯誤,都能正確演示。有任何顯示錯誤,則表示棋譜格式或內容有誤,因為我並沒有多作錯誤的檢查。

實際實作時在Chrome及Firefox有二個地方需要特別注意。

  1. Firefox不支援innerText的用法,需改用textContent。實作上可以簡單以a = p.innerText || p.textContent解決。
  2. Firefox不支援mouse event的offsetX/offsetY,我們可以另外包裝一個getOffset的函式解決:
    function getOffset(e)
    {
      if (e.offsetX) {
        return {x:e.offsetX, y:e.offsetY};
      }
    
      var el = e.target;
      var offset = {x:0, y:0};
    
      while (el.offsetParent) {
        offset.x += el.offsetLeft;
        offset.y += el.offsetTop;
        el = el.offsetParent;
      }
    
      offset.x = e.pageX - offset.x;
      offset.y = e.pageY - offset.y;
    
      return offset;
    }

2014年8月12日 星期二

HTML5 WebSocket 多人連線麻將

之前以java實作了多人連線的麻將Client及Server,現在加上使用HTML5 WebSocket的client端,經最新版的FireFox、Chrome及我的A6s上測試過。這同樣只是一個Demo,由簡單AI打麻將。現在二種不同平台不同網路協定的Client,可以連線對打麻將了。




;

底下對於WebSocket的Server及Client實作作一個重點摘要。

1,依據RFC6455完成WebSocket的Handshake,網路上有很多資料細節這裡就不多作說明。需要注意的是,對於"Connection: "這條內容,Client送給Server什麼,Server就需要回同樣的東西給Client。例如:Chrome送給Server的是"Connection: Upgrade", 而Firefox送給Server的是"Connection: keep-alive, Upgrade"。Server端對Handshake的回應也是以HTTP response形式,之後就可作一般的socket讀寫。

2,Server端的讀寫,需要特別注意的是Payload len的處理:如果小於等於125,那就是原始資料長度;如果等於126,則實際資料長度為接下來的二個byte指定的16bits數字;如果等於127,則實際資料長度為接下來的八個byte指定64bits數字。

3,Client端的onmessage事件中,如果處理的資料是binary,需對傳入的物件作型別檢查。

ws.onmessage = function (evt) {
  if (evt.data instanceof ArrayBuffer) {
  } else if (evt.data instanceof Blob) {
  } else if (typeof evt.data === "string") {
  } else {
  }
}

(src)

2014年7月9日 星期三

Good Player移植到Android平台

花了三天終於把Good Player移植到Android平台了... 為什麼拖了那麼久才作這件事呢? 真是一言難盡... 下面稍微講一下移植的主要過程和重點。


* 下載adt-bundle-windows-x86-20140321及android-ndk-r9d,免安裝解壓縮即可。

在這之前,我拿野火機的時代也曾經裝過Android SDK,不過那時只compile了hello程式就沒下文了,因為我的野火機實在太慢,讓我興致全無就沒有繼續下去。後來最近改拿A6S才又臨時起意,再把eclipse打開試試,結果又發現連不到我的手機。改安裝以上版本SDK,果然和版本有關可以連了,不過還是又拖到這幾天才真正開工。

*  編譯測試NDK sample: hello-gl2。

先確認可以編譯出來,並在A6S上跑。這點很重要,找一個可以編譯並執行的範例,這個範例可以demo我需要的功能,這樣我就能以這個範例為基礎開始工作。

* 參考hello-gl2範例,新建一個proj。

* 編譯需要的lib。

good主要使用到幾個lib,zlib, lua, libpng, libjpeg, smallworld2。SDK裡面已經有zlib了,所以直接使用。編譯lua5.1.4時遇到compile error,這是因為locale.h支援不足問題,很容易解決。libpng及libjpeg都沒遇到問題,smallworld2是自己的lib也很順利。最後加上good的code,也都OK編譯過了。非常好,一切順利!

* 把畫面顯示出來。

hello-gl2的畫面是個三角形,現在把它換成good的畫面。要顯示good的畫面很簡單,我只在JNILib_step把原來的renderFrame換成good的render就行了,不過一開始沒有顯示東西。搞了老半天,原來我每次改的東西需要在eclipse裡面第二次執行的時候才會update到手機上。但因為我只執行一次,所以看到的是舊的程式,而我又以為有問題所以又再修修程式重新執行,所以一直看不到新改程式的結果,工具不熟這個問題就讓我搞了不少時間... 加上版本設定之類的問題,最後終於讓我看到我畫的一個紅色方塊。可以看到紅色方塊方塊後,表示good可以畫出東西了,現在可以換成測試用的good proj來跑跑看了。

跑了一個test,東西出來了非常流暢,太興奮了!雖然都是白色方塊,是貼圖問題。研究一下,是設定問題,改了一行程式全部都正確顯示了!

* 加入choose file dialog。

一開始要執行那個good sample是寫死的,現在可以加個dialog自己選要開那個檔了。google研究一下,找一下最簡單的方法:



  final int ACTIVITY_CHOOSE_FILE = 1;

  @Override protected void onCreate(Bundle icicle)
  {
   ...
    Intent chooseFile;
    Intent intent;
    chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
    chooseFile.setType("*/*");
    intent = Intent.createChooser(chooseFile, "Choose a file");
    startActivityForResult(intent, ACTIVITY_CHOOSE_FILE);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (ACTIVITY_CHOOSE_FILE == requestCode) {
      if (resultCode == RESULT_OK){
        Uri uri = data.getData();
        String filePath = uri.getPath();
      }
    }
  }

* 在View上加入onTouchEvent的處理,模擬mouse輸入,

* 加入back key的處理。

一開始我把onKeyDown加在View,結果沒有作用。後來把onKeyDown改成在Activity實作,就work了。


;


以上是good移植到android的主要步驟。不過就像iOS, PSP, embeded system等其它平台的移植,除了Windows版本的player有聲音外,其餘平台都還沒有移植聲音...

Just for fun!

2014年6月21日 星期六

新增貼圖物件小工具(Select Texture Dimension)

* 新增貼圖物件小工具(Select Texture Dimension)

終於加入貼圖物件小工具到關卡編輯器(Level Editor),這樣就不用再手動輸入貼圖物件在貼圖裡的偏移座標和寬高了。


* 對編輯器的程式碼作更多重整...

以往主要的重點都放在底層核心,這陣子針對編輯器的程式碼作了不少重整的動作,提高可維護性。當然,作這些工作背後的真正目的是訓練,同時也從過程中又發現及學習到一些比較細節的東西。

2014年6月15日 星期日

改良粒子檢測器(particle editor)

* 改良粒子檢測器(particle editor)


只作一個小改良,在toolbar上可以知道目前是source view或是particle view。

* 修正一個當particle view active時,save changes dialog focus lost問題
* 針對二個效能瓶頸作最佳化
 - 一個是消除temp object
    - 一個是以空間換取時間

2014年5月5日 星期一

深奧又愚蠢的問題

前陣子有同仁發現我們的系統中有一支driver會造成系統當機,因為這支driver目前的負責人換我接手了,所以問題就轉到我手上,直到最近把手上幾個比較重要的項目告一段落後才抽空來看這個問題。

一開始先只看一下source,程式看起來不複雜,可能有問題的點都是很簡單易懂的標準程式寫法,看起來沒什麼破綻,看不出問題來。這支driver主要的程式註冊了一個event callback,在這個event callback裡面又會觸發另一個event,而這個event又會讓別支driver註冊的event callback執行,然後啟動一個service。看起來好像很複雜,其實並不複雜,就把它當成只連續call了幾次function就行。

我把driver打開執行看看,果然在我的環境也會發生系統當機的問題,是可以複製的問題,所以不是環境平台不同的問題。再把這支driver關閉,果然就沒有系統當機的問題,所以應該很有可能是這支driver造成的問題。接著我試著調整這支driver的執行次序看看,結果發現如果我讓它提早一些執行,結果就不會當機了。是那邊的記憶體有問題嗎?因為發現問題的同仁也說過,我們release的程式裡面,有幾個版本不會造成當機,但有的版本又會當機。現在我調整次序後又不會當機了?看來應該和記憶體有關係,是這支driver前面的driver造成的嗎?我又去看看前面的driver,看起來也是很簡單的程式,完全看不出來有什麼點是有可能會發生問題的。

光是作了以上幾個測試就花了我不少時間,因為我們的系統每次修改程式到重新編譯程式準備好環境作測試就會花不少時間。debug的方式主要是丟除錯訊息,因為這是最便捷的方法,當然也能支援source level debug,只是因為設定太麻煩以致於平常都習慣靠除錯訊息除錯。最後實在不行了,既然已經花了那麼多時間測試了,不如再多花點時間把source level debugger架起來吧。

source level debugger架起來後,我當然直接在driver的event callback裡面下一個breakpoint,然後執行看看。結果發生詭異的事情!第一次執行,程式並沒有中斷,這就奇怪了,我斷點是下在程式必經之路,怎麼不會斷呢?第二次程式是中斷了,但是居然斷在另外一支driver的callback裡面!我比對了我下斷點的function的位址和實際中斷的function的位址,還真的是一樣的位址。這種事情怎麼會發生呢!?這一定是記憶體的問題。

很快的我就找到問題真正發生點並把它修好:原來會造成系統當機的這支driver在註冊event callback到系統後,後面的初始化動作有問題,對系統報告錯誤後被系統unload掉了。但在driver被系統unload之前並沒有把先前註冊的event callback作反向的unregister動作,所以在系統裡面的記錄就指向一的不明位址。這就是造成當掉的原因,也是有時會當有時不會當的原因。因為這是memory的問題,現象會根據當是系統狀態而有不同。

這個問題初看好像很深奧,其實很愚蠢,這讓我又得到二個教訓。


  1. 那些看起來顯而易見沒有問題的程式有時候真的是問題的所在!
  2. 可以的話還是儘快打開你的source level debugger吧,source debug還是最強大的debug方法!


2014年4月11日 星期五

路徑搜尋之演化論

我現在要作一個實驗,模擬一個虛擬的世界,這個世界的構造很簡單,是一張10乘10的地圖,總共100個格子所組成。在這個世界裡面,會隨機產生金幣,機率是一半一半,也就是說大約在這100個格子裡面,平均會有50個金幣。還要模擬某種生物,這種生物的生存目的很簡單,就是儘可能的吃金幣,因為這個世界的食物只有金幣。能夠吃愈多金幣的生物就愈能夠生存下來並把它的基因繼續傳播下去。生活在這個世界的生物就把它命名為吃金幣的人...吃金幣的人在這個世界能作的事情很有限,除了吃金幣外,他只能作上下左右四種方向的移動,每次只能移動一個格子。

這個實驗的目的是想辨法找到一個最佳的路徑搜尋方法,讓吃金幣的人儘可能吃愈到愈多的金幣。為了評估吃金幣的人的路徑搜尋方法有多好,需要有一個可以量化的方法。所以就很簡單的設定,如果吃金幣的人每吃到一個金幣就能獲得10分,分數愈高就表示它的路徑搜尋方法愈好。當然也不能讓吃金幣的人任意的作無限次移動,因為這樣一來就沒有意義了,因為吃金幣的人一定可以吃光世界上所有金幣。所以我們要限制移動次數,比如說200次,每200次移動後再來作評分。

我們將會使用基因算法(genetic algorithm),讓吃金幣的人自行進化,得到一個認為最棒的路徑搜尋方法。

基因算法的主要精神是,對於所要解決的問題以模擬生物界物競天擇適者生存的自然法則,讓所摸擬的人工生物自行進化出在模擬世界中的優勢物種,也就是得到所求的最佳解。不過這裡講到的進化這二個字有誤導的嫌疑,實際上這也是大多數人對於進化及演化之間的誤解。進化這二字裡面隱含了方向性,而事實上演化是沒有方向性的,演化是生物對生存環境的適應。生物會演化成什麼樣子,沒有任何的目的性和方向性,這裡面帶有隨機性,主要取決於環境變因。

自然選擇和變異再加上時間是演化的基本原則,所以我們將要模擬的程式也是使用到這幾個基本原則。雖然實際上的細節,和真實世界生物界的演化並不相同,但因為我們利用這幾個演化的基本原則來模擬我們的世界,結果也會得到類似的結果。我們可以看到我們所模擬的吃金幣的人果真在這個金幣世界裡面,慢慢的演化出愈來愈聰明的路徑搜尋方法。而同自然界的生物演化一樣,你甚至無法知道也無法分析它到底是怎麼辨到的。



前面提到這個世界是個10x10的地圖,共有100個格子,在這個世界面裡有會隨機產生的金幣。如果一個格子上有金幣就不為空,反之表示這個格子是空的狀態,因此每一個格子有二種不同的狀態,所以我們可以用一個boolean值來表示一個格子。不過因為這個世界是有邊界的,為了讓吃金幣的人有辨法意識到邊界的存在,我們也需要把這個資訊編碼進來。所以加上有無金幣及邊界,每一個格子共有3種可能的狀態。我們可以用一個數字表示一個格子的3種不同狀態。

  0: 空
  1: 金幣
  2: 邊界

吃金幣的人在這個世界裡會有一個唯一個位置,我們簡單把座標定為0~99。他能作的動作有5種,吃金幣,往上往下往左或往右移動,移動的時候一次只走一步。因為吃金幣的人只能作4種方向其中一個方向的移動,再加上他目前所在的位置,所以可以說他的可見範圍有5個格子。

    0
  1 X 3
    2

如上,吃金幣的人的當前的位置是X,0123是其它4個方向所在的格子。因為每一個格子有3種可能狀態,5個格子就總共有3^5=243種可能狀態。如果我們把這243種不同狀態分別對應到吃金幣的人可以作的其中一種動作,例如吃或往上移動一格等等,就等於得到一個路徑搜尋的演算法。只要每次根據吃金幣的人的所在位置,以及他的視野上的5個格子的狀態來對這243種狀態表去查詢要作什麼動作,吃金幣的人就會自動的按照這個表的設定動起來。而這個表也就是這個吃金幣的人的基因!每一條基因所編碼的內容是狀態對動作,也就是生物對不同環境狀態作所出的不同反應。這看起來很機械,只是個查表動作。雖然只是機械式的查表,但生物所表現出來的行為就好像具有意識,只要這個表足夠複雜甚至能表現的好像擁有智慧一樣!

  0: 往上移動一格
  1: 往左移動一格
  2: 往下移動一格
  3: 往右移動一格
  4: 吃金幣

這一來每一個吃金幣的人都有一條長度是243個單位的基因,每一個單位就是一個數字,對應吃金幣的人所能執行的5種動作之一。當然243是理論數字,這裡面有一些狀態是不可能出現的而可以排除,但為了簡單起見我們就不特別處理了。

下面是一段長度是243的可能基因,不同位置的數字對應不同狀態的反應動作,而.是無動作的遮罩表示這個位置的基因是冗餘。

30.44.11.13.04.13.33.30....12.22.41.20.40.11.34.02....32.40.24.10.03.02.33.42....01.40.24.14.31.44.32.10....31.12.00.11.40.31.14.01....04.31.33.44.13.43.10.42....11.23.11.01.01.12.24.34....11.02.40.33.00.22.32.34...............................

模擬的方法如下:

  1. 一次模擬200個吃金幣的人,每一個初代吃金幣的人都隨機給一組基因。
  2. 讓吃金幣的人根據他自己的基因設定走個200步,最後再看看得分是多少。
  3. 這裡面有個扣分的機制,如果吃金幣的人走出邊界了要扣分,因為這是不允許的。或者如果吃金幣的人作了吃金幣的動作,但那個格子裡面沒有金幣也要扣分,因為他亂吃。這個扣分的機制等於是讓不好的基因淘汰的機制,給他一個生存的壓力。
  4. 上面讓每個吃金幣的人走200步的模擬,每一個吃金幣的人都作100次,然後再統計一個平均分數。
  5. 針對每個吃金幣的人的平均分數作排序,由高到低。可以知道這200個吃金幣的人的基因,誰優誰劣。
  6. 對這200個吃金幣的人兩兩配對來產生下一代。配對選擇的方法是,得分愈高的也就是基因愈優的就愈容易被選擇。得分低的也還是有可能被選擇,只不過機率較低,這樣一來也可以讓那些得分低,但在未來有潛力的基因有機會出頭。
  7. 配對的方法是隨機選擇一個基因中間位置,將二個親代基因切斷並交換。當然還有其它配對方法,這裡只選擇最簡單的一種。
    
    00003333333
    44448888888
     =>
    00008888888
    44443333333
    
    
  8. 新的子代基因再以一定機率產生突變。
  9. 跳至2繼續模擬。
下圖是某個吃金幣的人的成長曲線圖,水平軸是時間,垂直軸是分數,Y-軸上的數字是平均最高分數。上面的點線是最大值,下面的點線是種群平均值。


這是某個吃金幣的人大約迭代了3000次的結果。


上圖分別是另外二個吃金幣的人迭代了約4000次及6000次的結果。

可以看到這三個吃金幣的人的成長曲線和速度都不同,但他們都是朝特定方向進化。我會說進化,是因為他們的演化的確是有方向性的,而這個方向是我刻意控制的,也就是讓這些吃金幣的人想盡辨法得高分。

在這些模擬裡面,有時候也會出現類似進入演化的死胡同的現象。這樣的現象是因為在模擬的過程中過早把那些具有潛力的基因給淘汰掉了,因為分數不夠而被選擇的機會太少。即使後來因為突變而又產生這些基因,也因為種群數量太少而沒有辨法讓這些基因傳播開來。結果就是化大概只能永遠停留在某個高度,不能再進一步成長。


2014年4月4日 星期五

永恆的OLG - 壓力測試

利用一點空檔時間把client模擬程式修好。



在開發連線程式的初期,我都會先製作一個簡單的測試程式,用來摸擬大量client,主要用來作簡單的壓力測試,這個程式讓我可以在很早期的時候就先抓出一些難以發覺的底層錯誤。還記得我在開發另一個網路底層時,透過測試程式讓某個非常難以出現的問題浮現出來,一旦看到問題的存在,接下來就是開啟大量的測試來模擬同樣的狀態重現問題。那時候光時讓同樣的錯誤重現,就需要讓測試程式連續跑了好幾個小時,等到程式終於當在設定好的點後,再來透過除錯器很快的就把問題的原因找到並修正這個潛在的問題。假如沒有這樣的測試程式的話,相信這個問題可能會很難被發現,甚至有可能永遠都找不到,只能是一個未解之謎。

2014年2月6日 星期四

永恆的OLG ~ 2

把十年前的作品拿出來,試著build看看。其實很早就想這樣作了,這樣作的目的很單純,因為這是一個具體而微的on-line game,可以以它為基礎繼續作研究實驗。



2014年1月12日 星期日

快速組合算法(Fast Combination Algorithm)

猜數字遊戲 (電腦猜人)這篇文章裡,最後提到怎麼從10個數字裡面挑出4個數字的組合的方法。

這個方法很簡單,0x3ff這個數字共有10個位元1(bit),我們可以把這10個位元和0-9這10個數字作一一對應。用一個迴圈來找從1到0x3ff這些數字裡面,有那些數字是包含4個位元1,也就是要找的。例如0xf這個數字,2進制是0000 0000 0000 1111,最右邊4個bit都是1,根據1的位置轉換後變成3210這個數字。同理0x17這個數,2進制是0000 0000 0001 0111也是4個bits為1,轉換後為4210這個數字。依此類推。

這個方法用來選取小範圍數的組合還可以,可是一但數字大了,效能就太慘不忍睹了。例如以今彩539的可能組合為例,全部1-39共39個數字裡面挑出5個,全部共有575757種組合。雖然57萬多種組合看起來不大,但是以上面的方法來找,則要搜尋的迴圈範圍一下暴漲為2^39才能得到這57萬組結果,這是一個天文數字。所以這個方法就不可行了,勢必要使用更有效率的演算法才行。

稍微研究後,很幸運的不到10鐘就讓我找到了一個看來可行的演算法,很快的寫了一支小程式來測試。測試結果果然很理想,以C(39,5)作計算測試,不到1秒鐘的時間就得到正確結果!

;

底下是演算法的概述,同樣以10個數字取組合為例:

* 考慮最簡單的情況,作10取1組合

0 1 2 3 4 5 6 7 8 9

10個數字共有10個位置可以放入數字1,從最左邊的位置開始,共有10個位置可選擇,所以總共有10種可能。

用計算組合的公式驗算一下,C(10,1)=10!/1!(10-1)!=10!/1!9!=10

* 考慮10取2的情形

因為第一個1有0-9共10個位置可以選擇,如果第一個1選擇放入位置0,則第二個1有1-9共9種位置可以選擇放入。因為位置0的情況已經考慮過,所以接下來只要往後看不再往前看,需要排除掉已考慮過的位置。接著考慮如果第一個1選擇放入位置1,則第二個1排除位置0和1後還有2-9共8種選擇。依此類推,根據第一個1的位置選擇不同,則有9(p0), 8(p1), 7(p2), 6(p3), 5(p4), 4(p5), 3(p6), 2(p7), 1(p8), 0(p9)種選擇,小括號內的p0-9為第一個1的選擇位置。第一個1放入位置9時,第二個1就沒位置可放入了,所以可能性為0種,因此全部可能性共有9+8+7+6+5+4+3+2+1=45種。

驗算一下,C(10,2)=10!/2!(10-2)!=10!/2!8!=45

;

根據上述的討論,可以以遞迴的方式實作出上述演算法。虛擬碼如下:

void C(int LeftPos, int Depth)
{
  uint64 Flag = 1 << LeftPos;
  for (int i = LeftPos; i < MaxPos; i++, Flag <<= 1) {
    if (!(AllPosMask & Flag)) {
      if (0 < Depth) {
        AllPosMask |= Flag;
        C(i + 1, Depth - 1);
        AllPosMask &= ~Flag;
      } else {
        // Found it!
      }
    }
  }
}


Related Posts Plugin for WordPress, Blogger...