使用linux curses開發控制檯的打字遊戲
1. 關於linux curses庫,不多說,google下便知道,就像當年的TC下的視窗程式庫一樣
2. 事情源於在chinaunix上看到了the4king在c/c++論壇上的帖子:
程式碼的編譯命令是:
# gcc file.c -lrt -lcurses
我的平臺是Ubuntu10.04
3. 先閱讀原始碼,瞭解其思想。
該打字遊戲的實現思想大致如下:
介面如下幾部分組成,topwin(也就是顯示字母下落動畫效果的視窗),numwin(成績框,記錄輸入字元數及命中數)
其它訊號處理倒是簡單,對應了'1'暫停,'2'退出等功能。
主執行緒:
以字串darwscr代表整個topwin的輸出內容
do{
在最上面一行產生若干個字元(數量很少)。然後將新字串重寫回topwin,重新整理顯示
呼叫changekey(),然後檢查checkkey()在location[]記錄的命中字元,並把當前命中位置置成'@',把上次命中位置置成' '
將topwin自第二行開始向下移動一行///注意這個地方沒有加鎖,有執行緒同步問題
}
checkkey()執行緒:
do{
接收鍵盤輸入
在當前darwscr裡,找出命中位置,並記錄到緩衝區中 //有執行緒同步問題
}
4. 觀察其程式碼,發現一些不足之處
第一個地方,新增一個basewin,完全覆蓋原始主視窗,因為發現在不同的終端下,預設原始主視窗的背景不一致,現在用basewin來統一一下,這樣做有些移植性的意思哈。
首先,changekey()函式與函式之間用了不必要的訊號量,完全可以用函式呼叫來代替
其次,每交topwin下移一行(產生動畫效果)時,呼叫changekey()函式一次,實際上最多隻能處理一個input字元,效果就是如果某一行的字元數大於topwin的高度LINES,
那麼很悲劇,你無論鍵盤敲得多快,只能眼睜睜看著這一行落地而毫無辦法。而且原始碼裡只用了一個變數去儲存最近的命中字元,如果你輸入很快,後面就會覆蓋前面的。
解決辦法很簡單:
先建立輸入緩衝區,這裡用了一個迴圈佇列,長度為1000,客觀地講應該不會溢位。
接著,就是改動changekey()的stop()時間,把節省下來的時間用於迴圈讀緩衝區,爭取在有限的時間裡多處理幾個命中的字元。
最後,完成上面這些改動之後,編譯執行,緩衝區的效果是有了,可以在一次動畫效果之間命中多個字元,但問題又來了,發現顯示命中效果有問題,個別字元的命中效果產生了偏離,比如明明是第n行的第12個字元命中,卻顯示成了第n-行的第12個字元命中。檢查了下程式碼,發現是多執行緒的問題。原始碼在checkkey()裡檢查命中字元,並把其位置記錄到緩衝區裡,問題是,如果checkkey()恰好發生在一次動畫效果之前,那麼由於隨後發生了動畫效果,剛剛在checkkey()裡記錄的命中位置就不準確了,於是需要在動畫效果的程式碼中檢查緩衝區,並修正已有資料的偏移。同時,應該保證checkkey()修改緩衝區的程式碼與動畫程式碼互斥。同一時刻只能有一個執行, 不然就混亂了!
可以通過增加訊號量控制來改進這個問題
checkkey()執行緒:
迴圈接收鍵盤輸入
訊號量P操作,進入臨界區
在當前darwscr裡,找出命中位置,並記錄到緩衝區中
訊號量V操作,退出臨界區
主執行緒,對應地改動:
sem_wait(&sem_location);
動畫效果區程式碼
sem_post(&sem_location);
改動後的程式碼如下:
5. 在完成原打字遊戲的改進後,我想再增加功能,把打字遊戲變成單詞練習遊戲,就像一些打字練習的軟體一樣
基本思路是,生成一系列的小視窗,視窗有兩行,一行是單詞,隨機產生,一行空等待使用者輸入
動畫效果還是主執行緒實現,定時迴圈。
當用戶輸入的單詞完全匹配小視窗上的單詞時,視窗消失(不要delwin(),應該回收到一個pool中,以重複使用)
程式碼如下:
這個單詞練習遊戲還有很多地方沒有完善,比如單詞庫隨機功能,計數功能有問題(懶得改了),或者還有bug,但跑起來還像那麼回事,如下圖:
6. 需要說明的是refresh()函式的功能,在這上面犯了不少錯誤:
refresh()就是重新整理主螢幕(預設的螢幕)
a) 程式初始化階段,清除主螢幕,否則有時候程式啟動後,視窗上很多亂碼和不知名的符號!
b) 如果程式裡生成了好幾個視窗,並頻繁操作它們的位置,那麼也需要定時地重新整理主螢幕,否則同樣是視窗上顯示混亂!