トップ 最新 追記

Route 477



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は「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のサイトが酷い

どうしちゃったんだぜ…?

[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で頑張るんかなぁ。

本日のツッコミ(全2件) [ツッコミを入れる]

shiro [Schemeでもsrfi-42で内包表記が使え、簡潔になることが多いです。 (use srfi-27) (use ..]

yhara [おお、list-ecというのがあるのですね。 ありがとうございます。追記しておきました。]


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

[lab] はじめてのTA

2回生の実習のTAをやることになりました。

なんか端末ごとにSKKとかキーボードの設定が変わってて謎い。

[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はこんな風に使えば良かったらしい。

*1 だいたいさあ、 require 'rexml/document' の時点でもう覚えられんよね。

*2 と思ったんだけど、実はslice!(0,*)ってそんなに重い処理じゃない気がしてきた

[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

LimeChat

使い方

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はなんか使い方が直感的に分からなかったので断念

[net] fon2台目

なんか安売りしてたので2台目のfonルータを買ってしまった^^; 1台目は部室に寄付したのでこっちは家に置く。

sshアクセスできるようにして遊びたい。

[GTD] 分類した

あなたのリスト [保管したリスト一覧へ]
* いつかやる/多分やる - 121個
* 次にやる - 22個
* プロジェクトリスト - 32個
* 連絡待ち - 10個

多すぎワロタ。

あと適当に分類しすぎたかも知れん。しかし逆に考えるんだ。間違ってたら分け直せばいいやと考えるんだ。


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まで 付いてくるんだもんなぁ。

[idea] Subversion(とかVCS)ベースのTODO管理ツールとかどうだろう

  • 履歴を残すのが簡単にできる。
  • 複数マシン間の同期がワンクリックで取れる。

[idea] 継続ベースのゲームフレームワーク

全部のタスクがContinuation(コルーチン)で動くゲームとかできないかな。

特に分岐が絡む処理にコルーチンが有用なのは既に体験済みだし。

どれくらいのタスク数まで快適に動くだろうか。

[idea] googlebotのtwitter版

@google ほげ

とか発言すると

@yhara ほげ の検索結果のうち 日本語のページ 約 852,000 件

と返してくれる。

googlebotというかgoogle-countだなこれは。