「ゲーム性」という語の(もしかしたら最古の)用例


ここにスキャンして載せたのは、「物理の散歩道」シリーズという単行本のタイトルで知られる、『自然』誌の「ロゲルギスト」による連載エッセイの一部で、同誌19巻6号(1964年6月号)「コントロールを楽しむゲーム」、筆者はロゲルギスト I2今井功先生)からのものです(72ページ)。その時期から、「ゲーム性」という語のかなり古い、もしかしたら最古の用例とも思われます。
おもちゃの話とゲームの話がないまぜになっていますが、おもちゃのうちでも、完全に偶然に任せられるのでもなく、完全に人間がコントロールするのでもなく、競技(英語 game の原義のひとつに近い)的な性格等を持っている、といったような意味、すなわち、後によく使われるようになったのとだいたい似たような意味で使われていることがわかるかと思います(なお、内容を確認されたい方は、雑誌のバックナンバーでなくとも、『第三 物理の散歩道』に再録されていますので、そちらの92ページを参照されれば良いでしょう)。

物理の散歩道 第3 (科学ライブラリー)

物理の散歩道 第3 (科学ライブラリー)

情報処理学会発足の頃の話

これはハードコピーを見せたほうがインパクトがある、だろうと判断して持っていったところ、興味を持っていただけたのですが、


この「情報処理学会の発足は1960年、昭和でいうと35年」というのは、電気系6学会あたりに居る人でも(最近は? あるいは情報処理以外の所属だと)あまり知られてないようで、そのあたりの情報へのポインタとかを書いてみようと思います。

創設秘話、といったようなものは(某学SF研の創設の言葉は「闇あれ」だったと伝え聞いておりますが…脱線)、後々になってから面白い話が出てきたりもするものですが、情報処理学会の場合は古いほうに興味深いものがあり、現在「50年史」の附属資料として配布されている「学会30年のあゆみ」中の「30年の軌跡」の冒頭部分をまず読んでみるのが良いように思われます。

(PDF) http://www.ipsj.or.jp/50anv/50nenshi/data/pdf/0101.pdf (PDF)

そこに「発足 情報処理学会は事実上,山下英男(所属:略),和田弘(所属:略)によって創設された.」と端的に書かれていますように(所属については当該2次文献執筆時のものであるため略した)、先見的なヴィジョンを持った少数のリーダーシップにより設立されたというように伺えます。

両先生については、情報処理学会のコンピュータ博物館(バーチャル博物館)にある「日本のコンピュータパイオニア」にある記事も参考になるでしょう( http://museum.ipsj.or.jp/pioneer/h-yama.html http://museum.ipsj.or.jp/pioneer/h-wada.html )。

また、2010年に『情報処理』に掲載された和田弘先生のインタビュー(インタビューは2004年 http://id.nii.ac.jp/1001/00069850/ )も参考になると思います。これはもうほんとに『電子立国』の世界で、あと、アスキーから出ていた本『計算機屋かく戦えり』にもインタビューが収録されていますが、そちらについては電子化という噂を聞いたような気もしますがどうなっているのかな……

Gauche の util.stream の stream-delay はなぜあるのか、どういう場合にどのように使うものなのか?

All problems in computer science can be solved by another level of indirection. (David John Wheeler)
But that usually will create another problem.

これは、デビッド・ホイーラーさん*1wikipedia:デビッド・ホイーラー)による格言ですが、遅延データ構造はインダイレクション(定訳は無い気もしますが「間接化」でしょうか)による御利益が最もよくわかるものの一つではないでしょうか。

Gauche の util.stream( http://practical-scheme.net/gauche/man/?l=jp&p=util.stream )では stream-delay というマクロが提供されています。このライブラリは SRFI-40( http://srfi.schemers.org/srfi-40/srfi-40.html )準拠ですから詳細はそちらを読めば全て書いてありますが、なぜ用意されていて、どういう場合にどう使うものか、ざっとまとめておく意味はあるように思いますので、以下にそれを書きます。

リファレンスマニュアルの記述

Gauche のリファレンスマニュアルには次のようにあります。

Macro: stream-delay expr

    [SRFI-40] exprの遅延形式であるストリームを返します。

    原則として、ストリームを生成する関数はすべからく結果を stream-delayでラップすべきです。

「結果を」とありますが、関数の返す値が常に delay されたリスト、すなわちストリームであるべき、ということであれば、ストリームの構成子である stream-cons(と、stream-null )の返す値がそうなっていれば良いはずで、実装が表に出てしまっているように見える stream-delay がなぜ存在するのか、いまいちわかりにくく感じられるかもしれません。

実際にはこれは「ストリームを生成する関数」の「結果だけ」ではなく、それ自体の振舞いに関係していて、SRFI-40 を実装したライブラリには必須のマクロです。

遅延プリミティブ

まず、ここでの議論のための実装のために、以下のような遅延プリミティブを使うこととします。

(define-syntax delay
  (syntax-rules ()
    ((_ val)
      (lambda () val))))

(define (force thunk) (thunk))

全くナイーブに実装した delay と force です。本格的にはメモ化とかを実装すべきでしょうが、ここでの議論にはこれで十分です。

oddストリーム

SRFI-40 中では、ストリームを実装する戦略について、oddとevenの2通りの戦略がある、と分類しています。oddとevenという名前の理由については後で説明します。SICP の本文中で示されている(日本語版では p. 189 にある)実装はoddで、SRFI-40 のストリームはevenです。oddストリームの実装は次のようになります(ここで、単に cons car cdr とあるのは、実装基盤のScheme処理系のそれを使うという意味になります)。SRFI-40 中の解説では cons1 などのように "1" というサフィックスが付いています。

(define-syntax odd-cons
  (syntax-rules ()
    ((_ elem strm)
      (cons elem (delay strm)))))

(define (odd-car strm)
  (car strm))

(define (odd-cdr strm)
  (force (cdr strm)))

見てわかるように、cdr-partだけを遅延させています。force / delay はそれぞれ1箇所に入り、実装の詳細は綺麗に掩蔽されていて、一見エレガントなように見えます。

しかし実際には問題があります。odd-cons はマクロですので、それ自体ではcar-partにあたる引数 elem を評価しませんが、展開結果に cons が直接あり、その引数として直接 elem を渡してしまっていますから、結果として odd-cons があると、そこで car-part の実引数は評価されてしまいます。これは「cons should not evaluate its arguments」という、遅延ストリームの基本に反しています。

わざとらしいですが、簡単な例で示します。

(define (my-take n strm)
  (if (= n 0)
      '()
      (cons (odd-car strm) (my-take (- n 1) (odd-cdr strm)))))

(define (stream-from-n n)
  (print n)
  (odd-cons n (stream-from-n (+ n 1))))

(print (my-take 4 (stream-from-n 0)))

実行すると次のように出力されます。

$ gosh sample-odd.scm
0
1
2
3
4
(0 1 2 3)

ここで my-take が odd-cons でストリームを作っていれば問題は無いわけですが、このように普通の cons でリストを作ろうとすると「その中身にアクセスされることはないが、odd-cdr によってforceされてしまうconsセル」は作ってしまうため、そのconsセルのcar-partに相当する評価は起きてしまいます。SRFI中では、ある種の off-by-one errorhttp://catb.org/jargon/html/O/off-by-one-error.html )だ、と言っています。

ストリームがこのような「oddストリーム」であるため、SICPの本文中のコードには、陽に delay を使っているものがあり、なぜそうしなければならないかという練習問題になっています。

evenストリーム

SRFI-40 では、以上のようなoddストリームの問題が解決された手法として、evenストリームというものが示されています(他にもう一つの考え方としては、cdr-part だけでなく car-part も遅延させる、という方法もあると考えられます。Haskellなどの、任意の値が遅延されるスタイルに近いのはそちらとも言えます)。

evenストリームの実装を示します。SRFI-40 中の解説では cons2 などのように "2" というサフィックスが付いています。

(define-syntax even-cons
  (syntax-rules ()
    ((_ elem strm)
      (delay (cons elem strm)))))

(define (even-car strm)
  (car (force strm)))

(define (even-cdr strm)
  (cdr (force strm)))

こちらでは、even-cons の展開結果の最上位に delay がありますから、consセルに含まれる要素(consセルが要素として指すもの)ではなく、consセル自身が遅延されたものになっています。その代わり、carを取るにもcdrを取るにも、まず force する必要があるように、force / delay があらゆる場所に必要になっています。言い換えると、このモデルは、oddストリームからバグを除いたという良い特性の代わりに、掩蔽が綺麗にできないものになっている、ということになります(ここでは議論しませんが、実用的な実装では force / delay のメモ化も重要になるでしょう)。

ここで、mapについて考えてみます。evenストリームによるmapの実装は次のようになります。

(define (even-map f strm)
  (delay (force
    (if (even-null? strm)
      even-null
      (even-cons
        (f (even-car strm))
        (even-map f (even-cdr strm)))))))

「 (delay (force 」とあるのは一見無意味なように見えます、が、無意味ではなく、「evenストリームとして正しい」mapは、このようになっている必要があります。以下でそれを説明します。

evenストリームにおける even-cons の定義から、その cdr-part である strm は、even-cons の時点では評価されず、後でforceされた時点で初めて評価される、ということがわかります。evenストリームにおいては、他の「ストリームを受け取り、そのストリームに何かをして返す」というような関数では全て原則として同様に、引数として渡されたリストの評価は、その関数が返した値がforceされるまで遅延されなければならない、ということになります。

even-map のコードを見ればあきらかと思いますが、もし「 (delay (force 」が無ければ、渡されたストリームは even-null? によってforceされ評価されてしまいますから、このように全体が遅延されるように囲ってやる必要があるわけです。

Gauche の util.stream など、SRFI-40 の実装では明示的なforceが要らないようになっているので、このコードにおける delay の側だけが stream-delay として残るということになるわけですが、それがなぜ要るかという理由は以上のようになります。

名前について

oddとevenという名前は、oddストリームの挙動は少し変だ(oddだ)ということにも掛けられていますが、SRFI-41( http://srfi.schemers.org/srfi-41/srfi-41.html )にわかりやすい表現があります。

すなわち、oddストリームのその実際の構造は次のようになってます。

(cons 1 (delay (cons 2 (delay (cons 3 (delay '()))))))

ここで、終端されているリストであれば、構成子 {delay, cons, '()} の数が、常に奇数(odd number)個になるはずです。

一方、evenストリームは、その前にもう1個 delay が付いて、

(delay (cons 1 (delay (cons 2 (delay (cons 3 (delay '())))))))

のようになり、常に偶数(even number)個になるはずだ、ということから、それぞれの名前となっています。

SRFI-41 との関係

SRFI-41 をそんなに読み込めてないのですが)SRFI-40 では、以上のように「実装が掩蔽し切れないdelay」について、ユーザにはプリミティブである stream-delay のみを提供するというスタイルとしていますが、ユーザの負担が大きいという判断か、SRFI-41 は、より多い機能を提供するものになっているようです。

SRFI-45 と lazy

Gauche のマニュアルに「Delayとforceとlazy」として記述がありますが( http://practical-scheme.net/gauche/man/?l=jp&p=Delay%E3%81%A8force%E3%81%A8lazy )、この記事で説明したように「 (delay (force 」を重ねるのは、ある種のスペースリークとも言えますので、それを潰したようなプリミティブが lazy で SRFI-45 で議論されています( http://srfi.schemers.org/srfi-45/srfi-45.html )。

*1:ブロックソートにおける「BW変換」の W の由来であり、古いコンピュータに詳しい人であれば Wheeler Jump の名も出てくるでしょう。

Macr055で順列の生成

続き。 http://parametron.blogspot.jp/2015/04/christopher-stracheygpm_27.html にある、順列の生成。

{m55_define|p2|$1$2$3
}{m55_dnl}
{m55_define|p3|`{p2|$1|$2|$3}{p2|$1|$3|$2}'}{m55_dnl}
{m55_define|p4|`{p3|$1$2|$3|$4}{p3|$1$3|$2|$4}{p3|$1$4|$2|$3}'}{m55_dnl}
{m55_define|p5|`{p4|$1$2|$3|$4|$5}{p4|$1$3|$2|$4|$5}{p4|$1$4|$2|$3|$5}{p4|$1$5|$2|$3|$4}'}{m55_dnl}
{p5||a|b|c|d}{m55_dnl}

Macr055でNクイーン

前回の4クイーンを元に一般化して「マクロを生成するマクロ」によるNクイーン問題( http://parametron.blogspot.jp/2015/04/christopher-stracheygpm.html )。

エスケープが妖しいことになってて、もしかしたら無駄な展開とか起きてるかもしれませんが、一応ちゃんと動いて、正しい結果は出ています。

リスト中では N = 8 を定義してエイトクイーンになっていますが、macr055 は awk で実装しているうえに効率とか考えていないので流石に遅く、手元の環境で実行し(他の作業もしていましたので安定したものではないですが)time で確認したところ3時間ちょっとかかりました。

{m55_define|1+|`{1|2|3|4|5|6|7|8|9|10|
  {m55_define|1|$$$1}}'}{m55_dnl}
{m55_define|1-|`{-1|0|1|2|3|4|5|6|7|8|
  {m55_define|-1|$$$1}}'}{m55_dnl}
{m55_define|-|`{$2|
  {m55_define|$2|`{-|{1-|$1}|{1-|$2}}'}
  {m55_define|0|$1}}'}{m55_dnl}
{m55_define|lt|`{$1|
  {m55_define|$1|`{p|$1|$2|
    {m55_define|p|`{lt|{1-|$1}|$2}'}}'}
  {m55_define|-1|t}
  {m55_define|$2|f}}'}{m55_dnl}
{m55_define|or|`{$1|
  {m55_define|$1|t}
  {m55_define|f|$2}}'}{m55_dnl}
{m55_define|?|`{{lt|$1|$2}|
  {m55_define|t|`{{-|$2|$1}|
    {m55_define|{-|$2|$1}|f}
    {m55_define|$3|t}}'}
  {m55_define|f|`{{-|$1|$2}|
    {m55_define|{-|$1|$2}|f}
    {m55_define|$3|t}}'}}'}{m55_dnl}
{m55_define|gen1|`{$1|
  {m55_define|$1|``|$$$$$1'{gen1|{1+|$1}|$2}'}
  {m55_define|{1+|{1+|$2}}}}'}{m55_dnl}
{m55_define|gen2|`{$1|
  {m55_define|$1|````|$$$$$1'''{gen2|{1+|$1}|$2}'}
  {m55_define|{1+|{1+|{1+|$2}}}}}'}{m55_dnl}
{m55_define|gen3|`{$2|
  {m55_define|$2|```{or|{?|$$$$$1|$$$$1|0}|{or|{?|$$$$$1|$$$$1|$2}|''{gen3|{1+|$1}|{1-|$2}}'```}}'''}
  {m55_define|1|```{or|{?|$$$$$1|$$$$1|0}|{?|$$$$$1|$$$$1|1}}'''}}'}{m55_dnl}
{m55_define|gen4|`{$1|
  {m55_define|$1|````|$$$$$1'''{gen4|{1+|$1}|$2}'}
  {m55_define|{1+|{1+|$2}}|````|$$$$1|$$$$$1''''}}'}{m55_dnl}
{m55_define|gen_q|`{$1|
  {m55_define|$1|`{m55_define|q$1|`{qq$1|0'{gen1|1|$1}`}'}{m55_dnl}
{m55_define|qq$1|`{$$$$1|
  {m55_define|$$$$1|`{''{gen3|2|$1}``|
    {m55_define|f|`{q{1+|$1}'''{gen4|2|$1}```}{qq$1|{1+|$$$$1}'''{gen2|2|$1}```}'}
    {m55_define|t|`{qq$1|{1+|$$$$1}'''{gen2|2|$1}```}'}}'}
  {m55_define|$$$$'{1+|{1+|$1}}`|}}'}{gen_q|{1-|$1}}{m55_dnl}'}
  {m55_define|0}}'}{m55_dnl}
{m55_dnl}
{m55_define|N|8}{m55_dnl}
{m55_dnl}
{m55_define|gen5|`{m55_define|q$1|{gen6|1|$1}}'}{m55_dnl}
{m55_define|gen6|`{$1|
  {m55_define|$1|``$$$$$1 '{gen6|{1+|$1}|$2}'}
  {m55_define|$2|``$$$$$1
''}}'}{m55_dnl}
{gen5|{N}}{m55_dnl}
{gen_q|{1-|{N}}}{m55_dnl}
{m55_define|q0|`{qq0|0|$1}'}{m55_dnl}
{m55_define|qq0|`{$1|
  {m55_define|$1|`{q1|$1|$2}{qq0|{1+|$1}|$2}'}
  {m55_define|$2|}}'}{m55_dnl}
{q0|{N}}{m55_dnl}

Macr055で4クイーン

カレンダーは面倒そうな割にマクロ内で完結しなくてあまり面白くなさそうだったので、その次の http://parametron.blogspot.jp/2015/04/4.html 4クイーンから。

{m55_define|1+|`{1|2|3|4|5|6|7|8|9|10|
  {m55_define|1|$$$1}}'}{m55_dnl}
{m55_define|1-|`{-1|0|1|2|3|4|5|6|7|8|
  {m55_define|-1|$$$1}}'}{m55_dnl}
{m55_define|-|`{$2|
  {m55_define|$2|`{-|{1-|$1}|{1-|$2}}'}
  {m55_define|0|$1}}'}{m55_dnl}
{m55_define|lt|`{$1|
  {m55_define|$1|`{p|$1|$2|
    {m55_define|p|`{lt|{1-|$1}|$2}'}}'}
  {m55_define|-1|t}
  {m55_define|$2|f}}'}{m55_dnl}
{m55_define|or|`{$1|
  {m55_define|$1|t}
  {m55_define|f|$2}}'}{m55_dnl}
{m55_define|?|`{{lt|$1|$2}|
  {m55_define|t|`{{-|$2|$1}|
    {m55_define|{-|$2|$1}|f}
    {m55_define|$3|t}}'}
  {m55_define|f|`{{-|$1|$2}|
    {m55_define|{-|$1|$2}|f}
    {m55_define|$3|t}}'}}'}{m55_dnl}
{m55_define|q4|$1 $2 $3 $4
}{m55_dnl}
{m55_define|q3|`{z|0|$1|$2|$3|$4}'}{m55_dnl}
{m55_define|z|`{$1|
  {m55_define|$1|`{{or|{?|$2|$1|0}|{or|{?|$2|$1|3}|{or|{?|$3|$1|0}|{or|{?|$3|$1|2}|{or|{?|$4|$1|0}|{?|$4|$1|1}}}}}}|
    {m55_define|f|`{q4|$2|$3|$4|$1|$5}{z|{1+|$1}|$2|$3|$4|$5}'}
    {m55_define|t|`{z|{1+|$1}|$2|$3|$4|$5}'}}'}
  {m55_define|$5|}}'}{m55_dnl}
{m55_define|q2|`{y|0|$1|$2|$3}'}{m55_dnl}
{m55_define|y|`{$1|
  {m55_define|$1|`{{or|{?|$2|$1|0}|{or|{?|$2|$1|2}|{or|{?|$3|$1|0}|{?|$3|$1|1}}}}|
    {m55_define|f|`{q3|$2|$3|$1|$4}{y|{1+|$1}|$2|$3|$4}'}
    {m55_define|t|`{y|{1+|$1}|$2|$3|$4}'}}'}
  {m55_define|$4|}}'}{m55_dnl}
{m55_define|q1|`{x|0|$1|$2}'}{m55_dnl}
{m55_define|x|`{$1|
  {m55_define|$1|`{{or|{?|$2|$1|0}|{?|$2|$1|1}}|
    {m55_define|f|`{q2|$2|$1|$3}{x|{1+|$1}|$2|$3}'}
    {m55_define|t|`{x|{1+|$1}|$2|$3}'}}'}
  {m55_define|$3|}}'}{m55_dnl}
{m55_define|q0|`{w|0|$1}'}{m55_dnl}
{m55_define|w|`{$1|
  {m55_define|$1|`{q1|$1|$2}{w|{1+|$1}|$2}'}
  {m55_define|$2|}}'}{m55_dnl}
{q0|4}{m55_dnl}

例によってローカルな再帰ができないため、そのへんの書き方が違っている。

Macr055でクリスマスの詩

次の http://parametron.blogspot.jp/2015/03/christopher-stracheygpm.html の記事に移って、クリスマスの詩。

"$:" が10番目の引数に置換されるようにM55を改造。文字と文字コードの相互変換は chr と ord という名前の関数とするのが定番ですが、GNU awk のマニュアルにサンプルがあったので https://www.gnu.org/software/gawk/manual/html_node/Ordinal-Functions.html それを参考に実装。

{m55_define|1-|`{-1|0|1|2|3|4|5|6|7|8|9|:|
  {m55_define|-1|$$$1}}'}{m55_dnl}
{m55_define|getn|`{11th|10th|9th|8th|7th|6th|5th|4th|3rd|2nd|1st|
  {m55_define|11th|$$$1}}'}{m55_dnl}
{m55_define|gets|`{11 Pipers
|10 Lords
|9 Ladies
|8 Maids
|7 Swans
|6 Geese
|5 Rings
|4 Birds
|3 Hens
|2 Turtle Doves
|a Partridge
|{m55_define|11 Pipers
|$$$1}}'}{m55_dnl}
{m55_define|song|`{$1|
  {m55_define|$1|`On the {getn|$1} day
{gets|$1}$2
{song|{1-|$1}|{gets|$1}$2}'}
  {m55_define|-1|`On the 12th day
12 Drummers
'$2}}'}{m55_dnl}
{song|:}{m55_dnl}

色相環はマクロでやるのがそんなに面白そうでもなかったのでRubyで書いた(円の生成法に http://parametron.blogspot.jp/2008/07/blog-post_24.html のものを使っているので、楕円になっている)。

include Math

NUM_OF_DIV = 6

def get_rgb sel, i
  case sel
  when "f" then
    255
  when "d" then
    ((255*(NUM_OF_DIV-i)).to_f / NUM_OF_DIV).round
  when "0" then
    0
  when "u" then
    ((255*i).to_f / NUM_OF_DIV).round
  else
    raise
  end
end

magic = "fd00uffd00uffd00uf"

outer_radius = 125
inner_radius = 25

  print <<EOS
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256">
  <g style="stroke-width:0.1">
EOS

  eps = PI / (6 * NUM_OF_DIV)

  p1 = nil

  old_x, old_y = 1.0, 0.0

  (0 ... 6).each {|col|
    (0 ... NUM_OF_DIV).each {|i|
      rgb = [get_rgb(magic[col], i), get_rgb(magic[col+4], i), get_rgb(magic[col+2], i)]
      rgb.map! {|a| "%02x" % [a]}
      rgb = rgb.join
      rgb[0, 0] = "#"
      x1 = old_x - eps * old_y
      y1 = old_y + eps * x1
      unless p1 then
        p1 = [x1, y1]
        old_x, old_y = x1, y1
      else
        print %!    <g style="color:#{rgb}">\n!
        x2 = x1 - eps * y1
        y2 = y1 + eps * x2
        x_1, y_1 = [old_x, old_y].map{|a| 128 + outer_radius * a }
        x_2, y_2 = [x1, y1].map{|a| 128 + outer_radius * a }
        x_3, y_3 = [x2, y2].map{|a| 128 + outer_radius * a }
        x_4, y_4 = [x2, y2].map{|a| 128 + inner_radius * a }
        x_5, y_5 = [x1, y1].map{|a| 128 + inner_radius * a }
        x_6, y_6 = [old_x, old_y].map{|a| 128 + inner_radius * a }
        print %!      <path style="stroke:currentColor;fill:currentColor" d="M#{x_1} #{y_1}L#{x_2} #{y_2} #{x_3} #{y_3} #{x_4} #{y_4} #{x_5} #{y_5} #{x_6} #{y_6}"/>\n!
        print %!    </g>\n!

        old_x, old_y = x2, y2
      end
    }
  }
  print %!    <g style="color:#ff0000">\n!
  x_1, y_1 = [old_x, old_y].map{|a| 128 + outer_radius * a }
  x_2, y_2 = [1.0, 0.0].map{|a| 128 + outer_radius * a }
  x_3, y_3 = [p1[0], p1[1]].map{|a| 128 + outer_radius * a }
  x_4, y_4 = [p1[0], p1[1]].map{|a| 128 + inner_radius * a }
  x_5, y_5 = [1.0, 0.0].map{|a| 128 + inner_radius * a }
  x_6, y_6 = [old_x, old_y].map{|a| 128 + inner_radius * a }
  print %!      <path style="stroke:currentColor;fill:currentColor" d="M#{x_1} #{y_1}L#{x_2} #{y_2} #{x_3} #{y_3} #{x_4} #{y_4} #{x_5} #{y_5} #{x_6} #{y_6}"/>\n!
  print %!    </g>\n!

if false then
  epsiron = 2.0 * PI / NUM_OF_DIV

  old_x = 1.0
  old_y = 0.0

  (0 ... NUM_OF_DIV).each {
    new_x = old_x - epsiron * old_y
    new_y = old_y + epsiron * new_x

    print %!    <line x1="#{old_x*100.0}" y1="#{old_y*100.0}" x2="#{new_x*100.0}" y2="#{new_y*100.0}"/>\n!

    old_x = new_x
    old_y = new_y
  }
end

  print <<EOS
  </g>
</svg>
EOS