小松鼠嚇了一跳,有了魔法眼鏡後,這世界看起來完全不一樣了

顯示具有 programming 標籤的文章。 顯示所有文章
顯示具有 programming 標籤的文章。 顯示所有文章

2014年12月19日 星期五

Leap motion 擴增實境


利用 OpenCV 校正攝影機以及 Leap Motion Sensor 的位置,再利用 Three.js 來達成擴增實境的效果。

這是試玩的結果(還是跟 IPython notebook 一起探索),所以並沒有以最佳效果為目標。實際上 leap motion sensor 可以放在比較好的位置。

稍早拿來和 google hangout api 配合的效果。

原理上不難,只是中間要解決一堆技術上的小麻煩。

2013年8月5日 星期一

[COSCUP2013] Python, F#, Golang and Friends 投影片


我這次在 COSCUP 演講的投影片。


[COSCUP2013] Python, F#, Golang and Friends from weijr

當天因為時間改變,差了十分鐘,所以沒有把重點表達完整。

我主要想說的是,大多數想在 Python 做的邏輯流程,你都能直接在 F# 裡面用類似的思考流完成(Scala 也是)。
例外是 F# 中迴圈沒有 break,沒有 return,也沒有多重繼承。但習慣後就知道如何轉換。
反過來, F# 有了一些思考單元,在 Python 中則要被迫展開。
Go 則完全不同,如果你用 Python 的思考方式常常會不容易實作。通常你也不應該這樣做(雖然我之前就這樣做,但能做不表示要做,hack 本來就是要嘗試不該做的事)。
Go 的思考流跟 C 比較像,而且拿 C 的想法來寫 Go 會很輕鬆,因為可以寫得更簡潔。當然原本就是用 C 的想法寫 Python 的人就沒差(用 C++ 想法的人有差,物件導向)。


所以當你只常用一種語言的時候,思考其實是被限制住的。更可怕的是,你根本不會察覺這一點、根本不知道你失去什麼。

就像我在演講中舉的例子, 那些語言中只有絕對方位(東南西北),而沒有相對方位(前後左右)的人,有著絕佳的方位感。
一般語言習慣用相對方位的人,根本不知道自己失去了什麼。
(又如馬戲團被圈住的大象那個故事,或者語言中顏色名稱的種類)
所以不只要追求好的語言,有時甚至要刻意嘗試一些看似無用、怪異語言。
 

2013年4月25日 星期四

Go 1.1 達到類似 Python 的 map 及 list comprehension


Go 1.1 的 reflect 新增了 SliceOf, ChanOf,以及 MakeFunc。
SliceOf 及 ChanOf 可以達到類似於 Python 裡面的 list 及 iterator 的功能,MakeFunc 可以搭配 Call 使用,達到類似 decorator 的功能。
以下是簡單測試了一下 Python 的 map 及類似 list comprehension 的效果。


2012年7月8日 星期日

PDF 簡體/正體轉換 github 及 TODO


前篇所說, 這也許是你第一個能找到的 pdf 簡體/正體轉換器,source code 已放上 github 了:https://github.com/tjwei/translatePDF

影片可以看看大致上的效果。

2012年7月6日 星期五

PDF 簡體/正體轉換

 
網路上的簡體資源很多,圖書館中很多中文電子書也都是簡體的。雖說有時直接閱讀簡體不影響,但總是有些時候,我會比較想用正體字來閱讀。當然,也有不少人有正體轉簡體的需求。
文字檔的轉換不是什麼問題,但如果你上網搜尋 PDF 簡體/正體轉換的方式,得到的結果大概會是「先轉成 DOC/HTML,然後轉碼,最後再轉回 PDF」。
這是一般所謂的電腦專家可以給你的最好答案了。 但你是程式設計師,你是 Python 玩家,所以這個答案對你不夠好。

2012年6月22日 星期五

Javascript SNES Emulator WIP


這是一個可以在瀏覽器裡面跑的超任模擬器。
基本上是把修改過的超任模擬器 SNEM 經由 emscripten 轉換,所得到的 javascript SNES 模擬器。
以前的 jsnes 只是 NES 紅白機的模擬器,不是超任模擬器。這個大概是第一個。
目前算是半成本,還沒有聲音,效能還有待加強。但如果你的電腦還算新,那大概能在上面玩玩 Super Mario World。
桌機上的 Chrome,Firefox,Opera,Safari 都能跑。
我的 Xoom 上的 Firefox 及 Chrome 也都能跑,手機上的 firefox 也能跑,只是速度極慢,而且可能需要實體鍵盤才能控制。
ipad 2 上測試是無法玩,還要看看原因。

比較成熟以後,將會釋出原始碼和編譯過程。
下面先提供兩個暫時的連結,供測試。

http://www.b81.org/~tjw/smw
Super Mario Wrold,開始時會讀取一個 512K 的檔案,所以先稍待數十秒。

http://www.b81.org/~tjw/xnes
可以跑一些 public domain  的 rom。開始時會有一個 10M 的檔案下載,請耐心等候 1 分鐘。

操作方式為 方向鍵,C,D(start, select) S,X,A,Z(ABXY 按鈕)


2012/6/27 更新:
ipad2 上的 safari 可以跑了,雖然非常慢。其實只是因為有個沒用到的 Float64Array Heap,而 iOS Safari 不支援 Float64Array,很快的 sed 一下修掉就可以跑了。Android 4.0 的內建瀏覽器也能跑了。
速度上來說, xoom 上的 firefox beta(能在 tablet 上跑的舊版),Android 4.0 內建瀏覽器及 ipad2 上的 Safari 速度不相上下,都被 Xoom 上的 Google Chrome 海放。 Opera Mobile 還是無法正常執行。

2012/9/1 更新:
新網址
http://tjwei.github.com/xnes/

2011年7月13日 星期三

OpenNI + F# 3d mesh Test


修改之前的程式,將 points cloud 改成簡單的 mesh,配合 image texture。效果似乎好點。
程式碼中用到 F# powerpack 的 PSeq 和 Array.Parallel,簡單的利用平行處理加速。
執行檔和 source 下載
執行時需要 .Net 4.0
OpenNI (只要 binary 和 Hardware binary)
Sensor Kinect Driver

2011年7月12日 星期二

用 F# 和 Kinect +OpenNI 產生 Point Cloud

這和前一篇 用 F# 和 Kinect SDK 產生 Point Cloud 是幾乎一樣的程式,只不過改用 OpenNI 而不是用 Kinect SDK beta。 OpenNI 支援將色彩和深度資訊重合,所以出來的結果比之前手工亂搞的正確得多。但是似乎沒有 Kinect SDK 的 ready callback 機制,所以用 background worker 取代。
OpenNI 似乎也不支援 80x60 的解析度。

2011年7月11日 星期一

用 F# 和 Kinect SDK 產生 Point Cloud


本文附的 F# 程式碼,是用 Kinect SDK 來產生 "points cloud"。
程式的效果如影片。
過程中的一些紀錄:
  • 因為之前的 visual studio 2010 過了試用期限,所以我有一段時間沒用 F#, 稍有一點生疏,但這問題不大。問題是我沒有寫過 .net 上的 GUI 程式,更別說是 3D 繪圖了,所以花了一點時間在網路上找範例。 F# 的 GUI 參考資料似乎不是很多,很多似乎也不太靈。
  • Kinect SDK 和 driver 簡單好用,骨架判斷似乎比之前 OpenNI 容易(沒有打錯字,這不是用來判斷股價的軟體),在狹小空間(像是大多數寫程式的地方)就能辨認出骨架。
  • 根據 Wikipedia,Kinect 的 depth sensor 橫向角度 57度,垂直角度 43度。
  • 網路查詢結果,System.Media3D 似乎無法直接畫出「點」,沒辦法做出 point cloud。所以用 squares cloud 或者 cubes cloud 來代替。
  • 我的筆記型電腦 GPU 太差,最多只能跑 80x60 的 cubes cloud。所以影片中解析度很差。
  • 設法將 Video Image 和 Depth Image 組合,發現不吻合。理論上應該要用 Color in Depth Space 或者 Depth in Color Space 的參數來設定 Kinect,但是網路上查詢的結果是, Kinect SDK Beta 還不支援這些。
  • 也許應該改用 OpenNI,但是程式碼已經寫了一半了,繼續用手工的方是將色彩和深度組合。
  • 色彩和深度的圖形居然是鏡像(x軸方向相反)?應該是我哪裡搞錯了。
  • 由於沒有硬體規格,解析度太低也很難實驗,所以用觀察和嘗試錯誤法很粗糙的將色彩資訊以及深度資訊勉強組合在一起。
  • 之後改用 OpenNI 試試看。

2008年6月17日 星期二

Project Euler 198 題

Project Euler 的 198 題。簡單的說,要算出分母在 10^8 以內,大小在 0~0.01 之間的「混淆數」總共有幾個 。而所謂的混淆數 x,是指如果對於某個 m,分母不超過 m 的「最近似 x 的分數」不只一個時,就稱 x 為混淆數。比方對於 x=9/40 來說,m=6 時, 1/5 和 1/4 都是分母不超過 6 最近似 x 的分數。
這個題目其實如果沒有任何基礎知識的話,還不太容易,所以解出的人不多。但站在一些基礎知識之上, 10^8 這種數量級是很容易用程式來秒殺的。據說 Knuth 的 The Art of Computer Programming 裡面就有提供相關的演算法。
不過使用 Haskell,可以很直接的用遞迴解出:

f a b l m | a*b>l || (a==1 && b > m)  = 0
         | otherwise = (f a c l m)+1+(f c b l m) where c=a+b
main = putStrLn $ show result
      where result= (f 1 100 l2 m) + 49+l2-m
      l = 10^8
      l2 = l `div` 2
      m = floor (sqrt (fromIntegral l2)
七行就簡單解決了。如果你知道背後的數學,上面的程式碼其實是很直接的。但用 Python 解就麻煩了,因為上面這個遞迴解超出 Python 的遞迴深度。可以用迴圈來取代遞迴,如下面的演算法:


def p198(M=10**8, z=100, cnt=0):
 M2=M/2
 a=m=int(M2**0.5)
 stack=range(z,m)
 while stack:
     b=stack[-1]
     if a*b>M2:
         a=stack.pop()
     else:
         cnt+=1
         stack.append(a+b)
 return cnt+M2-z/2
(程式碼我從 Project Euler 直接貼過來,排版有點亂掉)
這個演算法是我參考別人的解答後寫出的,配上 psyco 後,其實還挺巧妙也挺快的,但不太容易讀懂,要跟蹤一下堆疊是怎麼跑的。
但有些簡單的工作, Haskell 寫起來又很累贅。比方有一個整數 n,想找出它的根號的整數部份,如後印出來,在 python 來說再簡單不過的
print int(x**0.5).
Haskell 的作法要先將整數轉成浮點數,開根號,然後再用 floor 轉成整數,最後再用 show 轉成字串才能印出。所以是
putStrLn.show $ (floor . sqrt) (fromIntegral n)
如果有許多浮點數與整數,那更複雜一點的運算就要轉來轉去。雖說這樣可以強迫正確的設計風格,但有時只是簡單的小計算,這樣也未免太累了。
果然 Haskell 能將複雜工作簡單化,簡單工作複雜化。

2008年6月12日 星期四

程式設計語錄

圖片來源: wikipedia
(選自 DevTopics 整理的語錄)
(用來讓自己看起有學問的)
  • "Simplicity, carried to the extreme, becomes elegance."  簡單到極致即為優雅。
    – Jon Franklin
  • "The best way to predict the future is to implement it."  預測未來的最佳方式就是實做它。
    – David Heinemeier Hansson
  • "Make everything as simple as possible, but not simpler."   儘量讓事情簡化,但別太簡化。
    – Albert Einstein
  • "The difference between theory and practice is that in theory, there is no difference between theory and practice."  理論和實務的差別在於,理論上,理論和實務沒有分別。
    – Richard Moore
  • "The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge."  知識的的敵人不是無知,而是知識的假象。
    – Stephen Hawking
  • "密碼就像內褲一樣: 不要讓別人看到,要常常更換,而且,別和陌生人共用."
    – Chris Pirillo
  • "Computers are like bikinis. They save people a lot of guesswork." 電腦就像比基尼,幫我們省下許多猜測的麻煩。
    (Sam Ewing)
  • "Come to think of it, there are already a million monkeys on a million typewriters, and Usenet is nothing like Shakespeare."  其實想想,現在已經有上百萬隻猴子坐在上百萬台打字機前了,但網路論壇一點也不像莎士比亞。(現在可用來描寫部落格)
    (Blair Houghton)
  • "Hardware: The parts of a computer system that can be kicked." 
    (Jeff Pesis)
  • "Web Services are like teenage sex. Everyone is talking about doing it, and those who are actually doing it are doing it badly." (網路服務) 
    – Michelle Bustamante
  • "The best way to get accurate information on Usenet is to post something wrong and wait for corrections."
    – Matthew Austern
程式師的幽默
  • 問:為什麼程式設計師是分不清萬聖節和聖誕節?
    答:因為 Oct 31 = Dec 25.
  • 一個男人在吸煙,他的女朋友很生氣的說:"Can't you see the warning on the cigarette pack? Smoking is hazardous to your health!" 男人回答說: "I am a programmer. We don't worry about warnings; we only worry about errors."
  • "I think Microsoft named .Net so it wouldn't show up in a Unix directory listing."
    (Oktal)
  • "C++ : Where friends have access to your private members."
    (Gavin Russell Baker)
  • "Computers are getting smarter all the time. Scientists tell us that soon they will be able to talk to us. (And by 'they', I mean 'computers'. I doubt scientists will ever be able to talk to us.)"
    (Dave Barry)
  • "I've noticed lately that the paranoid fear of computers becoming intelligent and taking over the world has almost entirely disappeared from the common culture. Near as I can tell, this coincides with the release of MS-DOS."
    (Larry DeLuca)
  • "It was a joke, okay? If we thought it would actually be used, we wouldn't have written it!"
    – Mark Andreesen, 談 HTML 的 BLINK 標籤

程式師與使用者
  • The three most dangerous things in the world are a programmer with a soldering iron, a hardware engineer with a software patch, and a user with an idea. - The Wizardry Compiled by Rick Cook
  • "If you think your users are idiots, only idiots will use it."
    – Linus Torvalds
  • "From a programmer's point of view, the user is a peripheral that types when you issue a read request."
    – P. Williams
  • "Computers are good at following instructions, but not at reading your mind."
    – Donald Knuth
  • "There is only one problem with common sense; it's not very common."
    – Milt Bryce
  • "Your most unhappy customers are your greatest source of learning."
    – Bill Gates
  • "There are only two industries that refer to their customers as 'users'."(販毒業與軟體業)
    (Edward Tufte)

  • "Any fool can use a computer. Many do."
    (Ted Nelson)
  • "That's the thing about people who think they hate computers. What they really hate is lousy programmers."
    (Larry Niven)
  • "Just remember: you're not a 'dummy,' no matter what those computer books claim. The real dummies are the people who–though technically expert–couldn't design hardware and software that's usable by normal consumers if their lives depended upon it."
    (Walter Mossberg)
  • "Software suppliers are trying to make their software packages more 'user-friendly'… Their best approach so far has been to take all the old brochures and stamp the words 'user-friendly' on the cover."
    (Bill Gates)
  • "There's an old story about the person who wished his computer were as easy to use as his telephone. That wish has come true, since I no longer know how to use my telephone."
    (Bjarne Stroustrup)
程式設計
  • (程式註解)"Commenting your code is like cleaning your bathroom — you never want to do it, but it really does create a more pleasant experience for you and your guests."
    – Ryan Campbell
  • "Low-level programming is good for the programmer's soul."
    – John Carmack
  • "A program is never less than 90% complete, and never more than 95% complete."
    – Terry Baker
  • "Manually managing blocks of memory in C is like juggling bars of soap in a prison shower: It's all fun and games until you forget about one of them."
    – anonymous Usenet use"
  • "Standards are always out of date. That's what makes them standards."
    – Alan Bennett
  • "There's no obfuscated Perl contest because it's pointless."
    – Jeff Polk
  • "Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' Now they have two problems."
    – Jamie Zawinski
  • "You can't have great software without a great team, and most software teams behave like dysfunctional families."
    (Jim McCarthy)
  • "There are only two kinds of programming languages: those people always bitch about and those nobody uses."
    (Bjarne Stroustrup)

2008年6月1日 星期日

Euler 計畫和其他

前一陣註冊了 Project Euler,然後玩了一下裡面的題目。
Project Euler 蒐集了一系列的挑戰問題,這些問題的答案都是一個數字或者一組數字。每當你破解一個問題後,你就能與其他同樣破解這個問題的人,討論答案與心得。通常解決這問題需要一點程式設計技巧和數學知識。
網站裡面也會對於不同國家或者程式語言的解題者做統計和排名。比方說這是台灣的統計,而這是 Python 語言的統計。和「點點點」相比,台灣的表現很爛。
一般所謂的 online judge 系統其實很多,ACM 風格的 online judge 是主流。 UVa online judge 是其中的代表。其他還有如ZeroJudge
和 Project Euler 不同, Project Euler 上傳的是答案,而這類 online judge 要你上傳的是「程式」。所以有限定使用的程式語言。標準的語言是 C,C++,Java。雖然我也寫 C++和C,但我還是比較喜歡能自由選擇工具。這點到不是根本性的問題,像是 Sphere Online Judge 就能讓你使用許多不同的程式語言,包含 perl, ruby, python。
另外一個不同點是時間限制,雖然 Project Euler 也有所謂的「一分鐘法則」,就是程式應該要能在一分鐘跑完,但這不是硬性規定,真正的裁判是你自己。事實上,如果你喜歡(而且可以的話),你甚至可以 用google 找答案。真正的裁判是你自己。我喜歡這種風格。
一般的 online judge,有些題目有時間限制。就比賽而言,這提供競賽公平性的基準。但這個時間限制沒有「理論上的定義」,端看裁判主機端執行的時間而定。這缺乏了一點美感。
另外就是題目取向,Project Euler 的題目有時會需要一點數學知識,而一般程式競賽有時則是要一點演算法上的知識。而 Project Euler 容許的執行時間往往比較長一點。
總之,雖然都是程式挑戰題,但是風格和取向各有不同。
另外一種類型是 Code Golf,著重在用最短短的程式碼達成指定的功能,就像是打高爾夫一樣,你想要用最少的桿數完成比賽。雖然也是程式解題競賽,但又另有一番風格。
當然,寫程式的目的在於寫出「真正有用」的東西,而不是解答這些問題來炫耀自己有多強。但是,不管是哪一種風格的程式解題競賽,在你解題的過程,都能增進自己的程式設計能力。比方 Code Golf 裡面,有些解答是不可思議的短,在你追求這個目標的同時,才發現原來有些語言特性是自己之前忽略的,某種問題原來還有這種寫法。
(對 Project Euler 有興趣的朋友,可能也會對 IBM 的 Ponder This 有興趣。)

2008年4月25日 星期五

Python 的 Generator Tricks

我們都知道 python 的 iterator很有趣,比方下面這個generator 能生成所有質數(根本就只是把質數的定義講一遍而已):

(n for n in itertools.count() if n>1 and not any(n%k==0 for k in range(2,n)))
比較複雜一點的費波納契數列 callable iterator
iter((lambda x:x.append(sum(x)) or x.pop(0)).__get__([1,1]),None)

我們也知道新的 send/yield 能夠玩出不少把戲,不過這篇簡報簡直把 generator 玩得爐火純青 Generator Tricks for System Programming

generator 根本就能夠當成 pipeline 來玩。

比方你有一組 log,最後一個欄位不是數字就是 ' -'(當成 0),你想算這些欄位的總和,原來你可能會寫成

wwwlog = open("access-log")
total = 0
for line in wwwlog:
bytestr = line.rsplit(None,1)[1]
if bytestr != '-':
total += int(bytestr)
print "Total",total
但用 generaotr 來思考
wwwlog = open("access-log")
bytecolumn = (line.rsplit(None,1)[1] for line in wwwlog)
bytes = (int(x) for x in bytecolumn if x != '-')
print "Total", sum(bytes)

優雅多了,而且從 pipeline 來看,反而更容易理解。

配合另外一個也很神的 generator_tools ,就能玩出更多把戲。

2008年3月5日 星期三

中文程式語言

每次一提到中文程式語言,常常會看到人批評「中文程式語言」不實用,浪費時間。

「程式裡面用的英文很簡單啦,不會造成困擾」

「打英文還比較快」

沒有錯,對「一般人」來說,也許的確不實用。

大部分的人都把自己當成是「一般人」(常常也沒有錯),但是忽略掉自己前面和後面都還有人。

同樣的那群「一般人」,他們會偏好中文的文件和軟體,也會感謝翻譯成中文化的作者。

如果單從我自己的角度來看,中文化根本沒必要,浪費時間。因為「文件中用的英文很簡單,不會造成困擾」、「直接看英文還比較快」。

而我的英文還很爛呢。

如果連我都覺得中文化翻譯都是沒有意義、浪費時間,更何況是對英文程度正常的人來說?

還好,不是每個人都只從自己的需求來看世界。

那些想磨練英文而翻譯的人不談(其實翻譯品質堪慮),中文化的工作者本身往往不需要這些中文化文件和軟體的,他們是為了別人而做的。

「一般人」的確需要感謝那些翻譯者,感謝他們不像「一般人」一樣,只因自己不需要,就認為浪費時間、不實用。

很明顯的,發展中文程式語言的人,也不是因為自己需要才發展的。

前幾天到我家裝潢,弄電路、音響的人,有些甚至連 In, Out 這麼簡單的字, 都不是很確定。以前當兵的同袍,英文的程度也是遠低於我的預料。他們才是真正的一般人。

但我認為他們其實都很聰明,如果有適當的環境,如果程式可以用中文寫,他們也許就能學會寫程式。

這是我關心中文程式語言的初衷。即使遠在當兵之前,我還過份高估一般人英文程度的時候也一樣。

因為我知道,這個世界很大,有很多不同的人,很多狀況會出乎我預料。

比方,我也實在不知道 OOPS 開放課程計畫的中文翻譯到底要給誰看。

我無法想像能有多少人能學習高等課程,卻看不懂英文。我無法想像有人需要這套計畫。

我反而能想像那種因為是中文,而望文生義誤解的人(推翻相對論,滿口量子能量場、證明尺規三分角的那類人)。

但同樣的,我知道世界上的人有千百種,如果要我下注,我還是會站在有人需要那邊。

2008年3月2日 星期日

用 30 行 ruby 寫俄羅斯方塊

學一種程式語言,第一步是先跟著 Tutorial 走一遍。
然後,找到參考手冊,因為,這是你的救生圈。像是 Python 的文件就寫得相當清楚。
所以,我無法理解那些在網路上問一些官方文件就有答案的問題的人。如果一個人看不懂文件上那麼清楚的文字,那表示他閱讀能力有問題。那他又怎麼能夠讀懂網友回答的文字?
現在學 ruby,文件風格和 python 有很大不同,但是也非常清楚有用。
當然,光是這樣還不夠,還要真的寫幾個程式才能算數。
至於要寫哪些程式呢?每個人都有自己喜好。就我來說,以前,每學一種 GUI framework 或者語言,我會寫一個 Ansi Terminal Emulator。更早一點,還沒有 GUI 的時候,碰到新的系統,會寫個簡單的畫圖程式和簡單的中文系統 Viewer。
這有點像是一種儀式,如同成年禮一樣,你學程式語言的成年禮。寫完這一個中小型的計畫,就能說自己會一種語言了。
不過我學 Python 的時候,倒是沒有什麼成年禮。因為 Python 實在太好學了,差不多是看到就會了。勉強要說的話,就是之前那個 Alpha-Beta Search 系統。
但這也就是為什麼我 Python 的程式設計能力不太紮實的緣故。後來用 Python 寫了一些短的的俄羅斯方塊,才體會到這點。
所以,現在我學 Ruby 時,也打算把俄羅斯方塊當成是成年禮的一部分。以下就是用 ruby 寫的 29 行俄羅斯方塊。


require "tk"
W,H,Blk=40,40,{0xf=>"red",0x2e=>"#0f0",0x27=>"blue",0x47=>"#ff0",
0x66=>"#0ff",0xC6=>"#38f",0x6C=>"#f0f", 0=>"#000"}
Bkeys=Blk.keys.select{|x| x!=0}
new_piece= proc{ proc{|pc| {:a=>(0..16).select{|i|(pc>>i)&1>0}.map!{|i|
[(i>>2)+1,i&3]},:i=>3,:j=> -2,:c=>pc}}.call Bkeys[rand(Bkeys.length)]}
def collide(na,ni,nj)
na.map {|n| true if @board[nj+n[1]][ni+n[0]]!=0}.include? true
end
def redraw
@canvas.delete "all"
@board.each_with_index{|b,j| b.each_with_index {|c,i|
TkcRectangle.new(@canvas, i*W, j*H, i*W+W,j*H+H,
'fill'=>Blk[@p[:a].include?([i-@p[:i],j-@p[:j]])? @p[:c]: c])}}
end
@score, @bw, @bh, @p = 0, 10, 20, new_piece.call
@board=Array.new(@bh+3){|j| j!=@bh ? [0]*@bw+[15]*3:[15]*(@bw+2)+[0]}
(@canvas=TkCanvas.new :width=>@bw*W,:height=>@bh*H).pack
TkRoot.new.bind("Any-Key") { |e| na,ni,nj=@p[:a],@p[:i],@p[:j]
ni+= e.keysym=="Left" ? -1:(e.keysym=="Right" ? 1:0)
na=na.map{|n| [n[1],3-n[0]]} if e.keysym=="Up"
nj+=1 if e.keysym=="Down"
@p[:a],@p[:i],@p[:j]=na,ni,nj unless collide na,ni,nj
redraw}
TkTimer.new(200,-1){|e| !(collide(@p[:a],@p[:i],@p[:j]+1) or !(@p[:j]+=1)) or
proc{ @p[:a].each{|n| @board[n[1]+@p[:j]][n[0]+@p[:i]]=@p[:c]}
@board=@board.inject([]){|a,l| l.include?(0)? a+[l]:[[0]*@bw+[15]*3]+a}
@p[:j]<0 ? exit : @p=new_piece.call}.call
redraw}.start(0).mainloop
這個比 python 的 tk 版要短,不過可讀性差多了。
原來寫的可讀性比較高,最開始甚至還有定義一組 class,不過行數大約是五十行。
後來不用 class,就變成 40行以內,然後再進一步犧牲一點可讀性,就降到了三十行以內。
ruby 高手可能可以找到不少以上程式碼的可笑錯誤吧!
ruby 和 python 的比較一直是個熱門話題,不過呢,我得先要等我更了解一點 ruby 再說。

2008年1月26日 星期六

BBS Lua: 繪圖模式


這主要是對[MMDays專欄] 我們需要開放什麼平台?從PTT Lua與Gaaan談起的回應。但不是針對該篇的主要內容,因為 comments 裡面的許多留言已經夠多了。
我主要只對裡面的拿老遊戲來挑戰 BBS 的多媒體效果來做一些回應回憶。
其實一般傳統的文字終端機早就能夠提供繪圖能力了,比那些老中文遊戲還要早就有了。
先不要其他知識,光是把字體縮小,文字長寬欄位增加,就能有不錯的效果了,這就是所謂的 Ascii-art、aalib 做的事情。就連整部 Matrix 的電影都能在文字終端機中顯示了(其實 mplayer 和很多其他 opensource 的軟體會支援 aalib 的 output,所以你其實可以放任意一部你喜歡的電影),還可以在文字 Terminal 上玩 Quack
除了Ascii Art 外,其實傳統終端機是真的可以畫圖的,參考這個
Graphic GUI Capabilities of Text Terminals
有一堆可以在文字終端機畫圖的增強功能指令集,像是 Tektronix 4014,算是工業標準,就連最正宗的 xterm 也支援。它的繪圖功能可以畫出 12bit 顏色的圖,解析度也足夠。一些標準的 plot 軟體也支援這個指令集。
其他一些終端機軟體,也支援各自的非標準指令集,像是 netterm ,雖然應該沒有繪圖指令集,但是理論上(其實算是安全漏洞)可以辦到更多事情,包含開啟小畫家。
不過古時候,大家都是用 DOS NCSA telnet 連 BBS, xterm 還太高檔,不是大家都能用。我忘了 NACA telnet 是否有支援繪圖指令(根據 wikipedia 是說有支援 Tektronix 4014 ),但不要忘了,當時大家用的是倚天中文系統、國僑、零壹中文系統,這些中文系統是有繪圖指令的,甚至早期的中文遊戲也是利用這些指令來畫圖(不過很多 BBS 會擋這個中文繪圖指令集,至少轉信的時候會)。
這些方法十幾年前就有了。
所以說寫這些沒用的功能要做什麼?
引述 ttyquack 作者的話(回答為什麼要寫這個軟體):
If you have to ask why, you're not a member of the intended audience. Please go on about your business and accept my apologies for this distraction.
為什麼要在 vim, emacs, utorrent, ptt2, word (, 或者漢書)裡面玩 tetris?
Because we can。

2007年11月28日 星期三

壞狗不道德搜尋: Google 不想讓你看的圖片


有善就有惡,有光明就有黑暗,有咕狗就有壞狗。
可能很多人都知道,Google 的圖片搜尋可以設定安全選項,使用者可以選擇「高安全性」、「中安全性」或「無」。依照選項的不同,Google 會把一些他認為不好的色情圖片過濾掉。
但正所謂有光亮就有陰影,在 Google 大神教導我們什麼是合乎道德的同時,祂同時也幫我們把不道德的東西找出來了。
因此,壞狗就誕生了。壞狗依照咕狗大神的判斷,將搜尋的結果分成「道德、不太道德、邪惡」(Good, Bad, Ugly) 三類。
壞狗的英文名字是 Bagle,因為 Google - Good +Bad =Bagle.
有興趣的人,可以到 Bagle 試試看。不過似乎 Google 的人工或智慧還不是那麼強,所以很多判斷很無厘頭,也許你會發現原來自己的照片被 Google 認為不適合讓小孩子看。不管怎麼樣,看看 Google 到底認為哪些圖片是限制級、不想讓你看,也挺有趣的。似乎人名和英文的判斷會比較準一點。
更新:
感謝各位的留言和回應(也包含PTT、黑米、推推王上面的)。
小明建議的名字:「悖骨」也不錯,頗有一點反叛的味道。原本的 More Bad,More Ugly,也改成了問句「More Good? More Bad? More Ugly?」,為善為惡只在各位的一念之間,可以自己決定。


更新:目前因為年久失修,已經無效。


技術細節:
這是一個概念測試,所以很多東西還不完整,比方倒退瀏覽功能鍵還沒有支援,一些搜尋結果資訊也不完整。另外 UI 也不是太好。
雖然「全部-好=壞」這個概念很簡單,但是實際要寫成程式有點麻煩,主要是 Ajax 的技術細節。照例,我不想自己架設主機,所以要用寄生蟲的方式。但 google image 沒有好用的 API(video有 rss,可以配上 google ajax feed api),當然可以用 openkapow, bookmarklet, grease monkey 等方式達成,但這次我試著使用 Google 翻譯來完成。Google cache 應該也可以這樣玩。
基本上,這是把 Google 翻譯當成匿名瀏覽的用法,所以用一般的匿名瀏覽網站也許也能辦到。不過用 Google 翻譯的好處是 Google 太強壯,不會倒,而且一些 Ajax 動作比較直接。

2007年5月30日 星期三

建立 wikipedia 參考

範例:在文章中自動搜尋字詞,然後連到 wikipedia 的解釋。用法很簡單,用 script tag 載入 list_b5.js(charset=big5) 之後, wikify(document.body)就行了。
利用的是 wikipedia 下載的關鍵字資料庫,把裡面一些字丟掉,變成了大約 900k 的 json 資料,所以特別是地一次看的時候,載入會比較慢,最慢可能會到三十秒左右,但是快的話會在五秒左右。
用簡單的編碼,可以把字典檔案縮小到 500多k,但是解開字典檔似乎會花掉更多時間。
特別是 IE ,會造成程式整個卡住,範例在這裡

2007年5月21日 星期一

影片自動連續放映器

影片自動放映器
這個整合了之前的 Youtube download URL JSON , goolge feeds api, google search rss output 這幾項東西,配合了修改過的 Flipvee 做成的。原來是打算弄全螢幕連續播放,但是 haxe 的 flash 9 api 好像還沒有放入 fullscreen 的部份(更新: 新的1.13 版開始支援),可能要配合 flex 2 來弄。
雖然還是玩具級的東西,但是輸入一些東西可以看到連續的影片,不會被中斷,也不用出現 youtube 的字樣或者每一段影片都會切換全螢幕。
只要輸入適當的關鍵字,就能比較完整的看到一些影片。
比方說
"team medical dragon" ep11
"high stakes poker" "episode 3"
流星花園
"the real hustle"
辣妹掌門人
狗狗猩猩
cyril "magic revolution"
極限體能王 2007
火影忍者 207
世界奇妙物語
penn teller bullshit death inc pt
等等

2007年4月27日 星期五

Tetris Program in 12 lines

前幾天看到這個用 100 行 python 寫的 Tetris ,對一個小程式來說, 100 行實在是太長了,直覺上覺得不需要這麼長的程式來寫 Tetris。
我覺得大概 50 行左右吧,前提是要某種程度的 readable,就像那個 100 行的程式一樣。
經過修改之後,改出了 30 行的程式,程式如下:
import sys,random
from pygame import *
score,bw,bh,tickcnt,TICK=0,10,20,0,USEREVENT+1
blk={0xf:0xff0000,0x2e:0xff00,0x27:0xff,0x47:0xffff00,0x66:0xffff,0xC6:0x7fff,0x6C:0xff00ff}
board=[[0xf if j== bh else 0]*bw+[0xf]*3 for j in range(bh+3)]
new_piece=lambda pc:([((z>>2)+1,z&3) for z in range(16) if (pc>>z)&1],3,-2,pc)
piece,px,py,pc=new_piece(random.choice(blk.keys()))
collide=lambda piece,px,py: [1 for (i,j) in piece if board[j+py][i+px]]
def tick():
global piece,px,py,pc,tickcnt,board,score
keys=key.get_pressed()
npx=px+(-1 if keys[K_LEFT] else (1 if keys[K_RIGHT] else 0))
npiece=[(j,3-i) for (i,j) in piece] if keys[K_UP] else piece #Rotate
if not collide(npiece,npx,py): piece,px=npiece,npx
if keys[K_DOWN]: py=(j for j in range(py,bh) if collide(piece,px,j+1)).next()
if tickcnt%5==0:
if collide(piece,px,py+1):
if py<0: sys.exit("GAME OVER: score %i"%score)
for i,j in piece: board[j+py][i+px]=pc
piece,px,py,pc=new_piece(random.choice(blk.keys()))
else: py+=1
nb=[l for l in board[:bh] if 0 in l]+board[bh:]
s=len(board)-len(nb)
if s: score,board= score+2**s,[board[-1][:] for j in range(s)]+nb
for i,j,c in [(i,j,blk.get(board[j][i],0)) for i in range(bw) for j in range(bh)]:
draw.rect(scr,blk[pc] if (i-px,j-py) in piece else c,((i*box[0],j*box[1]),box))
dummy,tickcnt = display.flip(),tickcnt+1
if init(): scr = display.set_mode((400,800))
dummy,box=time.set_timer(TICK,100),(scr.get_width()/bw,scr.get_height()/bh)
while 1:{QUIT: sys.exit, TICK: tick}.get(event.wait().type,str)()

其實是 32 行,稍微有一點點濫用了一些東西來把兩行縮成一行,湊成整數(tickcnt+1 那行)。但是基本上不使用 ; , add or 之類的東西來把縮短行數,更不要說用 exec/ compile 之類的東西了。上面的東西跟原來那個 100 行的一樣,需要 python +pygame,雖然說 pygame 已經算是標準的 module,但畢竟不是 python 裡面的東西,下面用內建 Tkinter 寫成 33 行的程式
import sys,random
from Tkinter import *
score,bw,bh,H,W=0,10,20,40,40
blk={0xf:"red",0x2e:"#0f0",0x27:"blue",0x47:"#ff0",0x66:"#0ff",0xC6:"#38f",0x6C:"#f0f"}
board=[[0xf if j== bh else 0]*bw+[0xf]*3 for j in range(bh+3)]
new_piece=lambda pc:([((z>>2)+1,z&3) for z in range(16) if (pc>>z)&1],3,-2,pc)
piece,px,py,pc=new_piece(random.choice(blk.keys()))
collide=lambda piece,px,py: [1 for (i,j) in piece if board[j+py][i+px]]
def tick(e=None):
global piece,px,py,pc,tickcnt,board,score
keys= e.keysym if e else ""
npx=px+(-1 if keys=="Left" else (1 if keys=="Right" else 0))
npiece=[(j,3-i) for (i,j) in piece] if keys=="Up" else piece #Rotate
if not collide(npiece,npx,py): piece,px=npiece,npx
if keys=="Down": py=(j for j in range(py,bh) if collide(piece,px,j+1)).next()
if e==None:
if collide(piece,px,py+1):
if py<0: sys.exit("GAME OVER: score %i"%score)
for i,j in piece: board[j+py][i+px]=pc
piece,px,py,pc=new_piece(random.choice(blk.keys()))
else: py+=1
nb=[l for l in board[:bh] if 0 in l]+board[bh:]
s=len(board)-len(nb)
if s: score,board=score+2**s, [board[-1][:] for j in range(s)]+nb
scr.after(300,tick)
scr.delete("all")
for i,j,c in [(i,j,blk.get(board[j][i],"#000")) for i in range(bw) for j in range(bh)]:
scr.create_rectangle(i*W,j*H,(i+1)*W,(j+1)*H,fill=blk[pc] if (i-px,j-py) in piece else c)
scr=Canvas(width=bw*W,height=bh*H,bg="#000")
scr.after(300,tick)
scr.bind_all("<Key>",tick)
scr.pack()
scr.mainloop()

雖然說 33 小於 100,但是其實沒什麼好得意的,畢竟寫 100 行程式的看起來像是個菜鳥。
但寫出來可以驗證我的估計,順便練習一下程式。正當我要把以上兩個程式碼放上 blog 時,發現有人寫出 Tetris in 15 lines。雖然我寫得短的時候不會覺得得意,但是 15 比 30 小很多,心理還是不是滋味,所以就連過去看看他玩什麼花樣。他的程式碼如下:
import random,pygame;R=range;L=len;W=10;Q=H=20;P=pygame;G=255;v=lambda b:['0'*W
]*(H-L(b))+b;a=p=x=y=c=d=0;D=P.display;T=P.USEREVENT;m=lambda i,j:j-y in R(L(p)
)and i-x in R(L(p[0]))and'0'!=p[j-y][i-x]and`d+1`or b[j][i];S=D.set_mode((W*Q,H
*Q));k=lambda p,x,y:1-max(p[i][j]!='0'and(y+i>=H or x+j<0or x+j>=W or'0'!=b[i+y
][j+x])for j in R(L(p[0]))for i in R(L(p)));n=E=b=v([]);P.time.set_timer(T,100)
exec("while E!=P.QUIT:\n E=P.event.wait().type;K=P.key.get_pressed()\n if n:d="
"random.randrange(7);q='111';p=[['1'*4],['010',q],['110','011'],['011','110'],"
"[q,'100'],['11','11'],[q,'001']][d];x=W/2-L(p[0])/2;y=n=0\n if E==T:\n [P.dr"
"aw.rect(S,[(h,g,f)for f in(0,G)for g in(0,G)for h in(0,G)][int(m(i/H,i%H))],("
"(i/H*Q,i%H*Q),(Q,Q)))for i in R(W*H)];t=''.join;z=[t([p[L(p)-1-i][j]for i in "
"R(L(p))])for j in R(L(p[0]))];p,x,y=[p,z][K[P.K_UP]*k(z,x,y)],x-(K[P.K_LEFT]*"
"k(p,x-1,y))+(K[P.K_RIGHT]*k(p,x+1,y)),y+(K[P.K_DOWN]*k(p,x,y+1));B=[t([m(i,j)"
"for i in R(W)])for j in R(H)];e=sum(r.find('0')<0for r in B);D.flip();c+=1\n "
" if c%5<1:\n if k(p,x,y+1)<1:\n if y<1:break\n a+=e and 2**e;n=b=v([r"
" for r in B if~r.find('0')])\n else:y+=1\n");print 'GAME OVER: score %i'%a

這個人的確比那個 100 行的那個人要厲害不少,不過呢,這個程式用到了 exec,然後把所有東西變成字串,餵進 exec 裡,所以是個不太可讀的程式,基本上只要 81*15 個字元內,就能夠變成 15 行了(他的程式長度是 1213)。我看了一下我的程式,字元數是比較多,但是主要是因為變數基本上都還是可讀的緣故,所以稍加修改之後,就能夠讓字元數少於 1100 。但是問題是行數,我還是認為使用 exec 已經犯規了。所以想說在不使用 exec 的情形下,讓行數減少。
這裡,就被迫要使用 ; 來合併行了。但是合併行還是有限制的,因為 if /while/for 等等 block 不能任意合併。所以,我用了一些技巧用 [ ] 來取代迴圈,用其他一些程式碼來取代 if/else。這樣,程式變長了,但是幾乎就能把所有的行數縮成一行(如果沒有字數限制)。
在一行是 80 字元的限制下,我寫了 15 行的程式如下(處理落下和 16 的那個相同,和100 不同):
from pygame import *;R,T,e=range,USEREVENT+1,[1];W,H=10,20;E={0xf:0xff0000,0x2e:
0xff00,0x27:0xff,0x47:0xffff00,0x66:0xffff,0xC6:0x7fff,0x6C:0xff00ff};B=[[0xf if
j==H else 0]*W+[0xf]*3 for j in R(H+3)];S=n=0;import sys,random as C;C=C.choice
O=lambda:(lambda Z:([((z/4)+1,z&3) for z in R(8) if (Z>>z)&1],3,-2,Z))(C(E.keys(
)));P,X,Y,Z=O();L=lambda P,X,Y:[1 for (i,j) in P if B[j+Y][i+X]];d=display;e=[1
];init();F=d.set_mode((400,800));time.set_timer(T,100);l=len
while (0 if d.flip() or e.__setitem__(0,event.wait().type) else e[0])!=QUIT:
if e[0]==T:K=key.get_pressed();U=X+(-1 if K[K_LEFT] else(1 if K[K_RIGHT] else 0
));V=Y+(1 if K[K_DOWN] else 0);Q=[(j,3-i) for i,j in P] if K[K_UP] else P;(P,X,Y
)=(P,X,Y) if L(Q,U,V) else(Q,U,V);n%5 or(L(P,X,Y+1) and((Y<0 and sys.exit("GAME\
OVER: score %i"
%S))or[B[j+Y].__setitem__(i+X,Z) for i,j in P]));(P,X,Y,Z)=(P,X,Y
,Z) if n%5 else(O() if L(P,X,Y+1) else(P,X,Y+1,Z));n+=1;D=[z for z in B[:H] if 0
in z]+B[H:];s=l(B)-l(D);(S,B)=(S+2**s,[B[-1][:] for j in R(s)]+D) if s else(S,B
);[draw.rect(F,E[Z] if (i-X,j-Y) in P else c,((i*40,j*40),(40,40))) for i,j,c in
[(z%W,z/W,E.get(B[z/W][z%W],0)) for z in R(W*H)]]
這樣,算是一個滿意的結果,行數雖然一樣,但是字元比較少,而且程式碼都是直接餵給 python的,沒有經過 exec。如果要使用 exec 的話,那縮到 14 行是沒有問的。
事實上,我還可以縮到 12 行,就像標題上寫的,很簡單,因為 python 內建有壓縮功能,要把程式碼當成字串,那當然可以壓縮。
exec("""eJxtUu9P4kAQ/c5fsV/QXTrUFjg17a3JFaoYUZDfsNmYCltuOWhJ25yC8X+/3QJqzDVp++a9
6XRm9sn1Jk4ylG5TSIJoHq9RkKJ7t07vzdnvWM6EGyaK3GwXwVoguc8uuX066Pldf+g/9A3bHUGT2hZU
LNenb9Zr6Kg7tNQF1mtFHCKNL3KsUG2PDvz5+SFSuK7xxR6f1w/fhuG761HGVIRkiJaUoiYSq1Qgi5dG
huZ5qYrCOEFLJCOkRlkI3DSqhLttugrWz/MATR3MMN6d1Yhhw+6kSvL83Wf+JdHF8fTqakdObA5VKFdg
StwejajldmAME5jSNq5j3/wjtikmhLitY/lcd5idV8USlkRX7uiSHlsaE86kMebclZHMMHGv6Vymm1Ww
NVORPa3jucC4prZxaVmqaibXIhc0SHAfbEUX5iJEXUycAlqs4udghQ49QQQe9Nw7qroyF+qrTSLSVMzV
bwZ0bOCyrbu4Y3dPLf+6z/ebwx9k9/ameWT1z4d0Ynyqjfbo4UN0HynDS6iW5X57asx8Sn7MHnQOuZ2C
pqI4Qy38CAMYEifvlubBXiz+oNRS0+ighXPZsPV4OTP5aTnKl6Z4VQs7vfl176P20O86KJ3FiUBFeVrs
EZ37tRHny67pVKv/Pzel6DYdNDGoXUANynafdvCY08wnsnS044bHmg53U7oSEfZIWb8aJJ8hdXrg0Z5R
KZVSYB4r25w5/LsTU8KNRuHYKMy0wvCuOILd2Qh8fWbYYwpzpkgO6hS+m3NUahKuVzNPghczEbMMX4PP
pnmfWJbHsCxPDpbLD2AGGMtSzYKlehBQ5gL11pMfjReu5EZZJNIbePktVwLZztvj4Lb/sXboO933vDvx
V0SZ+RJo75rZdiMgzRKCSeEfrBs8BQ=="
"".decode("base64").decode("zip"))
不過壓縮之後的東西不是文字,我用 base64 再轉過,才能餵到 python 裡面。所以這樣減肥的效果有限。但是這樣有個好處,因為簡單。原來我是打算用一個 dict 然後對程式碼來做多重替換,這樣可以省下不少空間。比方說裡面用 lambda 定義的一些程式,現在我可以用 re 來對程式碼替換了。簡單的說就是一個簡單的巨集功能。但是用 zip 的方式更聰明,因為手動的建字典建巨集工作,現在由 zip 幫你做了。
當然 base64 是不理想的,其實只要把 \x00 \\ \r 過濾掉,可以直接餵給 python,而且 exec後面也不用放括號,還有很多地方的空格也能減少。
這個程式絕對不會是最短的,我相信如果要這樣搞,十行是絕對可能的,我還是比較喜歡 33 行那兩個可讀的程式。