2007-04-01
■ [Ruby/SDL] フォントの話
Ruby/SDLで文字を表示したいと思ったとき、一番簡単なのはTTFファイルを読み込むことだ。TTFだと サイズを簡単に変更できるし、アンチエイリアスをかけられるから綺麗だし、文字幅も可変なので見た目にも美しい。 いいことづくめだ。
が、TTFにも一つだけ罠があって、それは多くのフリーフォントは無断での再配布を禁じているということだ。 Web上には「非営利なら連絡不要で利用可」というくらいの意味で「自由に使える」フォントはたくさん公開されている のだが、フォントファイル自体の再配布は禁じている(もしくは要連絡)ものが多い。
というわけで、
- (A) 作者に連絡して同梱許可をもらう。
- (B) 再配布自由なフォントを探す。
- (C) TTFをビットマップに書き出してそれを使う。(BMFont, SFont)
といういずれかの手段を取る事になる。(※無断で同梱してしまう、というのは著作権法違反なので却下^^;)
(A)は一番正攻法と言える。もともとフリーで公開されているものだから、きちんと連絡さえすれば大抵の作者は OKを出してくれるだろう。
(B)は、再配布を明示的に許可しているフォントサイトが少ないため結構難しいと思われる。 もしそういうサイトを見つけたら、ぜひフォント/リンクに追加してほしい。
(C)は一般的なゲーム製作で用いられているアプローチであるが、サイズを動的に変更できなくなるため 利便性はTTFに比べると多少劣る。でもまあ一番現実的だろう。
■ [Ruby/SDL] でだ
ワンクリックでBMFontを生成してくれるツールを作ろうかと思ったのだが、 ビットマップフォントを吐くと幅を可変にできないしSFontを吐くと色を動的に変更できない。さぁ困った。
「白黒のSFont」みたいなフォーマットがあればいいのだが…。
■ [Ruby/SDL] 逆引きRuby/SDL
Ruby/SDL Usersに、逆引きRuby/SDLというページを 作ってみました。まだ全然書いてないですけど。執筆者募集中。あとこんな項目が欲しい!とかも。
リファレンスにリンクを貼りたいんですけど、RDだからメソッドが増えるとURLがずれちゃうのが困り者。 メソッド名ごとにURLが固定されるようにできんかなぁ。
2007-04-05
■ [Ruby/SDL] fontmake.rb
TTFファイルからBMFont用のビットマップフォントを生成するスクリプトができた。
使い方
- ruby fontmake.rb hoge.ttf とすると、ttfファイルと同じディレクトリにhoge.ttf.bmpが出来ます。
- ruby fontmake.rb c:/home/fonts とすると、指定ディレクトリ以下の全てのttfファイルについてbmpを生成します。
- -s 20 とか付けるとサイズを指定できます。
(5/4追記:最新版はこちらへ)
■ [Sup] Supを適当に日本語対応してみる
Matzにっきでも紹介されていたメールクライアントのSupだが、 ちょっといじるだけで日本語のメールも表示できるようになった。
- 適当なパッチ (sup-0.0.8, EUC専用)
Supは「Rubyで書かれたコンソール版GMail」とでも言うべきもので、以下のような特徴を備えている。
- コンソールで動く。インターフェイスはncurses (emacsではない)。 色つき。フルキーボード操作。エディタは好きなものを設定できる。
- フォルダの概念がなく、検索とラベルを使う(GMail方式)。検索エンジンにはferretが使われている。
- メールをスレッド単位でまとめてくれる。引用やシグネチャを折りたためる(GMail風)。
- フルRubyなんで拡張が簡単。いじり回せる。
- インストールは gem install sup と非常に簡単。
初期設定はsup-configで対話的に進められる。 取り込めるメールソースはmbox, Maildir, IMAP, mbox+ssh (将来的にはGMailにも対応したいとのこと)。 取り込むと言ってもSupはindexを作るだけで、メールデータそのものをコピーすることはない。
ちょうど「コンソールベースの、レスポンスの速いGMailみたいなもんないかなー」と思ってたところなので、Supにはとても期待しています。
2007-04-08
■ [web] ll.jus.or.jpのサイトが酷い
- 2006 LL Ring: 403 Forbidden
- 2005 LLDN: 激しく文字化け(utf8らしい)
- 2004 LL Weekend: 激しく文字化け(eucらしい)
- 2003 LL Saturday: DirectryIndexが見えてる
どうしちゃったんだぜ…?
■ [prog] 0から99までのランダムな数値を、指定した数だけ小さい順に並べて表示する
(人力検索はてなの回答を書いていた…んですが、途中で誤読に気付いてボツに。)
プログラミングの基本的な要素を含んだ例題として、「0から99までのランダムな数値を、指定した数だけ小さい順に並べて表示する」 という問題を考えてみました。
- 入出力
- 文字列と数値の変換
- 乱数
- 配列(リスト)
- ソート
あたりがポイントでしょうか。
○○はもっと簡潔に書けるよ!というのがあればお教えください。
Ruby (ポイント:ブロック)
puts Array.new(ARGF.read.to_i){ rand(100) }.sort
Python (ポイント:リスト内包表現)
import sys import random count = int(sys.stdin.read()) for n in [random.randint(0,99) for x in range(count)]: print n
Haskell (ポイント:IOモナド)
import System.Random import List main = do input <- getContents let count = read input ran <- randomNumbers 0 99 putStr $ unlines $ map show $ take count ran randomNumbers :: Int -> Int -> IO [Int] randomNumbers from to = do gen <- newStdGen return $ randomRs (from,to) gen
Scheme(Gauche) (ポイント:再帰)
(use srfi-27) (define (random-numbers size n) (if (= n 0) '() (cons (random-integer size) (random-numbers size (- n 1))))) (define (main argv) (let ((count (read))) (for-each print (sort (random-numbers 100 count)))))
(4/13追記:コメント欄で教えて頂いたのですが、srfi-42のリスト内包表記を使うとより簡潔に書けるそうです。)
C++ (ポイント:データが大きくなっても安心)
#include <iostream> #include <vector> #include <stdlib.h> using namespace std; int main() { int count; vector<int> nums; srand(time(NULL)); cin >> count; for(int i=0; i<count; i++) nums.push_back(rand() % 100); sort(nums.begin(), nums.end()); for(int i=0; i<nums.size(); i++) cout << nums[i] << endl; return 0; }
OCaml (ポイント:パターンマッチ)
Random.self_init ();; let rec random_numbers = function 0 -> [] | n -> Random.int(100) :: random_numbers (n-1) ;; let println n = print_int n; print_newline ();; List.iter println (List.sort compare (random_numbers (read_int ())));;
おまけ:Befunge (ソートなし)
&>: #v_@ - : v < 1 >#v?1+^ ^,*52.<
Befungeでソートってどうやるんだろう…。p/gで頑張るんかなぁ。
2007-04-10
■ [ruby] optparseのRubyらしい使い方を考える
ありがちな
require 'optparse' opt = OptionParser.new mode = nil opt.on('-a') { mode = :a } opt.on('-b') { mode = :b } opt.parse!(ARGV)
より、
require 'optparse' mode = nil OptionParser.new{|opt| opt.on('-a'){ mode = :a } opt.on('-b'){ mode = :b } }.parse!(ARGV)
の方がRubyらしいと思うのだがどうだろう。
parse!だけ外にあるのがポイント。
2007-04-11
■ [ruby][event] RubyKaigi2007
行きます。というか、出ます!
6/9、6/10です。Rubyのイベントは勉強会以上の規模のものに行ったことがないので、非常に楽しみです。
大林さんと一緒にRuby/SDL関連の話をする予定です。よろしくお願いします。
■ [scheme] GaucheNight
こっちも行きます(スピーカじゃないけど)。5/9です。水曜だけど、なんとかなるだろ…多分…多分。
とりあえずチケットを予約した。あとは暇を見つけて新宿までチケット受け取りに行きたいところ (チケットを所持してる人優先で入場になるそうなので)。
■ [prog] Unlambda
今まで知らなかったんですが、Unlambdaって遅延評価とcall/cc付きだったんですね(笑)。無茶しやがって。
これのインタプリタ書いたら勉強になりそう。
あと、ひょっとしたらUnlambdaの実用的なソースが世界一揃ってるのはanarchy golfなんじゃなかろうか。
2007-04-13
■ [games] blocksumランカーへの道
フリーで公開されているアクションパズル、blocksumの 攻略メモです。34位ランクイン記念。
基礎知識
- 数字の書かれたブロックを繋げて消していく。2なら2個、3なら3個繋げると消える。10なら10個。
- ブロックを100個消すとLvが一つ上がる。
- Lv9をクリアするとスタッフロールに突入。ロール中もゲームは続きます。:-) ゲーム開始から12分になったところで終了。
Lv0
とりあえず「x」押しっぱなしで画面いっぱいまで上げる。 作るのは3か4。最初に入ってるピース次第。
2が暴発するので注意(笑)。
よほどうまく詰めない限り、一回では100ブロックに満たないと思う。ので、
- タイム優先なら、「x」で少し上げて、2をいくつか作ってレベルを上げる。
- そうでなければ、「x」で画面いっぱいまで上げて、もう一度3か4を作る。
という2択になる。真のランカーはタイムを優先するんだけど、僕はまだ12分いっぱいまでロールを生き残れないことが多いので後者で。
Lv1〜3
画面いっぱいまで上げて、5か6を作る。基本的に5で良いと思う。1が十分にあるか、4が多いときは6を作るのでも可。
LvA
ここまでが順調だと、スペシャルステージのLvAが出現する。出現ブロックは2,3,5なので、作るのは5か10 (10なら235か2323)。得点は10の方が、タイムは5の方が有利。
Lv4〜6
ここからプレイヤーによって趣味が分かれるところだけど、僕は10を作ることにしている(7とか8は未だに慣れてない)。
10の作り方としては
- 4321 424 343
- 55 541 532
- 64 631
- 73 721
- 82
- 91
あたりが頻出なんで覚えておくと良いと思う(太字は最重要)。まぁ覚えるというよりは慣れです。
LvB
スペシャルステージ2。出現ブロックは1,2,4,8。作るのは10でいいと思う。 作り方は82、424、42211あたりで。
Lv7〜9
ブロックのせり上がり速度がかなり速くなる。 Lv9が特にが難しい!調子がよければ10を作るのは不可能でないんですが、ツモ運が悪いと全然揃わない場合も。 そんなときは7や6を適当に石で消して生き残りましょう。
スタッフロール
とりあえず「最初は超高速で7を作る」と覚えておいてください。出現ブロックは1,3,7(,10)。7を作るには331。
で、そこから先は正直なところ上級者のリプレイを見た方が参考になるかと…。僕もなかなか12分いっぱいまで 生き残れません。2をいくつか作って時間稼ぎをして、その間に10など大きい数を作るというのが基本のようです。
2007-04-16
■ [event] Livecoding#3.1
氏久さんに誘われて、Livecoding#3の打ち上げ*1に混ぜてもらいました。
非常にライブ感溢れる飲み会で楽しかったです。ありがとうございました。
- ライブ鍋
- ライブ箸
- ライブ泡盛
- ライブ氷
- ライブ…あと忘れた
(もしかして:ライブ酔っ払い)
あとLivecoding#4は農場か海上で開かれるそうです。
*1 なんで4月になったのかは不明
■ [ruby] REXMLが難しすぎるのでJSONパーサ書いた
twitterの新着を表示するRubyスクリプトを書こうとしたんだが、XMLのパース方法が30分調べても理解できなかったんで 頭にきてJSONパーサを書いた。REXML難しすぎるよ…(酒のせいという説もある)。 *1
実装はtanakhさんのhttp://fxp.hp.infoseek.co.jp/arti/parser.htmlを大いに参考にしています。 構成要素ごとにメソッドを作るやり方ですね。 Rubyだと
- slice!を使って文字列を削っていく
- 何文字目まで処理したかの整数を管理する
という2種類の方法が考えられるんですが、ここでは速度を考えて後者で実装してみました。 *2
所要時間は1時間ちょい。慣れればもっと速く書けるんだろうなぁ。
class JSON class ParserError < Exception def initialize(txt, i) msg = "Parse error at col #{i}: #{txt[i,20].inspect}" super(msg) end end def self.parse_file(path) parse(File.read(path)) end def self.parse(txt) i = space(txt, 0) case txt[i] when ?{ o, = object(txt, i+1) o when ?[ a, = array(txt, i+1) a else raise ParserError.new(txt, i) end end def self.object(txt, i) i = space(txt, i) case txt[i] when ?} i = space(txt, i+1) [{}, i] else ms, i = members(txt, i) i = space(txt, i) raise ParserError.new(txt, i) unless txt[i] == ?} i = space(txt, i+1) [Hash[*ms], i] end end def self.members(txt, i) ms = [] loop do raise ParserError.new(txt, i) unless txt[i] == ?" s, i = string(txt, i+1) i = space(txt, i) raise ParserError.new(txt, i) unless txt[i] == ?: i = space(txt, i+1) v, i = value(txt, i) ms.concat [s, v] i = space(txt, i) break unless txt[i] == ?, i = space(txt, i+1) end [ms, i] end def self.array(txt, i) i = space(txt, i) if txt[i] == ?] i = space(txt, i+1) [[], i] else es, i = elements(txt, i) i = space(txt, i) raise ParserError.new(txt, i) unless txt[i] == ?] i = space(txt, i+1) [es, i] end end def self.elements(txt, i) es = [] loop do v, i = value(txt, i) es << v i = space(txt, i) break unless txt[i] == ?, i = space(txt, i+1) end [es, i] end def self.value(txt, i) i = space(txt, i) case txt[i] when ?" string(txt, i+1) when ?0..?9, ?. number(txt, i) when ?- n, i = number(txt, i+1) [-n, i] when ?{ object(txt, i+1) when ?[ array(txt, i+1) when ?t raise ParserError.new(txt, i) unless txt[i,4] == "true" [true, i+4] when ?f raise ParserError.new(txt, i) unless txt[i,5] == "false" [false, i+5] when ?n raise ParserError.new(txt, i) unless txt[i,4] == "null" [nil, i+4] else raise ParserError.new(txt, i) end end def self.string(txt, i) start = i i += 1 until txt[i] == ?" && txt[i-1] != ?\\ raise ParserError.new(txt, start) if txt.size <= i [txt[start..i-1].gsub(/\\([^nu])/, "\\1"), i+1] end def self.number(txt, i) to = nil float = false i.upto(txt.size-1) do |k| case txt[k] when ?0..?9 next when ?. float = true else to = k break end end raise ParserError.new(txt, i) unless to num = float ? txt[i..to-1].to_f : txt[i..to-1].to_i [num, to] end def self.space(txt, i) loop do case txt[i] when ?\s, ?\n, ?\r i += 1 else break end end i end end
selfまみれになっているのがちょっと気になる。^^; class << JSON とかすればいいんだけど、 そうすると今度はJSON::ParserErrorをどこに書けばいいのかわからん。
関連
REXMLはこんな風に使えば良かったらしい。
■ [ruby] twitterの新着を表示するRubyスクリプト
というわけで、twitterの新着を標準出力に吐くRubyスクリプトです。
- userとpassを自分のアカウントに書き換えてください
- 上のjson.rbが必要(同じディレクトリに置く)
- 端末はeuc-jpを仮定
あ、僕のtwitterアカウントは http://twitter.com/yhara です。あんまりまめには更新してないですが、よろしくどうぞ。
#!/usr/bin/env ruby require 'net/http' require 'kconv' require 'json' user = "your user name" pass = "your password" def decode_utf(str) str.gsub(/\\u([a-f\d]{4})/){ [$1.to_i(16)].pack "U" } end Net::HTTP.version_1_2 req = Net::HTTP::Get.new("/statuses/friends_timeline.json") req.basic_auth user,pass json = Net::HTTP.start('twitter.com',80) {|http| http.request(req).body } JSON.parse(json).each do |st| puts "<#{st['user']['screen_name']}> #{decode_utf(st['text']).toeuc}" end
そのうちnadoka botにする。
逆にRubyからtwitterに投稿するほうは、bluewindからtwitterを更新できるようにRubyでシンプルなクライアントを書いた とか。
2007-04-20
■ [spoj] プログラミング得意なやつちょっと来い - SPOJ Open Contestは4/27まで
Sphere Online Judgeでプログラミングコンテストが開催中です。
C系言語に限らず、LLから関数型、Brainf**kまでさまざまな言語で参加可能です。あなたのお気に入りの言語でぜひ。
問題は10問あって、7問が普通の問題、3問がchallengeになっています。 普通の問題は正解すれば所定の得点が入ります。challengeは問題ごとにスコアの計算方法が決まっていて (速いほうがいいとか、精度が高いほうがいいとか)、1位とのスコア比によって得点が入ります。
問題の日本語訳(を途中で挫折したもの)を http://mono.kmc.gr.jp/~yhara/w/?SPOJ2007 に置いておきます。どうぞご利用ください。
いま業界(?)で熱いのは√2をどこまでも計算せよ(最大200万桁)というSQRT2だそうです。 ていうか上位 2 人 が圧倒的すぎる!
2007-04-21
■ [ruby] Ruby Twitter Gem by John Nunemaker
RubyからTwitterにアクセスするためのライブラリをrubyforgeにて発見。
twit = Twitter::Base.new('emailaddress', 'password') # You and Your Friends Timeline twit.timeline(:user).each do |s| puts s.text, s.user.name end # Your Friends twit.friends.each do |u| puts u.name, u.status.text end
こんなんだそうです。しかしインスタンス生成がTwitter::Base.newってのは覚えにくい! twit = Twitter.login(user, pass) とかなんかあるだろう…。
メモ
- サンプルとして、コマンドラインツールが付いてきます。twitter post hoge で送信、twitter timeline で受信。
- user/passは~/.twitterから読み込むこともできるようです。
- Hpricot 0.5+と相性が悪いようです(Hpricotのバグ)。ソース
■ [nadoka] twitterの新着をnadoka経由でIRCに表示
twitterの新着を表示するnadoka bot(twitterbot.nb)を書きました。 まだ受信専用です。
require 'net/http' require 'kconv' require 'rexml/document' class Twitter def initialize(user, pass) @user, @pass = user, pass @req = Net::HTTP::Get.new("/statuses/friends_timeline.xml") @req.basic_auth @user,@pass @most_recent_id = nil end # timelineを読み込む。前回呼ばれた以降に更新されたstatusを配列で返す。 def timeline xml = Net::HTTP.start('twitter.com',80) {|http| http.request(@req).body } doc = REXML::Document.new(xml) results = [] doc.each_element('/statuses/status') do |elem| id = elem.elements['id'].text.to_i time = Time.parse(elem.elements['created_at'].text) text = elem.elements['text'].text user = elem.elements['user'].elements['screen_name'].text if @most_recent_id.nil? || id > @most_recent_id results << [id, time, text, user] end end @most_recent_id = results[0][0] unless results.size == 0 results.reverse end end class TwitterBot < Nadoka::NDK_Bot def bot_initialize @tw = Twitter.new(@bot_config[:user], @bot_config[:pass]) @ch = @bot_config[:ch] end def on_timer t @tw.timeline.each do |id, time, text, user| send_notice @ch, "<#{user}> #{text.gsub(/\n/," ").tojis}" end end end
使い方
nadokarcに、下のような感じで設定を書き加えてください。(※以下はtrunkでの例なんで、nadokaのバージョンによっては 書き方が違うかも)
BotConfig = [ { :name => :TwitterBot, :user => "myname", :pass => "asdffdsa", :ch => "#myname-twitter:*.jp" },
2007-04-26
■ [GTD] GTDはじま…りそう
最近やるべきことが多すぎて脳内がパニックになっているので、写真でわかるGTD(初回編)を参考に やりかけの仕事を収集してみた。ロッテリアで。2時間かけて。
実はTODOアプリを作りたいなぁと前々から計画していて、それが完成したらTODOの洗い出しをしよう…と思っていたのだが、 全然できる気配がないので^^; checkpad.jp を使うことにした。*1
「○○が終わったら××しよう」という考え方は危険だ。○○が終わるまで永遠に××できないんだから。 ○○がなくても××できるんじゃないか?をちゃんと考えないとな。
*1 remember the milkはなんか使い方が直感的に分からなかったので断念
2007-04-30
■ [misc] HHKB Lite2を買ってみたが
古いキーボードよりBSとEnterが遠くてまだ慣れない。BSのつもりでEnterを押してしまう。
あとShift+Del(HHKBではShift+Fn+BS)が入力できない。「カタカナに変換」がF7じゃなくてFn+7なのがちょっとめんどい。
しかしサイズが小さいのはとても良い。:-) 俺の机はこんなに広かったのか…。
■ [web] twitterの転送量が多いのは
APIで取れるJSON/XMLが冗長なのも一因な気がする。
timeline(最新の発言)がほしいだけなのに各発言者のlocationとかdescriptionとかprofile画像のURLまで 付いてくるんだもんなぁ。
□ shiro [Schemeでもsrfi-42で内包表記が使え、簡潔になることが多いです。 (use srfi-27) (use ..]
□ yhara [おお、list-ecというのがあるのですね。 ありがとうございます。追記しておきました。]