backstage

合唱音源の新着情報の舞台裏

なぜ『ニンジャスレイヤー』はただの小説ではないのか

f:id:s2terminal:20141123190321j:plain:w240

2016年を象徴する大きな出来事として「ニンジャスレイヤー第3部: 不滅のニンジャソウル」が完結しました。

ニンジャスレイヤーとは「サイバーパンク・ニンジャアクションノベル」いわゆる能力者系のストーリーで、主人公が反則的に強く、『ニンジャ』と呼ばれる超人がはびこるネオサイタマで、強大な悪をバタバタと倒していく痛快な展開が特徴です。

ニンジャスレイヤーは文学作品です。 ですが、他の小説とは決定的に異なる点がひとつあります。それは Twitter上で連載されている という点です。

(ニンジャスレイヤーは後に物理書籍が出版され、マンガやアニメ化もされています。またTwitter連載版がオリジナルというわけでもなく、原典として翻訳前の英語版が存在します。ですが、本稿では日本における展開の起点となるTwitter連載版にフォーカスを当てます)

2016年の振り返りとして、この作品が一体どういう魅力を持っているのか、自分の考えを記します。

ニンジャスレイヤーという作品の特徴

この作品には、普通に読むだけでは拾い切るのが困難なほど膨大な登場人物や複雑な伏線が貼られていたり、情報工学や神話など無駄に専門性の高い元ネタが含まれています。 過去に本ブログでも指摘しました。

s2terminal.hatenablog.com

また、どれだけシリアスなシーンでもギャグを忘れない、ツッコミどころの多さもあります。 この小説のネタ要素は「ステレオタイプの日本観」を由来にした世界設定に依るものが多く、そういった光景が登場人物にとってはすべて当たり前なのか、劇中にツッコミ役が誰一人としていないのも特徴です。

主人公のピンチに、病み上がりの師匠が決死の覚悟で駆けつけるという熱いシーン。 額に三角の布を当て、死を覚悟した出で立ち...「死んどるがな!」と突っ込まざるを得ません。

さて、ここまでなら"すこし狂った文学作品"で終わってしまいます。

Twitter上で連載されるニンジャスレイヤー

ニンジャスレイヤーは、Twitter上で連載されています。

twitter.com

  • 140文字制限 によって数分に一度更新されるテンポの良さ
  • 実況用の 公式ハッシュタグ の存在
  • リツイート による引用・拡散のしやすさ

上記特徴を持つTwitterと、先程の特徴がマッチします。 複雑な伏線や専門的な設定は"ヘッズ"と呼ばれるファンによって解析され、解説や感想がTwitter実況タグに流れてきます。 ギャグが多い展開になると実況タグがツッコミで盛り上がり、Twitterのトレンドに入ったりします。

みんなで一緒にツッコみながら読む小説が、今まで他にあったでしょうか。私は知りません。

ファンとの交流イベント

ニンジャスレイヤーは公式で二次創作を奨励しており、二次創作用の専用ハッシュタグを切ってイラストコンテストのような事をしていたりします。 他にはTwitterのリプライ機能を使ったインタビューや、人気投票も行っています。 いずれも雑誌の読者コーナーのようなノリですが、これらがTwitter上で作品連載とともに、シームレスに、リアルタイムで行われるのも特徴的です。

特に、これら日常的な告知自体も手が込んでおり、その胡乱なアナウンスが数々の迷言を生み、本編とともに話題になったりします。

また、他にもTwitterの機能を使った特殊な演出として、トレンド入りのエネルギーで戦うニンジャの登場や、複数アカウントによる複数視点での同時連載、投票機能で次の展開を決めるなどする事も稀にあります。

ニンジャスレイヤーは唯一無二の存在なのか

Twitterは小説を投稿するSNSではありません。 ですが様々な要素が組み合わさることで、ニンジャスレイヤーという 文学作品を利用したエンターテイメントTwitter上に実現しています。 これはfacebookInstagram等ほかのSNSでは実現しなかったでしょうし、小説投稿サイトや専用のホームページを作ってもここまで面白くはならなかったと思います。

唯一近いのは「動画の任意箇所にコメントを付けられる」という特徴を持つ ニコニコ動画 だと思います。 ですが文章の投稿に適していないこと、引用性やハッシュタグの存在など、Twitterと比べると小説を投稿するには厳しい点が多いです。

一方で、平日昼間から深夜に及ぶ事もある連載をリアルタイムで追わないと実況の熱気を再現するのが難しいTwitterに対して、ニコニコ動画はいつでも頭から再生することで順番にコメントを流すことができる利点もあります。 (そのためアニメ化には期待していたのですが...)

まとめ

ニンジャスレイヤーとは、小説に「実況」「引用性」など独自の要素を加える事で完成した、新しいエンターテイメントの形だと思っています。

関連記事

s2terminal.hatenablog.com

ニンジャスレイヤーと2038年問題

f:id:s2terminal:20160615013707p:plain

ニンジャスレイヤー第3部最終章の予告が行われました。 それまでのエピソードからも日付を示唆する文言が度々含まれていましたが、最終章が 2038年1月18日 という日付になることが明言されました。

ninjaheads.hatenablog.jp

アガメムノンは2038年1月18日深夜に月面メガトリイ基地でアルゴス・システムのリブートを行う。インターネット再定義のためにアルゴスのタイピング速度が一時的に減じるこの夜だけが、ハッキングで攻撃しうる唯一のチャンスである。アマクダリが再定義を完了させれば、Y2K規模の災厄が再び地表を覆うだろう。その後、アマクダリが全てを支配するのだ。

2038年1月18日深夜にY2Kと同等の災厄が起きる とあります。

これは、「Y2K(2000年問題)」と「2038年問題」という、現実に起こりうる2つの年問題が元ネタになっています。

2000年問題

西暦の下2桁を使って年月を判定するシステムを組んでいると、1999年から2000年になった瞬間に、コンピュータは2000年を1900年を勘違いしてしまい、誤動作してしまうのではないか?という問題です。Y2Kとも呼ばれます。

実際の年月 データ コンピュータの認識
1999年11月 9911 1999年11月
1999年12月 9912 1999年12月
2000年1月 0001 1900年01月

くわしくは:2000年問題 - Wikipedia

ニンジャスレイヤーにおける2000年問題

西暦二千年を迎えた瞬間、世界中でUNIXが多数爆発し、優秀なUNIX技術者が大勢死んだ

(ニンジャスレイヤー第3部「ダークサイド・オブ・ザ・ムーン」より)

限られたIPと違法プロキシサーバ基地を巡り、暗黒メガコーポ群主導による論理物理両面での電子戦争が勃発した

(ニンジャスレイヤー第2部「キョート・ヘル・オン・アース」より)

ニンジャスレイヤーは近未来の日本を舞台としており、Y2Kは過去の出来事として語られます。 しかし、現実の世界とは異なる点があります。

  • ニンジャスレイヤーの世界では、2000年時点でIPv4枯渇問題が解決されていないようで、 固定IPを持つプロキシサーバ等は貴重な資源である
  • ニンジャスレイヤーにおけるコンピュータは、バッファオーバーフロー等の不具合が起きると 物理的に爆発する

そのため、以下のように現実世界における2000年問題とは大きく異なる結末を迎えます。

  1. 2000年問題世界中のUNIXが爆発
  2. 爆発の影響でUNIX技術者が多数犠牲となる
  3. 技術者が足りず、IPアドレス枯渇が深刻になる
  4. 残されたIP資源を巡って 戦争が勃発

この戦争の影響で日本は電子的に鎖国、独自の発展を遂げることになります。

何度読んでもこの設定を考えた作者は天才だと思います。

2038年問題

2000年問題と似たような問題なのですが、エンジニア以外には理解しづらく、影響範囲が大きいのが、この2038年問題です。

UNIXでは、UNIXTIMEというデータが広く使われています。 UNIXTIMEとは 1970年1月1日からの経過秒数で時刻を表したもの です。

たとえば、2016年6月14日23時30分10秒のUNIXTIMEは、「1465914610」となります。

$ ruby -e 'require "time";puts Time.parse("2016-06-14 23:30:10").to_i'
1465914610

現代のコンピュータは性能が良いので、とても大きな値を取り扱うことができます。 しかし過去のコンピュータでは、このUNIXTIMEを 符号付き32bit整数 で取り扱っていました。

この「符号付き32bit整数」というのは、約21億5千万が最大値となります。 21億5千万というのは大きな数字に見えますが、21億5千万秒は 約68年 です。

そのため、1970年1月1日から約21億5千万秒後、 2038年1月19日3時14分8秒以降、コンピュータが時刻を正しく扱うことができなくなる というのが、2038年問題です。

実際に、先ほどのUNIXTIMEを31桁2進数(符号付き32bit整数の仮数部)で表すと、以下のようになります。

$ ruby -e 'require "time";puts sprintf("%31b", Time.parse("2016-06-14 23:30:10").to_i)'
1010111011000000001010011110010

$ ruby -e 'require "time";puts sprintf("%31b", Time.parse("2038-01-19 12:14:06").to_i)'
1111111111111111111111111111110
$ ruby -e 'require "time";puts sprintf("%31b", Time.parse("2038-01-19 12:14:07").to_i)'
1111111111111111111111111111111
$ ruby -e 'require "time";puts sprintf("%31b", Time.parse("2038-01-19 12:14:08").to_i)'
10000000000000000000000000000000
$ ruby -e 'require "time";puts sprintf("%31b", Time.parse("2038-01-19 12:14:09").to_i)'
10000000000000000000000000000001
$ ruby -e 'require "time";puts sprintf("%31b", Time.parse("2038-01-19 12:14:10").to_i)'
10000000000000000000000000000010

12時14分8秒に桁があふれたのがわかると思います。 (グリニッジ標準時で午前3時14分8秒なので、日本時間では12時となる)

2038年問題自体は、現実世界でも待ち受けている、私たちが立ち向かわなければならない問題です。

くわしくは:2038年問題 - Wikipedia

ニンジャスレイヤーにおける2038年問題

世界中のUNIXはオーバーフローを起こし、Y2Kにも比肩する爆発と災厄を引き起こすはずです

(ニンジャスレイヤー第3部「スルー・ザ・ゴールデン・レーン」より)

すでに 2038年問題の起きるまさにそのタイミングで、Y2K規模の爆発が起こる であろう事が劇中で指摘されています。

しかしこれが2038年問題による脆弱性なのか、敵組織による何らかの策略により故意に引き起こされる物であるのかはハッキリしていません。

確実なのは UNIXが爆発する ということです。

ルートDNSサーバ

2038年問題について語られると同時に、以下の様なセリフも見られます。

ヒエラルキー最上位に位置する神秘的なUNIXサーバがかつて地球全土に13個存在し、幾つかが滅びたという。その残されたひとつがジグラットなのだ

(ニンジャスレイヤー第3部「スルー・ザ・ゴールデン・レーン」より)

この「ヒエラルキー最上位に位置する神秘的なUNIXサーバ」とは、現実における ルートDNSサーバ が元ネタと思われます。 (ジグラットとはカスミガセキ・ジグラットのこと。ネオサイタマ市役所等を取り込んだ巨大建造物)

実際にルートDNSサーバというのは世界に13個のみ存在し、それを構成するうちのひとつは霞ヶ関から数kmの地点である東京大学にあるそうです。(※実際には地理冗長されているので、サーバ13個=13箇所ではないそうです)

ニンジャスレイヤー第3部の敵組織は、世界中のインターネットを支配するカギを握る何らかのサーバを持っており、2038年問題のタイミングを狙ってなんかするようです。

備えよう。

まとめ

  • IPv6移行は大切
  • 64bit整数は偉大
  • UNIXは爆発する

寿司言語を作ってみた

f:id:s2terminal:20140724204827j:plain

マジメな記事 を書いていて休日ながら疲れてしまったので、ちょっとプログラムでも書こうと思いました。

なんかRuby gemが作りたかったので、作ってみました。

3時間ほどで完成しました。

すし言語

github.com

Ruby上に実装したプログラミング言語です。

すしとRubyはともに日本を代表する文化であり、相性が良いに違いありません。

文法

文法 意味
pointer_increment
すし pointer_decrement
すすし pointer_value_increment
すすすし pointer_value_decrement
すすすすし output
すすすすすし input
すすすすすすし jump_forward
すすすすすすすし jump_backward

いわゆるBrainfu*k派生言語です。

インストール

gemにしましたが、面倒だったので rake release はしていません。 specific_installを使って下さい。

$ gem install specific_install
$ gem specific_install -l https://github.com/s2terminal/sushi_lang.git

Hello World

しすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすしすすしすすしすすしすすしすすしすすしすすすすしすすすすしすすしすすしすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすししすすすしすすすすすすすしすしすすすすしすすしすすしすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすすすすすしすすすしすすすすすすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすし

$ cat sample/sample_script.sushi 
しすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすしすすしすすしすすしすすしすすしすすしすすすすしすすすすしすすしすすしすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすししすすすしすすすすすすすしすしすすすすしすすしすすしすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすしすすすすしすすすすすすしすすすしすすすすすすすししすすしすすしすすしすすしすすしすすしすすしすすしすすすすすすしすしすすしすすしすすしすすししすすすしすすすすすすすしすしすすしすすすすしすすすすすすしすすすしすすすすすすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすしすすすすし

$ sushi_lang sample/sample_script.sushi
Hello World!

 実装

メインの sushi_lang/sushi_lang.rb はこんな感じ

  class Sushi
    NETA = [
      :pointer_increment,       # >
      :pointer_decrement,       # <
      :pointer_value_increment, # +
      :pointer_value_decrement, # -
      :output,                  # .
      :input,                   # ,
      :jump_forward,            # [
      :jump_backward            # ]
    ]

    def initialize(filename)
      # Compiler
      @source = filename
      @object_code = []
      @jump = {}

      # Turing Machine
      @tape = []
      @pointer = 0
      @line = 0
    end

    def compile
      File.open(@source) do |file|
        su_count = 0
        line = 0
        jump_stack = []

        file.each_char{|char|
          case char
            when ""
              su_count += 1
            when ""
              code = NETA[su_count]

              # jump
              if code == :jump_forward
                jump_stack.push(line)
              elsif code == :jump_backward
                left_curly_brace = jump_stack.pop
                @jump[line] = left_curly_brace
                @jump[left_curly_brace] = line
              end

              @object_code.push(code)
              su_count = 0
              line += 1
          end
        }

      end
    end

    def execute
      until @object_code[@line].nil?
        self.send(@object_code[@line])
        @line += 1
      end
    end

    private

    # ++p;
    def pointer_increment
      @pointer += 1
    end

    # --p;
    def pointer_decrement
      @pointer -= 1
    end

    # ++*p;
    def pointer_value_increment
      @tape[@pointer] ||= 0
      @tape[@pointer] += 1
    end

    # --*p;
    def pointer_value_decrement
      @tape[@pointer] ||= 0
      @tape[@pointer] -= 1
    end

    # putchar(*p);
    def output
      print @tape[@pointer].chr
    end

    # *p = getchar();
    def input
      @tape[@pointer] ||= 0
      @tape[@pointer] = STDIN.getc
    end

    # while (*p) {
    def jump_forward
      @line = @jump[@line] if @tape[@pointer] == 0
    end

    # }
    def jump_backward
      @line = @jump[@line] if @tape[@pointer] != 0
    end
  end

sendを使って全部プライベートメソッド化している所とかちょっと格好つけてみました。

まとめ

 参考文献