跳到主要內容

發表文章

單人撲克牌遊戲 - 蒙地卡羅

新增一個簡單的單人撲克牌遊戲: 蒙地卡羅 ,簡單介紹一下玩法。 下載 事先排列好5x5張牌。 每次移動一張可以配對的牌,並消除這對牌。在上下、左右及斜向相隣的二張牌,只要擁有同樣數字(不計花色),即可配對。 消除二張配對的牌後,剩餘的牌以往左往上的方式補滿空隙,接著在發新牌補滿後面的空格。 重覆步驟2~3,直到沒有牌可以配對及發完所有牌為止。 結果有二種。一個是勝利,成功的消除掉所有牌。另一個是Gameover沒有牌可以再作配對。

以Boost.Spirit實作算式計算機

Spirit 是開源碼C++程式庫 Boost 裡的一個項目,大量使用了C++ Template Meta-Programming實作技術。它提供了一個用來實作語言剖析器的框架,就好像lex/yacc一樣是專門用來製作語言剖析器的工具。但是Spirit因為應用了C++ Template Meta-Programming的技術,使得我們可以在C++程式碼中寫出相當於 EBNF 的描述且可直接編譯並執行,也因為如此Spirit是一套既神奇又威力強大的工具程式庫。 按照慣例,前面我們已經以 手工的方法 ,以及 lex/yacc的方式 實作了算式計算機,這次我們要使用Spirit來作個簡單的算式計算機。 EBNF表示式 雖然前面我們已經提到過Spirit的神奇的能力,也就是可以直接把EBNF式直接以程式碼的形式寫在C++程式碼內,但是因為受限於C++程式語言的語言,有些表式法需要作些調動,這裡面主要有幾個地方的語法需要注意。 A B 上面是原EBNF表示式的寫法,用來表示一個序列,A之後會有個B。而在Spirit裡需改寫成如下的形式。 A >> B 看起來沒有什麼太大的不同,也可以很容易的理解這是表示在A之後跟著B的序列。再來是*號的用法。 A* 在EBNF內,在符號A之後加個*號,表示A的數量是可重複的,可以是0個以上的A所構成的序列。而在Spirit中需改寫成如下形式。 *A 正好相反,需要把*號擺到符號的前面,因為在C++裡面並沒有任何後序式的*號運算元。不過就算改成前置式也不會影響到原來所代表的意義,並不會造成什麼衝突,容易理解。 A? 這是表示符號A是可有可無,也就是A的數量可以是0個或一個。以下改成以Spirit的寫法。 !A 如你所見到的,問號變成驚嘆號,同時也提到前面變成前置式。那麼一個以上的表示式呢?一樣使用加號,也一樣改成前置式即可,如下所示。 +A 現在來看一個簡單的範例,如下所示是EBNF式,用來表示以逗號分隔的數字的EBNF表示式。 S := INTEGER (',' INTEGER)* 改成Spirit的形式。 S = INTEGER >> *(',' INTEGER); 很直接,也不需要再多作說明。不過上面的式子還不是最終可用的版本,底下還會再陸續交待清楚。 Parser 在上面我們所看...

malloc(0)的回傳值?

用VC寫個小程式作個實驗。 int *a = (int*)malloc(0); if (a)   cout else   cout 程式執行的結果是, 'not null' 為什麼配置一塊大小為0的記憶體的回傳值不是NULL? 有時候要配置的記憶體大小是計算得到的,這個大小有可能為0,為了和配置失敗回傳的NULL作區分,同時也能保持程式對任何大小記憶體的處理一致,所以就讓0大小的配置也回傳個有效的指標。 不管是不是NULL,free都能接受。 ... 最後,根據 C99標準 : If the size of the space requested is zero, the behavior is implementation-defined : either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object. 所以,簡單的講,malloc(0)是沒有定義的行為。對於沒有定義的行為,去討論它是沒意義的...

虛擬檔案系統

大家都頃向於把自己遊戲所使用到的資源,以自己的檔案格式作加密打包。我也不例外,以前也和大家一樣設計了自己的檔案格式,可以將多個檔案加密壓縮打包起來,當然也還需要製作可以讀寫這種檔案格式的工具程式。實作出來後感覺還不錯,只不過其實這只是多此一舉的動作。 後來我改使用zip格式作資料包。想要加密?也沒有問題,加上密碼保護就可以了。工具也有一堆現成的,要用 WinZip 或 7-Zip 什麼的都可以,完全不必花費任何力氣就擁有工具可以讀寫資料包。所有我需要作的事就是設計一個程式庫,可以用來讀寫 Zip檔案格式 就行了。事實上 zlib 已經能夠滿足這個需求了,只需要再多作一點程式包裝。所以我多花了些功夫在程式介面的設計上,最後完成了虛擬檔案系統的設計( smallworld2 ::Archive)。 為什麼把它叫作虛擬檔案系統呢?因為它目前支援了二種檔案系統,一個是一般的路徑檔案系統,另一個就是剛剛提的zip檔案系統。不管你使用的是那一種檔案系統,對於呼叫者而言是抽象的。呼叫者只需要透過統一的介面只存取檔案,完全不必考慮這個檔案是在那個資料夾或那個zip檔內,或甚至是在那一塊記憶體內(這是第三種形式)。 class Archive { public:   bool addFileSystem(std::string const& path);   bool addFileSystem(std::istream& stream);   bool isFileExist(std::string const& name) const;   bool loadFile(std::string const& name, std::ostream& outs, std::string const& password=""); }; 如上是虛擬檔案系統的C++程式介面,很簡單。因為其中也使用了C++ stream作為資料交換介面,所以也能很容易的和 C++ IOstream 家族結合使用,變的很有彈性。 addFileSystem是用來增加一個路徑或zip檔案到搜尋路徑。而另一個stream的版本則是用來加入一個來自串流的檔案系統,這個串流可以指向C++ Fil...

good Game Editor 1.1 Beta Release!

遊戲資源編輯 貼圖 (Texture editor, Jpeg, PNG, BMP) 聲音 (Audio, OGG, WAV) 地圖編輯 (Map editor) 精靈編輯 (Sprite editor) 關卡編輯 (Level editor) Script編輯 (Script editor) 範例程式 打磚塊/寶石方塊/踩地雷等16個小遊戲範例 遊戲範例 25940m 輸出WIN32獨立可執行檔 (Stand-alone exe) ; 本來要等到加上文字物件後再釋出,不過這陣子比較沒時間更新,所以先把目前的版本作為1.1beta釋出... 這個版本作了許多小更新,主要在加強 particle的部份,應用方面可參考25940m,另外API reference也作了更新。 下載good Game Editor 1.1 Beta API 參考手冊

猜數字遊戲 (電腦猜人)

前幾天午睡時突然被告知要參加公司內部的程式設計比賽,題目是用C寫一支文字模式的4位數字猜數字遊戲,由使用者來猜電腦的數字。在上星期時其實就已經有公佈了,但我沒有注意到所以是臨時加入,還好這是個簡單的題目,不用花多少時間就可以寫出來。 規則: - 這是一對一比賽,雙方各選擇一4位數字,不讓對方知道。 - 4位數字由數字0至9組成,每位數不得重複。 - 雙方輪流猜對方的數字,直到一方猜中為止。 - A方猜B方的數字後,B方根據A方的猜測回答幾A幾B。 - 一個A表示猜中一個數字且位置正確,一個B表示猜中一個數字但位置不正確。 - 當一方猜中4A0B時即表示猜中對方全部4個數字且位置正確,贏得比賽。 - 例:B的謎底是4208,底下箭頭左測是A的猜測,箭頭右測是B的回答。    1234 ==> 1A1B    5678 ==> 1A0B    2406 ==> 1A2B    ...    4208 ==> 4A0B ; 寫個程式讓玩家來猜電腦的數字不難,不過我從來沒有寫過讓電腦來猜玩家數字的版本,所以花了點時間想想怎麼寫。 研究後歸納出二個點。 1, 使用窮舉法將所有可能數字組合列出。 2, 每次猜測後根據結果排除不可能是答案的組合,重複這個動作直到猜中答案為止。 第1點只是實作問題,第2點概念也很簡單,但要過濾不是答案的組合根據的是什麼?乍看之下沒什麼頭緒,不過想通之後就非常簡單了。 它的基本原理如下:假如謎底是4561,如果猜1524則會得到1A2B。從相反的角度來看,如果謎底是1524,則猜4561時也會得到1A2B的回答。 利用這個方法,每一次猜測一個數字X後,再以這個數字當作答案,來和所有剩下來的候選答案作比對,如果得到的結果(幾A幾B)和數字X是一樣的話,就把這個數字保留下來繼續作為候選答案,否則就過把這個數字過濾掉。下一把,繼續從候選答案裡選一個出來猜,重複上面的動作,直到猜中為止。 ; C++ STL的algorithm裡有個叫作next_permutation的函數,可以用來生成排列。 #include <iostream> #include <algorithm> using namespace std; int main () {   int myints[] = {1,2,3};  ...

以lex/yacc實作算式計算機

前面我們透過 手工的方式 實作了一個簡易的算式計算機,現在我們要開始使用工具來作同樣的事,比較看看手工和使用工具有什麼不同的差別。首先要介紹的就是lex&yacc。 lex & yacc lex(Lexical Analyzar)及yacc(Yet Another Compiler Compiler)是用來輔助程式設計師製作語法剖析器的程式工具。lex的工作就是幫助我們將輸入的資料文字串流分解成一個個有意義的token,而yacc的工作就是幫我們分析這些token和我們定義的規則作匹配。下圖中所表示的是使用lex及yacc的一般工作流程。 首先看到yacc會讀入一個.y檔案,這裡.y檔案的內容就是我們使用類似(E)BNF語法定義的語法規則,yacc會分析這些語法規則後,幫我們產生可以用來解析這些規則的程式碼,而這個檔案一般名稱預設為y.tab.c,產生的程式碼裡面最重要的一個的函式叫作yyparse。 同yacc類似,lex也會讀入一個.l的檔案,這個檔案裡面定義的是如何從文字流裡解出token的規則,使用的方法是常規表示式(regular expression)。在圖的左側中間我們還可以看到有一個叫作y.tab.h的檔案從yacc產生出來並餵給lex作輸入,這個檔案是yacc根據在讀入的.y檔裡面所定義的token代號所產生出來的一個header,這樣yacc及lex產生出來的程式碼裡面就可以使用共通定義的代碼而不必各寫個的。lex分析過.l檔案後也會產生一個一般預設叫作lex.yy.c的原始碼檔案,裡頭最重要的一個函式叫作yylex。 最後,我們把yacc產生出來的y.tab.c還有lex產生出來的lex.yy.c,以及其它我們自己撰寫的原始碼檔案一起拿來編譯再作連結,最後產生出來的就是一個可以用來解析我們定義的語法的解析器工具。以上是整個lex及yacc的使用流程概觀。 常規表示式 在正式使用lex之前,我們首先來對常規表示法作一個基本的認識。常規表示法是一種用來表示字串樣式(pattern)的中繼語言,就好比前文所介紹的(E)BNF表示式一樣,都是用來描述其它語言的語言,只不過用途不太一樣罷了。 常規表示式使用一些中繼符號(meta-symbol)以及ASCII字元定義字串樣式,以下列出一些常規表示式所使用的符號。 . 表示除了換行字元...