2011年12月31日星期六

下棋跨年

今晚在旺踢版遇到一個想下圍棋的版友,跟他下了兩盤,不自覺就跨年了......
他是有在看棋靈王,但是他是把圍棋想得太簡單了。結果兩盤下沒多久就棄局投降了。我也沒學過圍棋、沒背棋譜與定石,但是以前看過布局的書,又以前跟別人下過上百盤,所以會看地勢,但不太會廝殺。

N年沒下過圍棋了,竟然還贏兩盤真神奇。第二盤他還叫我讓十子,所有星位都給他佔了,一開始超難下,沒有地方能發展的,邊下邊直冒冷汗。還好我攻擊能力還是勝他,所以硬搶了一個邊角開始發展,然後他又認輸了。

看來他完全不懂如何用地勢來圍別人。不過要會看地勢也是要下很多盤才看得出來的,由於自己從來沒在圍棋學院那類地方學過,都得用一盤盤一直下來看出規律與經驗,但只要跟那邊出來的一過招,我註定是要敗的。

2011年12月10日星期六

Tcl程式如何寄信

最近寫程式如果透過系統的cron去寄信,他就只能寄送純文字的格式。
如果讓他輸出html的語法,他就真的會「html的語法跟內文」一起顯示出來。

所以如果要寫一個比較漂亮的、有html排版的文件,就需要靠程式內建的寄信函式了。

寄信函式會用到tcllib中的smtp跟mime函式。信件的封裝格式是什麼其實我還不是
很清楚,但就是需要先用mime封裝並加上表頭(header)才行。

因此如果我們要寫一個簡單的寄信程式,語法如下:
#!/usr/sfw/bin/tclsh8.5
package require smtp
package require mime
#先宣告需要的tcllib:smtp跟mime

#1.先用mime格式化輸出字串:
set fmsg "檔案路徑與名稱"
set token [::mime::initialize -canonical text/html -file $fmsg -param {charset "utf-8"}]
#如果::mime::initialize後面接"-file"參數則是去讀取這個檔案,而如果是要讀入程式
#內部變數的字串則是用"-string",如果用了"-string"就需要做編碼轉換轉換成需要的
#輸出格式編碼,才能讓瀏覽器或mail client端軟體立刻辨識出,而不會顯示成亂碼,
#然後還要後續調顯示方式才能正常顯示。而用"-canonical"參數格式化成html文件。
# (以轉成UTF-8為例。因為個人好惡原因,我絕不用Big5的)
set token [::mime::initialize -canonical text/html \
-string [encoding convertto utf-8 $fmsg] \ <==這邊的fmsg是含字串的變數
-param {charset "utf-8"} -encoding 8bit]


#2.用smtp函式寄出信件
::mime::setheader $token Subject "信件主題"
::mime::setheader $token From "信件要顯示的寄件者"
::mime::setheader $token To "信件要顯示的收件者"
::smtp::sendmessage $token -originator "真正要用的寄件者" -recipients "真正會用的收件者" \
-servers "郵件伺服器"
#郵件伺服器的設定要小心,如果沒設定好,信是直接寄丟,並且系統的maillog是看不到
#東西的。因為這隻程式並沒有碰到系統的mail server而直接寄出。
#如果老是收不到信,可能就要開系統的postfix,然後程式指定自己系統的IP來relay信件。


如果想要群組寄信的話,最簡單的方式是寫一個收件者列表檔案,然後用foreach陸續讀入去寄信。底下是自己寫的範例函式:
set MAILLIST "maillist檔案位置"
set MAILSERVER "mail-server.test.com"
set ORIGIN "testgroup@test.com"

set mf [open $MAILLIST r]
set ml [lrange [split [read $mf] "\n"] 0 end-1]
#之所以是end-1是因為用\n切開會多一個空元素

proc logmail { fmsg ml ori ms } {
package require smtp
package require mime
set maillist $ml
set token [::mime::initialize -canonical text/html \
-string [encoding convertto utf-8 $fmsg] \
-param {charset "utf-8"} -encoding 8bit]
foreach i $maillist {
::mime::setheader $token Subject "郵件標題"
::mime::setheader $token From "來源者"
::mime::setheader $token To $i
::smtp::sendmessage $token -originator $ori -recipients $i \
-servers $ms
}
}

logmail $msg $ml $ORIGIN $MAILSERVER


補記html語法的表格樣式是這樣(我最記不住網頁類的語法了):
(兩個獨立的大表格用
切開,底下用宣告,然後一直塞
就可以一直加表格欄位了)

Two tables, side by side, centered together within the page.







ab
12



嘗試插入第二個文字






abc
122





2011年10月27日星期四

2011年8月16日星期二

推廣Scilab



第一次也是最後一次大手筆的推廣Scilab,為它做了光碟,預計在COSCUP當天在社群攤位上發放。
光碟裡面當然是軟體與我們那幾位寫的文件。(其實也想不出還能放什麼)

光碟封面與紙盒的設計當然是就地取材,將現有相關圖資源拼與整理一下,不過看起來還蠻雅觀的,印出來後發現比之前設計的圖搞還耐看,希望大家會喜歡。
如果有人想要這光碟但是COSCUP當天沒辦法去的,可以直接跟我索取喔。 ^^


2011年7月22日星期五

呼叫FFmpeg的轉檔程式

其實這隻程式很早以前就想寫了,但就是懶得學ffmpeg,
後來學會轉音檔後,就順手用Tcl/Tk寫了一隻GUI來執行它。
花了一天寫出來,結果現在一天到晚想上youtube抓各類音樂、影片
下來,然後轉成mp3/ogg來收藏......
自學學會Tcl/Tk後,會寫視窗程式果然很吃香,
某些終端機指令很強,就順手幫它寫視窗介面,
不用依賴別人、或是網路上找半天都是不能build的code,
自己寫還是比較快........

結果我找不到Blog調整頁面寬度的設定,程式碼被壓毀了....... :(
========================程式碼如下============================

#!/usr/bin/wish8.5
###########################前置宣告########################
set ftype1 {
{{全部} {*}}
}

proc scancodec { ffcmd } {
set clist [split [exec -ignorestderr $ffcmd -formats] "\n"]
set ll [llength $clist]
for {set i 0} {$i<$ll} {incr i} {
set line [lindex $clist $i]
set line1 [lindex $line 0]
if {$line1=="E" || $line1=="DE"} {lappend oclist $line}
}
set ocl [llength $oclist]
for {set i 0} {$i<$ocl} {incr i} {
lappend ::ocf [lindex [lindex $oclist $i] 1]
}
.area2.op3 configure -values $::ocf -state normal
}

proc conv {ffcmd ab ar sel inpdir outdir} {
set ::probmx 0
set ::count 0
set par "-ar"
if {$ar==""} { set par "" }
set fls [split [exec ls $inpdir] "\n"]
set ll [llength $fls]
set ::probmx $ll
.area4.prob configure -maximum $::probmx
for {set i 0} {$i<$ll} {incr i} {
set infile [lindex $fls $i]
######檢查檔名異常######
set llf [string length $infile]
set c ""
set infile1 ""
for {set j 0} {$j<$llf} {incr j} {
set c [string index $infile $j]
if {$c==" " || $c=="\[" || $c=="\]" } {
set c "_"
#append infile1 "_"
}
append infile1 $c

}
if {[string match $infile $infile1]==0} {
file rename $inpdir/$infile $inpdir/$infile1
set infile $infile1
}
########################

set outfile [lindex [split $infile "."] 0]
eval "exec -ignorestderr $ffcmd -ab $ab $par $ar \
-i $inpdir/$infile -f $sel $outdir/$outfile.$sel"
incr ::count
update
after 1000
}
}

set ab 192k
set ::ocf ""
set ::probmx 0
set ::count 0

###########################主程式##########################
if { [file exists /tmp/ffcmd]==1 } {
set f [open /tmp/ffcmd r]
set ffcmd [lindex [split [read $f] "\n"] 0]
close $f
}
label .findff -text "轉檔指令位置:(注意:目前只支援ffmpeg)"
label .ffpar -text "ffmpeg參數設定:(留空白則使用預設)"
frame .area1 -relief flat
frame .area2 -relief flat
frame .area3 -relief flat
frame .area4 -relief flat
button .conv -text "開始轉檔" -command {
conv $ffcmd $ab $ar $sel $inpdir $outdir
}
button .exit -text "離開" -command { exit }
pack .findff .area1 .ffpar .area2 .area3 .area4 .conv .exit -side top -fill x

entry .area1.ffpath -textvariable ffcmd
button .area1.bw -text "瀏覽" -command {
set ffcmd [tk_getOpenFile -title "轉檔程式的位置" -filetypes $ftype1]
}
button .area1.ok1 -text "確定" -command {
scancodec $ffcmd
set f [open /tmp/ffcmd w+]
puts $f $ffcmd
close $f
}
pack .area1.ffpath .area1.bw .area1.ok1 -side left

label .area2.opl1 -text "音頻碼率"
entry .area2.op1 -textvariable ab
label .area2.opl2 -text "音頻採樣率"
entry .area2.op2 -textvariable ar
label .area2.opl3 -text "輸出格式"
ttk::combobox .area2.op3 -values $::ocf -textvariable sel -state disabled
grid .area2.opl1 -column 1 -row 1
grid .area2.op1 -column 2 -row 1
grid .area2.opl2 -column 1 -row 2
grid .area2.op2 -column 2 -row 2
grid .area2.opl3 -column 1 -row 3
grid .area2.op3 -column 2 -row 3

label .area3.inpl -text "來源音檔目錄"
entry .area3.inp -textvariable inpdir
button .area3.inpbw -text "瀏覽" -command {
set inpdir [tk_chooseDirectory -title "來源目錄" -initialdir [pwd] ]
}
label .area3.outl -text "輸出音檔目錄"
entry .area3.out -textvariable outdir
button .area3.outbw -text "瀏覽" -command {
set outdir [tk_chooseDirectory -title "輸出目錄" -initialdir [pwd] ]
if {[file exists $outdir]==0} { file mkdir $outdir}
}
grid .area3.inpl -column 1 -row 1
grid .area3.inp -column 2 -row 1
grid .area3.inpbw -column 3 -row 1
grid .area3.outl -column 1 -row 2
grid .area3.out -column 2 -row 2
grid .area3.outbw -column 3 -row 2

label .area4.prol -text "處理進度:"
ttk::progressbar .area4.prob -orient horizontal \
-maximum $::probmx -variable ::count
pack .area4.prol .area4.prob -side top -fill x

2011年7月19日星期二

絕冬城之夜2-澤希爾風暴(Storm of Zehir)

今晚將這個遊戲全破了,但是有一種茫茫然的感覺。雖然這片資料片跟主程式一樣都是4G的光碟,但是卻玩不出主程式那種豐富的劇情,這點讓人很失望。

之前在玩絕冬城2主程式時,最大的樂趣就是超長的遊戲時數,與跟隊友的互動。一次操縱一整個隊伍來冒險的遊戲方式從柏德之門開始一直到現在,都是非常棒的設計。從柏德之門2開始,主角與NPC隊友之間的互動變得很熱絡,也讓人很期待繼續冒險下去又會發生什麼關係。

冰風之谷系列最大的失敗就是組了一個死氣沈沈的隊伍去冒險,所有的角色全部是玩家自己創造。然後又是一場場乾枯又荒涼的冒險。絕冬城一代從主程式到資料片,這個部份一直在改進,隊伍的人數也越來越多。到了二代終於到了巔峰,甚至進入某些場景,還會有各種動畫、其他角色與其他NPC對話後影響整個隊伍的命運。

NWN2 SoZ則走了倒退路線,雖然它的遊戲音樂整個就是柏德之門風,然後開頭、結尾的表現方式也回復到柏德冰風那時代的翻書說故事的方式,有點不習慣但不壞。
不過這版資料片的動畫變的超少(應該說沒有),人物變成了類似冰風之谷的組隊方式,讓遊戲瞬間變沉悶了,雖然後來可以用招募的方式招到其他NPC隊友,但是這些隊友說的話變超少,只像是個死板的戰鬥棋子。

SoZ修改了過去NWN1,2、柏德冰風系列的大場景設計,優點是可以靠者自己搜索就找到許多地點來冒險。但是有個很大的缺點,就是次一級的中型場景量很少,除了絕冬城、薩馬葛、十字路口城堡以外,其他都是一些很小的場景地圖,甚至只有一層,一下就打完。然後大場景的旅途中會有一大堆怪物來騷擾,有時候多到讓人煩死,也是一個缺點。

身為絕冬城系列的遊戲,到了這個資料片只剩下黑湖區可以進入,場景真讓人難堪的少啊。主線劇情短,支線劇情不多,倒是一大堆打起來完全沒味的廢場景一堆......

最後的蛇神殿真的很漂亮,這版資料片的遊戲場景有比主程式細膩很多。但還是有一大堆內部擺設都一樣的旅館與小房子。這種畫面漂亮但不耐玩的遊戲也不是第一次碰到。它應該算第二短的遊戲。(第一短的是灰鷹世界TOEE)

還有一個讓人注意到的是遊戲規則已變了(變成D&D 3.5版),人物生命值低於一定程度會變成昏迷,然後要對他傷勢做處理來穩定,則不會死亡。以前這樣的設計也有在TOEE玩過,所以感覺並不會不適應。

今天全破後感覺BUG變成SoZ最大的特色,玩到途中某個場景開始,主角以外的其他隊員會變成無敵狀態,不曉得是不是註冊、增刪隊伍的BUG。這個BUG就讓人一路贏到最後一關。
最後的蛇人神殿,從頭到尾都用潛入方式溜到最後一層是很好玩的打法,可惜很多房間沒辦法瀏覽到,打完BOSS如果選擇繼續冒險,Sasani跟Volo都離開後,回到十字路口碉堡會找不到他們,甚至找不到退出冒險生涯選項......

2011年6月17日星期五

Solaris如何ADSL/VPN上網

之前ADSL的筆記有貼過,但當時是用別人寫好的client端連線軟體(ADSL1.jar)來上網的。但是最近那個連結被拿掉了,之後在網路上再也找同樣好用的軟體了。只好自己手動作設定了。
去年桌機搬到台北去,之後又重灌,它不見了。上個月買了筆電,一回到家,才赫然想起家裡是ADSL上網,這下糟了......

還好經過一個多月,終於會解這個難題了。
第一步:抓到IP位址(這個不難,網路上文件一大堆)
1.設定ISP設定檔:/etc/ppp/peers/myisp (如果檔名命名為myisp,
之後pppd就是call這個名字,solaris裡已預設有一個範本檔就叫myisp)
sppptun
plugin pppoe.so
connect "/usr/lib/inet/pppoec [網卡名稱(例:rge0)]" # dial into ISP
persist
user "帳號" 名 # my account name at my ISP (例如"12345678@hinet.net)
password "密碼"

#以下按造範本檔原有的即可
remotename myisp # name of the ISP; for pap-secrets
noauth # do not authenticate the ISP's identity (client)
noipdefault # assume no IP address; get it from ISP
defaultroute # install default route; ISP is Internet gateway
updetach # log errors and CONNECT string to invoker
noccp # ISP doesn't support free compression

#加上這一行
usepeerdns # 這是關鍵點,ADSL要連出去跟DNS有很大的關係。

2.設定連線的網卡名稱:echo "rge0" > /etc/ppp/pppoe.if
3.帳號密碼檔:/etc/ppp/pap-secrets、/etc/ppp/chap-secrets裡面加入帳號密碼。
網路對於帳號密碼到底是放在什麼地方才有效果是眾說紛紜,全都加入即可。
4.進行連線:pppd debug updetach call myisp
加上debug參數,才比較能看清楚執行到哪個步驟。這時即會抓到IP位址。

第二步:如何連上網路(最難的在這邊)
從之前抓到IP之後,這時ifconfig -a已經可以看到自動產生了一個介面sppp0:
lo0: flags=2001000849 mtu 8232 index 1
inet 127.0.0.1 netmask ff000000
rge0: flags=1000843 mtu 1500 index 2
inet 0.0.0.0 netmask ffffff00 broadcast 255.255.255.255
ether fa:73:4b:b:7f:16
sppp0: flags=10010008d1 mtu 1492 index 3
inet "A IP" --> "B IP" netmask ff000000
ether 0:0:0:0:0:0

這時候是沒辦法ping出去的,ping的到A IP,但是ping不到B IP。
然後卡在這裡好幾個月(包含VPN也一樣,凡是A IP --> B IP這種形式的)
不論routing table、netmask怎麼改,就是ping不出去。
偏偏網路上的教學都只到這裡為止,他們就能上網了。

後來終於知道答案了,關鍵點在之前那行usepeerdns,
加了那行pppd call myisp時,最後會多兩行DNS的IP,
這兩個IP跟B IP同網域,把這兩個IP加到/etc/resolv.conf
這時B IP就ping的到了,連同整個WAN都能ping的出去。
雖然感覺很匪夷所思,但也想不通究竟為什麼?但這就是答案。

之後會試著用同樣觀念去處理VPN上網試試看。