<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
    xmlns:admin="http://webns.net/mvcb/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>ハタさんのブログ(復刻版)</title>
<link>http://blog.xole.net/index.php</link>
<pubDate>Wed, 08 Feb 2012 23:43:17 </pubDate>
<description>
ハタさんのブログ(復刻版) - RSS 2.0 (Really Simple Syndication).
</description>
<item>
<title>【その後】 やったー Titanium Mobile でも Socket.io が動いたよー　＼(^o^)／</title>
<link>http://blog.xole.net/article.php?id=779</link>
<pubDate>Wed, 08 Feb 2012 23:43:17 +09:00</pubDate>
<description>久々に書いてます。
前回 やったー Titanium Mobile でも Socket.io が動いたよー　＼(^o^)／ って喜んでたら、socket.io のバージョンアップでｇｄｇｄしてしまっている間に
masuidrive さん...</description>
<content:encoded>
<![CDATA[<p>久々に書いてます。</p>
<p>前回 <a href="http://blog.xole.net/article.php?id=775">やったー Titanium Mobile でも Socket.io が動いたよー　＼(^o^)／</a> って喜んでたら、socket.io のバージョンアップでｇｄｇｄしてしまっている間に<br />
masuidrive さんのところで、<a href="https://github.com/masuidrive/ti-websocket-client">ti-websocket-client</a> が作られてたので、僕のトコで作ってた <a href="https://github.com/nowelium/socket.io-titanium">socket.io-titanium</a> にそのまま放り込んでまま放置プレイにしてたので、ちょっと書いてみようかなと。</p>

<p>チャットルームみたいなものを作ってみようと思います。<br />
結論から書くと、下の動画みたいになってます。<br />
また、コードは <a href="https://github.com/nowelium/socket.io-titanium">https://github.com/nowelium/socket.io-titanium</a>にそのまま置いてます。</p>

<iframe width="420" height="315" src="http://www.youtube.com/embed/10ogNjWCpyc" frameborder="0" allowfullscreen>
</iframe>

<h1>チャットルームみたいなチャンネル付きチャット<h1>

<h2>サーバの実装(node.js)</h2>

<p>node.js + socket.io でこんなコードを用意します。</p>

<pre class="javascript">
<span class="keyword" >var</span> io = require(<span class="string" >'socket.io'</span>).listen(8080);

<span class="keyword" >var</span> archiveMessages = {};
<span class="keyword" >var</span> channels = [<span class="string" >'foo channel'</span>, <span class="string" >'bar channel'</span>];

<span class="keyword" >var</span> chat = io.of(<span class="string" >'/chat'</span>);
chat.on(<span class="string" >'connection'</span>, <span class="keyword" >function</span>(socket){
  console.log(<span class="string" >'connected: %s'</span>, socket.id);

  <span class="comment" >// push available channel list</span>
  socket.emit(<span class="string" >'available_channel'</span>, channels);

  socket.on(<span class="string" >'join'</span>, <span class="keyword" >function</span>(value){
    console.log(<span class="string" >'%s joined channel: %s'</span>, socket.id, value.channelId);

    socket.join(value.channelId);
    socket.set(<span class="string" >'channel_id'</span>, value.channelId, <span class="keyword" >function</span>(){
      <span class="keyword" >var</span> messages = archiveMessages[value.channelId] || [];
      socket.emit(<span class="string" >'joined'</span>, messages);
      socket.broadcast.to(value.channelId).emit(<span class="string" >'user:join'</span>, {
        id: socket.id
      });
    });
  });

  socket.on(<span class="string" >'post'</span>, <span class="keyword" >function</span>(message){
    socket.get(<span class="string" >'channel_id'</span>, <span class="keyword" >function</span>(err, channelId){
      console.log(<span class="string" >'%s says&lt;%s channel&gt;: %s'</span>, socket.id, channelId, message);

      <span class="keyword" >if</span>(!(channelId <span class="keyword" >in</span> archiveMessages)){
        archiveMessages[channelId] = [];
      }
      archiveMessages[channelId].push(message);

      socket.emit(<span class="string" >'posted'</span>, {
        message: message
      });
      socket.broadcast.to(channelId).emit(<span class="string" >'user:message'</span>, {
        id: socket.id,
        message: message
      });
    });
  });

  socket.on(<span class="string" >'disconnect'</span>, <span class="keyword" >function</span>(){
    console.log(<span class="string" >'%s disconnected'</span>, socket.id);
    socket.get(<span class="string" >'channel_id'</span>, <span class="keyword" >function</span>(channelId){
      socket.leave(channelId);
      socket.broadcast.to(channelId).emit(<span class="string" >'user:leave'</span>, {
        id: socket.id
      });
    });
  });
});</pre>

<p>ここで重要なのは、socket.join(channel) なところと、 socket.broadcast.to(channel)、そして、socket.leave(channel) なところでしょうか。<br />
これを使うことで、同じ channel に join してる人に broadcast できたりするので、今回はここからスタート</p>

<h2>ルームを取得する</h2>

<p>こっから titanium になります。</p>

<p>server 側で available_channel を socket.connection イベント時(接続したとき) に返してもらっているので、それを素直に読み取ってTableViewに詰めます。</p>

<pre class="javascript">
<span class="keyword" >var</span> win = Titanium.UI.currentWindow;

<span class="keyword" >var</span> io = require(<span class="string" >'socket.io-titanium'</span>);
<span class="keyword" >var</span> socket = io.connect(<span class="string" >'169.254.10.100:8080'</span>);

<span class="keyword" >var</span> channelTable = Titanium.UI.createTableView();

<span class="keyword" >var</span> chat = socket.of(<span class="string" >'/chat'</span>);
chat.on(<span class="string" >'available_channel'</span>, <span class="keyword" >function</span>(channels){
  <span class="comment" >//</span>
  <span class="comment" >// channel view</span>
  <span class="comment" >//</span>
  <span class="keyword" >var</span> rows = channels.map(<span class="keyword" >function</span>(channel){
    <span class="keyword" >var</span> row = Titanium.UI.createTableViewRow({
      title: channel
    });
    row.addEventListener(<span class="string" >'click'</span>, <span class="keyword" >function</span> (){
      channelTable.fireEvent(<span class="string" >'click:channel'</span>, {
        channelId: channel
      });
    });
    <span class="keyword" >return</span> row;
  });
  channelTable.setData(rows);
});
win.add(channelTable);
</pre>

<p>socket.ioの通信はJSONをそのまま扱ってくれるので、JSON.parseとか気にしなくていいので楽ですね。</p>

<h2>ルームチャットを実装する</h2>

<p>サーバ側では join イベントを待っているので、win.open 時に呼び出してあげてます。<br />
また、サーバ側から送られてくるイベントもの(ここでは user:join や user:message など)は、一般的な EventEmitter 的な待ち受けを行い、イベントを受け取ったら処理をする。という一般的なイベントドリブンな感じで実装します。</p>

<pre class="javascript">channelTable.addEventListener(<span class="string" >'click:channel'</span>, <span class="keyword" >function</span>(evt){
  <span class="comment" >//</span>
  <span class="comment" >// chat view</span>
  <span class="comment" >//</span>
  <span class="keyword" >var</span> chatWindow = Titanium.UI.createWindow({
    title: <span class="string" >'room: '</span> + evt.channelId + <span class="string" >' chat'</span>
  });

  <span class="keyword" >var</span> lastRowIndex = 0;
  <span class="keyword" >var</span> chatTable;
  <span class="keyword" >if</span>(/android/i.test(Titanium.Platform.osname)){
    <span class="keyword" >var</span> input = Titanium.UI.createSearchBar({
      showCancel: <span class="keyword" >false</span>
    });
    chatTable = Titanium.UI.createTableView({
      search: input
    });
    chatWindow.add(chatTable);
  } <span class="keyword" >else</span> {
    <span class="keyword" >var</span> header = Titanium.UI.createView({
      top: 0,
      height: 60,
      width: win.width,
      borderWidth: 2,
      borderColor: <span class="string" >'#333'</span>
    });
    <span class="keyword" >var</span> input = Titanium.UI.createTextField({
      top: 10,
      height: 40,
      width: 200,
      color: <span class="string" >'#333'</span>,
      hintText: <span class="string" >'message here'</span>
    });
    chatTable = Titanium.UI.createTableView({
      top: 60,
      height: win.height - 60,
      width: win.width
    });

    chatWindow.add(header);
    chatWindow.add(input);
    chatWindow.add(chatTable);
  }

  chat.on(<span class="string" >'joined'</span>, <span class="keyword" >function</span> (messages){
    <span class="keyword" >var</span> rows = messages.map(<span class="keyword" >function</span>(message){
      <span class="keyword" >return</span> Titanium.UI.createTableViewRow({
        title: message,
        color: <span class="string" >'#999'</span>
      });
    });
    <span class="keyword" >var</span> section = Titanium.UI.createTableViewSection({
      headerTitle: <span class="string" >'archived message(s)'</span>
    });
    rows.forEach(<span class="keyword" >function</span>(row){
      section.add(row);
    });
    chatTable.setData([section]);
    <span class="comment" >// delay</span>
    setTimeout(<span class="keyword" >function</span> (){
      chatTable.scrollToIndex(rows.length - 1, { animated: <span class="keyword" >false</span> });
      lastRowIndex = rows.length;
    }, 100);
  });
  <span class="keyword" >var</span> addMessage = <span class="keyword" >function</span>(message){
    <span class="keyword" >var</span> row = Titanium.UI.createTableViewRow({
      title: message
    });
    chatTable.appendRow(row, { animated: <span class="keyword" >true</span> });
    <span class="comment" >// delay</span>
    setTimeout(<span class="keyword" >function</span> (){
      chatTable.scrollToIndex(lastRowIndex, { animated: <span class="keyword" >true</span> });
      lastRowIndex = lastRowIndex + 1;
    }, 100);
  };
  chat.on(<span class="string" >'posted'</span>, <span class="keyword" >function</span>(value){
    <span class="keyword" >return</span> addMessage(<span class="string" >'you posted: '</span> + value.message);
  });
  chat.on(<span class="string" >'user:join'</span>, <span class="keyword" >function</span>(value){
    <span class="keyword" >return</span> addMessage(value.id + <span class="string" >' joined this channel'</span>);
  });
  chat.on(<span class="string" >'user:leave'</span>, <span class="keyword" >function</span>(value){
    <span class="keyword" >return</span> addMessage(value.id + <span class="string" >' leaved this channel'</span>);
  });
  chat.on(<span class="string" >'user:message'</span>, <span class="keyword" >function</span>(value){
    <span class="keyword" >return</span> addMessage(value.id + <span class="string" >' says '</span> + value.message);
  });
  input.addEventListener(<span class="string" >'return'</span>, <span class="keyword" >function</span> (){
    <span class="keyword" >var</span> messageValue = input.value;
    <span class="keyword" >if</span>(/^\s+$/.test(messageValue)){
      <span class="keyword" >return</span>;
    }

    chat.emit(<span class="string" >'post'</span>, messageValue);
    input.value = <span class="string" >''</span>;
  });
  chatWindow.addEventListener(<span class="string" >'open'</span>, <span class="keyword" >function</span> (){
    chat.emit(<span class="string" >'join'</span>, {
      channelId: evt.channelId
    });
  });
  chatWindow.addEventListener(<span class="string" >'close'</span>, <span class="keyword" >function</span> (){
    chat.disconnect();
  });

  chatWindow.add(chatTable);
  <span class="keyword" >return</span> Titanium.UI.currentTab.open(chatWindow);
});</pre>

<p>うーん。短い。(色々冗長だけど)<br />
基本的な部分はこんな感じで実装しました。</p>

<h2>ついでにwebViewでも実装を書いてみる</h2>

<p>これも github の方にはそのまま入ってますが、一応記述するとするとこんな感じ</p>

<pre class="php">&lt;!doctype html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=<span class="string" >"utf-8"</span> /&gt;
    &lt;meta name=<span class="string" >"viewport"</span> content=<span class="string" >"height=device-height, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"</span> /&gt;
    &lt;title&gt;chat sample&lt;/title&gt;
    &lt;style&gt;
    #rooms button {
      display: block;
    }
    &lt;/style&gt;
    &lt;script type=<span class="string" >"text/javascript"</span> src=<span class="string" >"../socket.io/dist/socket.io.js"</span>&gt;&lt;/script&gt;
    &lt;script type=<span class="string" >"text/javascript"</span>&gt;
    <span class="keyword" >var</span> $ = <span class="keyword" >function</span>(id){
      <span class="keyword" >return</span> document.getElementById(id);
    };
    window.addEventListener(<span class="string" >'load'</span>, <span class="keyword" >function</span> (){
      <span class="keyword" >var</span> socket = io.connect(<span class="string" >'169.254.10.100:8080'</span>);

      <span class="keyword" >var</span> chat = socket.of(<span class="string" >'/chat'</span>);
      chat.on(<span class="string" >'available_channel'</span>, <span class="keyword" >function</span>(channels){
        channels.forEach(<span class="keyword" >function</span> (channelId){
          <span class="keyword" >var</span> button = document.createElement(<span class="string" >'button'</span>);
          button.appendChild(document.createTextNode(channelId));

          $(<span class="string" >'rooms'</span>).appendChild(button);
          button.addEventListener(<span class="string" >'click'</span>, <span class="keyword" >function</span>(){
            $(<span class="string" >'chat'</span>).style.display = <span class="string" >''</span>;
            chat.emit(<span class="string" >'join'</span>, {
              channelId: channelId
            });
          });
        });
      });

      <span class="keyword" >var</span> addMessage = <span class="keyword" >function</span>(message){
        <span class="keyword" >var</span> li = document.createElement(<span class="string" >'li'</span>);
        li.appendChild(document.createTextNode(message));
        $(<span class="string" >'messages'</span>).appendChild(li);
      };
      chat.on(<span class="string" >'joined'</span>, <span class="keyword" >function</span>(messages){
        messages.forEach(<span class="keyword" >function</span>(message){
          <span class="keyword" >var</span> li = document.createElement(<span class="string" >'li'</span>);
          li.appendChild(document.createTextNode(message));
          $(<span class="string" >'archives'</span>).appendChild(li);
        });
      });
      chat.on(<span class="string" >'posted'</span>, <span class="keyword" >function</span>(value){
        <span class="keyword" >return</span> addMessage(<span class="string" >'you posted: '</span> + value.message);
      });
      chat.on(<span class="string" >'user:join'</span>, <span class="keyword" >function</span>(value){
        <span class="keyword" >return</span> addMessage(value.id + <span class="string" >' joined this channel'</span>);
      });
      chat.on(<span class="string" >'user:leave'</span>, <span class="keyword" >function</span>(value){
        <span class="keyword" >return</span> addMessage(value.id + <span class="string" >' leaved this channel'</span>);
      });
      chat.on(<span class="string" >'user:message'</span>, <span class="keyword" >function</span>(value){
        <span class="keyword" >return</span> addMessage(value.id + <span class="string" >' says '</span> + value.message);
      });

      $(<span class="string" >'submit'</span>).addEventListener(<span class="string" >'click'</span>, <span class="keyword" >function</span> (){
        <span class="keyword" >var</span> messageValue = $(<span class="string" >'message'</span>).value;
        <span class="keyword" >if</span>(/^\s+$/.test(messageValue)){
          <span class="keyword" >return</span>;
        }

        chat.emit(<span class="string" >'post'</span>, messageValue);
        $(<span class="string" >'message'</span>).value = <span class="string" >''</span>;
      });
    });
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=<span class="string" >"rooms"</span>&gt;
    &lt;/div&gt;
    &lt;div id=<span class="string" >"chat"</span> style=<span class="string" >"display:none"</span>&gt;
      &lt;input type=<span class="string" >"text"</span> id=<span class="string" >"message"</span> value=<span class="string" >""</span> placeholder=<span class="string" >"message here"</span> /&gt;
      &lt;input type=<span class="string" >"submit"</span> id=<span class="string" >"submit"</span> value=<span class="string" >"send"</span> /&gt;
      &lt;p&gt;archives&lt;/p&gt;
      &lt;ul id=<span class="string" >"archives"</span> style=<span class="string" >"color: #999"</span>&gt;&lt;/ul&gt;
      &lt;p&gt;messages&lt;/p&gt;
      &lt;ul id=<span class="string" >"messages"</span>&gt;&lt;/ul&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>

<p>手抜きながら短いですね。</p>

<h2>FAQ. 169.254.10.100 ってIPアドレスは何？</h2>

<p>これは、iphonesimulator とか android simulator で 127.0.0.1 とか 使えないので、
localhost(127.0.0.1) の alias を切ってます(OSX)</p>

<p>こんな感じのコマンドで割り当てます</p>
<pre>shell &gt; sudo ifconfig lo0 alias 169.254.10.100 netmask 0xffffff</pre>


<h2>その他。</h2>

<p>これはまったく、未確認情報で、hybiの実装を見てないんだけど、socket.io が parse error を吐く！ってことがあったら、<a href="https://gist.github.com/1768174">https://gist.github.com/1768174</a> のpatch を使ってみると治ることがあるらしい。</p>

<p>ということで、ひさびさにブログを更新した今日この頃。</p>]]>
</content:encoded>
</item><item>
<title>javascripterになろう。の巻 3章</title>
<link>http://blog.xole.net/article.php?id=778</link>
<pubDate>Mon, 22 Aug 2011 01:02:14 +09:00</pubDate>
<description>前回「紛失した。」って書いてあったハズなのに、とある人(@hika69)からの熱い熱意によって、描き起こしました。



javascripterになろう。の巻 3章



例によって、独自解釈部分を含む + 校正はしてないので...</description>
<content:encoded>
<![CDATA[<p>前回「紛失した。」って書いてあったハズなのに、<a href="https://twitter.com/#!/hika69">とある人(@hika69)</a>からの熱い熱意によって、描き起こしました。</p>

<ul>
<li>
<a href="http://xole.net/20110821.html">javascripterになろう。の巻 3章</a>
</li>
</ul>

<p>例によって、独自解釈部分を含む + 校正はしてないので、間違っている点があるかも。</p>]]>
</content:encoded>
</item><item>
<title>javascripterになろう。の社内勉強会</title>
<link>http://blog.xole.net/article.php?id=777</link>
<pubDate>Sun, 31 Jul 2011 23:48:24 +09:00</pubDate>
<description>社内で使っているモノにTitaniumとかSenchaとか増えてきたので、javascriptをより知ってもらおうと思って、社内勉強会で使った資料とか、今さら




javascripterになろう。の巻1章


javascr...</description>
<content:encoded>
<![CDATA[<p>社内で使っているモノにTitaniumとかSenchaとか増えてきたので、javascriptをより知ってもらおうと思って、社内勉強会で使った資料とか、今さら</p>

<p>
<ul>
<li>
<a href="http://xole.net/20110309.html">javascripterになろう。の巻1章</a>
</li>
<li>
<a href="http://xole.net/20110330.html">javascripterになろう。の巻2章</a>
</li>
</ul>
</p>

<p>地震とか色々あって3章が紛失してしまったけど、今のところ3章まで話したハズ。<br />
内容の校正とかしてないから、間違ってる点があるかも。</p>]]>
</content:encoded>
</item><item>
<title>みんな iolangage をやれば解決だ</title>
<link>http://blog.xole.net/article.php?id=776</link>
<pubDate>Mon, 13 Jun 2011 03:07:47 +09:00</pubDate>
<description>気がつくと今年も6月の中盤にさしかかろうとしてますね。
そんな中、Scalaの（いろいろと）話題があって、僕も数ヶ月前まではScalaで開発してたので、そういうのみてると何とも言えない気持ちになる。
というか、自分も使ってる言語が炎上(...</description>
<content:encoded>
<![CDATA[<p>気がつくと今年も6月の中盤にさしかかろうとしてますね。<br />
そんな中、Scalaの（いろいろと）話題があって、僕も<a href="http://blog.xole.net/article.php?id=766">数ヶ月前まではScalaで開発してた</a>ので、そういうのみてると何とも言えない気持ちになる。<br />
というか、自分も使ってる言語が炎上(?)してるのはなかなか見るに見兼ねるのである(昔はPHPの時もそういうのが嫌だった)</p>

<p>(中略)</p>

<p>ということで、ここでは <a href="http://iolanguage.com/">Io language</a> の素晴らしさに浸ってみようと思う(※ちなみに、ここでは Io 20080120ベースです)</p>

<h2>Io Language もマルチパラダイム言語だよ！</h2>
<p>Scalaの説明で、マルチパラダイム言語 って説明されてたりするけど、それなら Io Language にも当てはまる</p>

<h3>Actorが使える</h3>
<p>Scala(もとい Act1, Erlang)で有名になった Actor は Io Language でも使える</p>

<pre class="javascript">hoge := Object clone
hoge foo := method(a, b,
  (a + b) println
)

async1 := hoge @@foo(1, 2)
async2 := hoge @@foo(3, 4)
yield
yield
</pre>

<h3>Prototypeが使える</h3>
<p>Javascript(Self) で有名な Prototype は Io Language でも使える</p>

<pre class="javascript">Hoge := Object clone <span class="keyword" >do</span> (
  foo := method(<span class="string" >"hello world"</span>)
)

bar := Hoge clone
bar foo println

<span class="comment" >// appendProto</span>
baz := Object clone
baz appendProto(Hoge)
baz foo println
</pre>

<h3>メッセージ指向でもある</h3>
<p>Small Talkのようなメッセージ指向のやりとりもできる</p>

<pre class="javascript">a := <span class="keyword" >if</span>(1 &lt; 2, <span class="string" >"hello world"</span>, <span class="string" >"foo bar"</span>)
a split foreach(v, v uppercase println)
<span class="comment" >// ==&gt; HELLO</span>
<span class="comment" >//         WORLD</span>
</pre>

<h3>LISP風である</h3>
<p>関数プログラミングとかみたいに "("と")" で十分プログラミングできる。 ちなみに記号といえば @ と @@ くらいだったりする</p>

<pre class="javascript">
Object <span class="keyword" >do</span> (
  Lobby setSlot(<span class="string" >"Hoge"</span>, clone <span class="keyword" >do</span> (
    setSlot(<span class="string" >"foo"</span>, method(<span class="string" >"hello world"</span>))
    setSlot(<span class="string" >"bar"</span>, method(<span class="string" >"foo bar"</span>))
  ))
  writeln((block(
    <span class="string" >"#{foo}"</span> interpolate
  ) setScope(Hoge) call))
)

<span class="comment" >//</span>
<span class="comment" >// もう少し崩すと</span>
<span class="comment" >//</span>
Object <span class="keyword" >do</span> (
  writeln(block(
    <span class="string" >"#{foo}"</span> interpolate
  ) setScope(Lobby setSlot(<span class="string" >"Hoge"</span>, clone <span class="keyword" >do</span> (
    setSlot(<span class="string" >"foo"</span>, method(<span class="string" >"hello world"</span>))
    setSlot(<span class="string" >"bar"</span>, method(<span class="string" >"foo bar"</span>))
  ))) call)
)
</pre>

<h3>すべてがオブジェクトである</h3>
<p>インスタンスベースであるので、こまけぇことが気になりません</p>

<pre class="javascript">Range
Hoge := Object clone <span class="keyword" >do</span> (
  foo := 1 to(10)
)
hoge := Hoge clone <span class="keyword" >do</span> (
  bar := foo last to(<span class="string" >"20"</span> asNumber)
)
hoge bar foreach(v, v println)
<span class="comment" >// foo のインスタンスが共有されてるので rewind...</span>
Hoge foo rewind foreach(v, v println)</pre>

<p>※いい例が出てこない...</p>

<h3>C言語風である</h3>
<p>Cスタイルの記法ならいくつか用意されてる(return とか)</p>

<pre class="javascript">a := 1;
b := 10;

a = 1 + 2;
b = a * 10;

<span class="keyword" >for</span>(i, a, b,
  write(i);
);

<span class="keyword" >if</span>(a &lt; b) then (
  return write(<span class="string" >"a"</span>);
) <span class="keyword" >else</span> (
  return write(<span class="string" >"b"</span>);
)
</pre>
<p>※ C言語のようなスタイルが思いつかなかったので、手抜きです...</p>

<h3>VMだってある</h3>
<p>VMが必要なら、小さいけどVMは付いている。Collectorも用意されてる(※<a href="http://blog.xole.net/article.php?id=696">むかしやった</a>やつ</a>)</p>

<pre class="javascript">
<span class="comment" >//Collector setDebug(true)</span>
aaa := method(
    Hoge := Object clone <span class="keyword" >do</span>(
        foo ::= nil
    )
    Foo := Object clone <span class="keyword" >do</span>(
        hoge ::= nil
    )

    h := Hoge clone setFoo(Foo clone setHoge(Hoge clone))
    f := Foo clone setHoge(Hoge clone setFoo(Foo clone))
    h clone setFoo(f clone setHoge(h clone))
    f clone setHoge(h clone setFoo(f clone))

    l := List clone
    <span class="keyword" >for</span>(i, 0, 100, l append(h, f, i * 0.987654321))
)
test := method(100 repeat(aaa))

writeln(<span class="string" >"time: "</span>, Date clone cpuSecondsToRun(test), <span class="string" >" seconds"</span>)
writeln(<span class="string" >"mpa \t as \t mb \t time \t mbt \t gc"</span>)

lastTimeUsed := 0.0

list(1.01, 1.05, 1.1, 1.2, 1.5, 1.7, 2, 4) foreach(as,
    list(0.01, 0.1, 1, 2, 4, 16) foreach(mpa,
        Collector setMarksPerAlloc(mpa)
        Collector setAllocatedStep(as)
        
        time := Date clone cpuSecondsToRun(test)
        mb := (Collector maxAllocatedBytes / 1000000) asString(0, 2)
        
        writeln(
            Collector marksPerAlloc asString(0, 2), <span class="string" >"\t"</span>,
            Collector allocatedStep asString(0, 2), <span class="string" >"\t"</span>,
            mb, <span class="string" >"\t"</span>, 
            time asString(0, 2) , <span class="string" >"\t"</span>, 
            (mb asNumber * time) asString(0, 2), <span class="string" >"\t"</span>, 
            (100 * (Collector timeUsed - lastTimeUsed) / time) asString(2, 1), <span class="string" >"%"</span>
        )
        
        lastTimeUsed = Collector timeUsed

        <span class="comment" >// Collector showStats</span>
        writeln(<span class="string" >"collected items: "</span>, Collector collect)
        Collector resetMaxAllocatedBytes
    )
    <span class="string" >""</span> println
)
</pre>

<h3>new？こちとらcloneで2文字も多い</h3>
<p>clone地獄。ときどき with も使える。それでも4文字...。</p>

<pre class="javascript">a := List clone
a append(1)
a append(2)
a println

List <span class="keyword" >with</span>(3, 4) println
list(5, 6, 7) println

Object squareBrackets := Object getSlot(<span class="string" >"list"</span>);
[1, 2, 3] println

b := Map clone
b atPut(<span class="string" >"foo"</span>, 1)
b atPut(<span class="string" >"bar"</span>, 2)
b asObject println

Object curlyBrackets := method(
  map := Map clone
  call message arguments foreach(arg, arg setName(<span class="string" >"atPut"</span>); map doMessage(arg))
  map
)
{foo := 1, bar := 2} asObject println</pre>

<p>with が使えたり、listのようにシンタックスシュガーがあるけど、存在しない場合は自分で作るしかない。それが io style</p>

<h2>まとめ</h2>
<p>ということで、いろいろできる Io Language<br />
なんだ Io Language すばらしいじゃん。というお話しでした。</p>


<h2>その他</h2>

<h3>ブラウザで動かせない?</h3>
<p>いつか誰かが動かせるようにしてくれる。と期待中</p>
<p>※ 完全に放置してるけど、Rhino で <a href="https://github.com/nowelium/io.js">io.js</a> をつくってみたよ。ただ途中で秋田</p>

<h3>JVMで動かせない?</h3>
<p>
<a href="http://ioke.org/">Ioke</a>なんてものがあるらしい。</p>
<p>※ むかーし JavaCCの勉強で作ろうとした残骸 <a href="https://github.com/nowelium/Jo/blob/master/src/lang/jo/parser/JoParser.jjt">JoParser.jjt</a>。これも途中で放置</p>]]>
</content:encoded>
</item><item>
<title>やったー Titanium Mobile でも Socket.io が動いたよー　＼(^o^)／</title>
<link>http://blog.xole.net/article.php?id=775</link>
<pubDate>Sat, 07 May 2011 22:10:29 +09:00</pubDate>
<description>socket.ioでブラウザ依存の部分を使わないようにwrapper書いただけです。。
ref - https://github.com/nowelium/socket.io-titanium


ちなみに、module版が既にありま...</description>
<content:encoded>
<![CDATA[<p>socket.ioでブラウザ依存の部分を使わないようにwrapper書いただけです。。<br />
ref - <a href="https://github.com/nowelium/socket.io-titanium">https://github.com/nowelium/socket.io-titanium</a>
</p>

<p>ちなみに、module版が既にありました<br />
ref - <a href="https://github.com/saiten/TiSocketIO">https://github.com/saiten/TiSocketIO</a>
</p>

<h3>使い方</h3>

<p>socket.io(socket.io-nodeじゃない方)を含むので、--recursiveオプションでgit cloneする</p>

<pre>
git clone git@github.com:nowelium/socket.io-titanium.git --recursive
</pre>

<p>上のリポジトリを落としてきたら、こんな構成になってるので、example-nodejs-server/server.js を起動する(socket.ioはnpmとかでインストールしとくこと)</p>

<pre>
prj/
 - README
 - LICENSE
 - tiapp.xml
 - example-nodejs-server/
   - server.js
 - Resources/
  - app.js
  - socket.io-titanium.js
  - socket.io/
    - package.json
    - socket.io.js
    - lib/
      - io.js
      - socket.js
      - util.js
      - transport.js
      - transports/
        - xhr.js
        - xhr-polling.js
</pre>

<p>serverはこんなコードで</p>

<pre class="javascript">
<span class="keyword" >var</span> http = require(<span class="string" >'http'</span>);

<span class="keyword" >var</span> server = http.createServer(<span class="keyword" >function</span>(req, res){
  res.writeHead(200, {<span class="string" >'Content-Type'</span>: <span class="string" >'text/plain'</span>});
  res.end(<span class="string" >'hello world'</span>);
});
server.listen(8080);

<span class="keyword" >var</span> io = require(<span class="string" >'socket.io'</span>);
<span class="keyword" >var</span> socket = io.listen(server);
socket.on(<span class="string" >'connection'</span>, <span class="keyword" >function</span>(client){
  console.log(<span class="string" >'connect : '</span> + client.sessionId);
  client.on(<span class="string" >'message'</span>, <span class="keyword" >function</span> (message){
    console.log(<span class="string" >'client message: '</span> + message);
    client.send(<span class="string" >'rep:'</span> + message);
  });
  client.on(<span class="string" >'disconnect'</span>, <span class="keyword" >function</span>(){
    console.log(<span class="string" >'disconnect : '</span> + client.sessionId);
  });
});</pre>

<p>こんな感じで起動しておく</p>

<pre>
shell > node example-nodejs-server/server.js
</pre>

<p>Resources/app.jsはこんな感じにしてます。IPアドレスの部分は適宜必要に応じて変更してください</p>

<pre class="javascript">Titanium.UI.setBackgroundColor(<span class="string" >'#FFF'</span>);

require(<span class="string" >'socket.io-titanium'</span>);

<span class="keyword" >var</span> socket = <span class="keyword" >new</span> io.Socket(<span class="string" >'169.254.10.100'</span>, { port: 8080 });
socket.connect();
socket.send(<span class="string" >'hello world!!'</span>);
socket.on(<span class="string" >'message'</span>, <span class="keyword" >function</span> (message){
  Titanium.API.debug(<span class="string" >'got message: '</span> + message);
});

<span class="keyword" >var</span> win = Titanium.UI.createWindow({
  barColor: <span class="string" >'#369'</span>,
  tabBarHidden: <span class="keyword" >true</span>,
  title: <span class="string" >'demo'</span>
});
win.add(Titanium.UI.createLabel({
  text: <span class="string" >'check console output'</span>
}));

<span class="keyword" >var</span> tabGroup = Titanium.UI.createTabGroup();
tabGroup.addTab(Titanium.UI.createTab({
  window: win
}));  
tabGroup.open();</pre>

<p>後は Titanium Developer に Import Project して起動</p>

<h3>こんなログがみえるハズ</h3>

<p>起動してほっておくと、server側はこんな感じのログがでます</p>

<pre>
7 May 22:02:53 - socket.io ready - accepting connections
7 May 22:03:03 - Initializing client with transport "xhr-polling"
7 May 22:03:03 - Client 07882023160345852 connected
connect : 07882023160345852
client message: hello world!!
7 May 22:03:14 - Initializing client with transport "xhr-polling"
7 May 22:03:14 - Client 9657788660842925 connected
connect : 9657788660842925
disconnect : 07882023160345852
7 May 22:03:21 - Client 07882023160345852 disconnected
7 May 22:03:25 - Initializing client with transport "xhr-polling"
7 May 22:03:25 - Client 7632819225545973 connected
connect : 7632819225545973
</pre>

<p>クライアント(iphonesimulator)はこんなログを出してます</p>

<pre>
[INFO] Application started
[DEBUG] reading stylesheet from: /path/to/Library/Application Support/iPhone Simulator/4.2/Applications/36A39314-2736-4755-95B2-A459111F09F1/socketio.titanium.app/stylesheet.plist
[INFO] socketio.titanium/1.0 (1.6.2.878906d)
[DEBUG] Analytics is enabled = YES
2011-05-07 22:03:03.737 socketio.titanium[43279:207] [DEBUG] Reachability Flag Status Change: -R -----l- networkStatusForFlags
[DEBUG] loading: /path/to/Resources/app.js, resource: path/to/Resources/app_js
[DEBUG] loading: /path/to/Resources/socket.io-titanium.js, resource: path/to/Resources/socket_io-titanium_js
[DEBUG] loading: /path/to/Resources/socket.io/lib/io.js, resource: path/to/Resources/socket_io/lib/io_js
[DEBUG] loading: /path/to/Resources/socket.io/lib/socket.js, resource: path/to/Resources/socket_io/lib/socket_js
[DEBUG] loading: /path/to/Resources/socket.io/lib/transport.js, resource: path/to/Resources/socket_io/lib/transport_js
[DEBUG] loading: /path/to/Resources/socket.io/lib/transports/xhr.js, resource: path/to/Resources/socket_io/lib/transports/xhr_js
[DEBUG] loading: /path/to/Resources/socket.io/lib/transports/xhr-polling.js, resource: path/to/Resources/socket_io/lib/transports/xhr-polling_js
[DEBUG] application booted in 81.870973 ms
[DEBUG] got message: rep:hello world!!
2011-05-07 22:03:08.761 socketio.titanium[43279:9c03] [DEBUG] Reachability Flag Status Change: -R -----l- networkStatusForFlags
</pre>

<p>ということで、動いてくれたのでよしとする。</p>]]>
</content:encoded>
</item><item>
<title>JavaScriptでRPCゲーム的な。その３</title>
<link>http://blog.xole.net/article.php?id=774</link>
<pubDate>Fri, 06 May 2011 00:57:58 +09:00</pubDate>
<description>
その２からの続き
レベルアップするようにしてみる。難易度とかゲームバランスとかって難しくなってきた

Function.prototype.bind = function(obj){
  var __method__ = this...</description>
<content:encoded>
<![CDATA[<p>
<a href="http://blog.xole.net/article.php?id=773">その２</a>からの続き<br />
レベルアップするようにしてみる。難易度とかゲームバランスとかって難しくなってきた</p>

<pre class="javascript">Function.prototype.bind = <span class="keyword" >function</span>(obj){
  <span class="keyword" >var</span> __method__ = <span class="keyword" >this</span>;
  <span class="keyword" >var</span> __args__ = Array.prototype.slice.call(arguments);
  __args__.shift(); <span class="comment" >// obj</span>
  <span class="keyword" >return</span> <span class="keyword" >function</span> (){
    <span class="comment" >// concat args</span>
    <span class="keyword" >var</span> args = Array.prototype.slice.call(arguments);
    <span class="keyword" >return</span> __method__.apply(obj, __args__.concat(args));
  };
};

<span class="keyword" >var</span> AbstractBattleStrategy = <span class="keyword" >function</span> (character){
  <span class="keyword" >this</span>.character = character;
};
AbstractBattleStrategy.prototype.next = <span class="keyword" >function</span> (){};
AbstractBattleStrategy.prototype.attack = <span class="keyword" >function</span>(){};
AbstractBattleStrategy.prototype.guard = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> SimpleBattleStrategy = <span class="keyword" >function</span> (){
  AbstractBattleStrategy.apply(<span class="keyword" >this</span>, arguments);
};
SimpleBattleStrategy.prototype = AbstractBattleStrategy.prototype;
SimpleBattleStrategy.prototype.attack = <span class="keyword" >function</span>(){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.offence;
};
SimpleBattleStrategy.prototype.guard = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.defence;
};

<span class="keyword" >var</span> WoundedRecoveryBattleStrategy = <span class="keyword" >function</span> (){
  AbstractBattleStrategy.apply(<span class="keyword" >this</span>, arguments);
  <span class="keyword" >this</span>.recovery = <span class="keyword" >false</span>;
};
WoundedRecoveryBattleStrategy.prototype = AbstractBattleStrategy.prototype;
WoundedRecoveryBattleStrategy.prototype.attack = <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >this</span>.recovery){
    <span class="keyword" >this</span>.character.hp += 30;
    <span class="keyword" >return</span> 0;
  }
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.offence;
};
WoundedRecoveryBattleStrategy.prototype.guard = <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >this</span>.recovery){
    <span class="keyword" >return</span> 0;
  }
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.defence;
};
WoundedRecoveryBattleStrategy.prototype.next = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.recovery = <span class="keyword" >false</span>;

  <span class="keyword" >if</span>(<span class="keyword" >this</span>.character.hp &lt; 10){
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 3);
    <span class="keyword" >if</span>(1 == rand){
      <span class="keyword" >this</span>.recovery = <span class="keyword" >true</span>;
    }
  }
};

<span class="keyword" >var</span> AbstractLevelStrategy = <span class="keyword" >function</span> (character, proto){
  <span class="keyword" >this</span>.character = character;
  <span class="keyword" >this</span>.proto = proto;
};
AbstractLevelStrategy.prototype.execute = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> SimpleLevelStrategy = <span class="keyword" >function</span>(){
  AbstractLevelStrategy.apply(<span class="keyword" >this</span>, arguments);
};
SimpleLevelStrategy.prototype.execute = <span class="keyword" >function</span> (){
  <span class="keyword" >var</span> level = <span class="keyword" >this</span>.character.level;
  <span class="keyword" >var</span> proto = <span class="keyword" >this</span>.proto;

  <span class="keyword" >this</span>.character.strength = level * proto.strength;
  <span class="keyword" >this</span>.character.agility = level * proto.agility;
  <span class="keyword" >this</span>.character.vitality = level * proto.vitality;
  <span class="keyword" >this</span>.character.intelligence = level * proto.intelligence;
  <span class="keyword" >this</span>.character.dexterity = level * proto.dexterity;
  <span class="keyword" >this</span>.character.lucky = level * proto.lucky;

  <span class="keyword" >this</span>.character.offence = <span class="keyword" >this</span>.character.strength + <span class="keyword" >this</span>.character.agility;
  <span class="keyword" >this</span>.character.defence = <span class="keyword" >this</span>.character.vitality + <span class="keyword" >this</span>.character.dexterity;
  <span class="keyword" >this</span>.character.hp = level * proto.hp;
};

<span class="keyword" >var</span> AbstractCharacter = <span class="keyword" >function</span> (){};
AbstractCharacter.prototype.strength = 0;
AbstractCharacter.prototype.agility = 0;
AbstractCharacter.prototype.vitality = 0;
AbstractCharacter.prototype.intelligence = 0;
AbstractCharacter.prototype.dexterity = 0;
AbstractCharacter.prototype.lucky = 0;
AbstractCharacter.prototype.name = <span class="string" >''</span>;
AbstractCharacter.prototype.hp = 0;
AbstractCharacter.prototype.level = 1;
AbstractCharacter.prototype.experience = 0;
AbstractCharacter.prototype.offence = 0;
AbstractCharacter.prototype.defence = 0;
AbstractCharacter.prototype.battleStrategy = <span class="keyword" >null</span>;
AbstractCharacter.prototype.levelStrategy = <span class="keyword" >null</span>;
AbstractCharacter.prototype.battle = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >new</span> Battle(<span class="keyword" >this</span>, <span class="keyword" >this</span>.battleStrategy);
};
AbstractCharacter.prototype.setLevel = <span class="keyword" >function</span>(level){
  <span class="keyword" >this</span>.level = level;
  <span class="keyword" >this</span>.levelStrategy.execute();
};

<span class="keyword" >var</span> Slime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.battleStrategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
  <span class="keyword" >this</span>.levelStrategy = <span class="keyword" >new</span> SimpleLevelStrategy(<span class="keyword" >this</span>, Slime.prototype);
};
Slime.prototype = <span class="keyword" >new</span> AbstractCharacter();
Slime.prototype.name = <span class="string" >'スライム'</span>;
Slime.prototype.hp = 20;
Slime.prototype.experience = 10;
Slime.prototype.strength = 3;
Slime.prototype.agility = 3;
Slime.prototype.vitality = 3;
Slime.prototype.intelligence = 0;
Slime.prototype.dexterity = 3;
Slime.prototype.lucky = 0;

<span class="keyword" >var</span> HoimiSlime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.battleStrategy = <span class="keyword" >new</span> WoundedRecoveryBattleStrategy(<span class="keyword" >this</span>);
  <span class="keyword" >this</span>.levelStrategy = <span class="keyword" >new</span> SimpleLevelStrategy(<span class="keyword" >this</span>, HoimiSlime.prototype);
};
HoimiSlime.prototype = <span class="keyword" >new</span> AbstractCharacter();
HoimiSlime.prototype.name = <span class="string" >'ホイミスライム'</span>;
HoimiSlime.prototype.hp = 25;
HoimiSlime.prototype.experience = 50;
HoimiSlime.prototype.strength = 5;
HoimiSlime.prototype.agility = 5;
HoimiSlime.prototype.vitality = 2;
HoimiSlime.prototype.intelligence = 0;
HoimiSlime.prototype.dexterity = 2;
HoimiSlime.prototype.lucky = 0;

<span class="keyword" >var</span> KingSlime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.battleStrategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
  <span class="keyword" >this</span>.levelStrategy = <span class="keyword" >new</span> SimpleLevelStrategy(<span class="keyword" >this</span>, KingSlime.prototype);
};
KingSlime.prototype = <span class="keyword" >new</span> AbstractCharacter();
KingSlime.prototype.name = <span class="string" >'キングスライム'</span>;
KingSlime.prototype.hp = 50;
KingSlime.prototype.experience = 80;
KingSlime.prototype.strength = 8;
KingSlime.prototype.agility = 12;
KingSlime.prototype.vitality = 8;
KingSlime.prototype.intelligence = 0;
KingSlime.prototype.dexterity = 8;
KingSlime.prototype.lucky = 1;

<span class="keyword" >var</span> Hero = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.battleStrategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
  <span class="keyword" >this</span>.levelStrategy = <span class="keyword" >new</span> SimpleLevelStrategy(<span class="keyword" >this</span>, Hero.prototype);
};
Hero.prototype = <span class="keyword" >new</span> AbstractCharacter();
Hero.prototype.name = <span class="string" >'勇者'</span>;
Hero.prototype.hp = 100;
Hero.prototype.experience = 50;
Hero.prototype.strength = 10;
Hero.prototype.agility = 10;
Hero.prototype.vitality = 10;
Hero.prototype.intelligence = 10;
Hero.prototype.dexterity = 10;
Hero.prototype.lucky = 10;

<span class="keyword" >var</span> Battle = <span class="keyword" >function</span>(character, strategy){
  <span class="keyword" >this</span>.character = character;
  <span class="keyword" >this</span>.strategy = strategy;
};
Battle.prototype.logger = <span class="keyword" >null</span>;
Battle.prototype.start = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'の戦闘開始: Lv.'</span> + <span class="keyword" >this</span>.getLevel() + <span class="string" >' HP: '</span> + <span class="keyword" >this</span>.getHp());
};
Battle.prototype.attack = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.strategy.attack();
};
Battle.prototype.guard = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.strategy.guard();
};
Battle.prototype.next = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.strategy.next();
};
Battle.prototype.end = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'は戦闘を離脱した'</span>);
};
Battle.prototype.isDie = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.hp &lt; 1;
};
Battle.prototype.isAgilityQuick = <span class="keyword" >function</span>(target){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.agility &lt; target.character.agility;
};
Battle.prototype.addDamage = <span class="keyword" >function</span> (damage){
  <span class="keyword" >this</span>.character.hp -= damage;
};
Battle.prototype.addExperience = <span class="keyword" >function</span>(experience){
  <span class="keyword" >this</span>.character.experience += experience;
};
Battle.prototype.getExperience = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.experience;
};
Battle.prototype.getLevel = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.level;
};
Battle.prototype.getHp = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.hp;
};
Battle.prototype.getName = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.name;
};
Battle.prototype.getLucky = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.lucky;
};
Battle.prototype.offence = <span class="keyword" >function</span>(defencer){
  <span class="keyword" >var</span> attackValue = <span class="keyword" >this</span>.attack();
  <span class="keyword" >var</span> guardValue = defencer.guard();

  <span class="keyword" >if</span>(0 &lt; <span class="keyword" >this</span>.getLucky()){
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 100);
    <span class="keyword" >if</span>(<span class="keyword" >this</span>.getLucky() == rand){
      <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'のクリティカル！'</span>);
      attackValue = attackValue * 2;
    }
  }

  <span class="keyword" >var</span> damage = 0;
  <span class="keyword" >if</span>(guardValue &lt; attackValue){
    damage = attackValue - guardValue;
  }
  defencer.addDamage(damage);

  <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'の攻撃: '</span> + defencer.getName() + <span class="string" >'に'</span> + damage + <span class="string" >'のダメージ'</span>);

  <span class="keyword" >if</span>(defencer.isDie()){
    <span class="keyword" >this</span>.addExperience(defencer.getExperience());
    <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(defencer.getName() + <span class="string" >'は倒れた'</span>);
  }
};

<span class="keyword" >var</span> TurnBattle = <span class="keyword" >function</span>(hero, monsterList){
  <span class="keyword" >this</span>.hero = hero;
  <span class="keyword" >this</span>.monsterList = monsterList;
};
TurnBattle.prototype.logger = <span class="keyword" >null</span>;
TurnBattle.prototype.execute = <span class="keyword" >function</span>(){
  <span class="keyword" >var</span> heroBattle = <span class="keyword" >this</span>.hero.battle();
  <span class="keyword" >try</span> {
    heroBattle.start();

    <span class="keyword" >var</span> monsterBattles = <span class="keyword" >this</span>.monsterList.map(<span class="keyword" >function</span>(monster){
      <span class="keyword" >var</span> battle = monster.battle();
      battle.start();
      <span class="keyword" >return</span> battle;
    });

    <span class="keyword" >var</span> turn = 1;
    <span class="keyword" >while</span>(<span class="keyword" >true</span>){
      <span class="keyword" >this</span>.logger.log(<span class="string" >'ターン： '</span> + turn);

      <span class="comment" >// 攻撃対象をランダムで設定</span>
      <span class="keyword" >var</span> rand = Math.floor(Math.random() * monsterBattles.length);
      <span class="keyword" >var</span> monsterBattle = monsterBattles[rand];

      <span class="keyword" >this</span>.battle(heroBattle, monsterBattle);

      monsterBattles = monsterBattles.filter((<span class="keyword" >function</span>(monsterBattle){
        <span class="keyword" >if</span>(monsterBattle.isDie()){
          monsterBattle.end();
          <span class="keyword" >return</span> <span class="keyword" >false</span>;
        }
        <span class="keyword" >return</span> <span class="keyword" >true</span>;
      }).bind(<span class="keyword" >this</span>));

      <span class="keyword" >if</span>(monsterBattles.length &lt; 1){
        <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(heroBattle.getName() + <span class="string" >'は勝利した'</span>);
      }
      <span class="keyword" >if</span>(heroBattle.isDie()){
        <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(heroBattle.getName() + <span class="string" >'は死んでしまった'</span>);
      }

      turn = turn + 1;
    }
  } <span class="keyword" >finally</span> {
    heroBattle.end();
  }
}
TurnBattle.prototype.battle = <span class="keyword" >function</span> (hero, monster){
  <span class="keyword" >try</span> {
    <span class="keyword" >if</span>(hero.isAgilityQuick(monster)){
      monster.offence(hero);
      hero.offence(monster);
    } <span class="keyword" >else</span> {
      hero.offence(monster);
      monster.offence(hero);
    }
  } <span class="keyword" >finally</span> {
    hero.next();
    monster.next();
  }
};

<span class="keyword" >var</span> AbstractQuestState = <span class="keyword" >function</span>(){};
AbstractQuestState.prototype.next = <span class="keyword" >true</span>;
AbstractQuestState.prototype.execute = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> QuestStartState = <span class="keyword" >function</span> (){};
QuestStartState.prototype = <span class="keyword" >new</span> AbstractQuestState();
QuestStartState.prototype.execute = <span class="keyword" >function</span> (quest){
  quest.logger.log(<span class="string" >'クエスト開始'</span>);
  quest.count = 0;
  quest.hero.setLevel(1);
  quest.state = <span class="keyword" >new</span> NextQuestState();
};

<span class="keyword" >var</span> QuestEndState = <span class="keyword" >function</span> (){};
QuestEndState.prototype = <span class="keyword" >new</span> AbstractQuestState();
QuestEndState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(quest.count + <span class="string" >'回のクエストを終えた'</span>);
  quest.logger.log(<span class="string" >'クエスト終了'</span>);
  quest.state.next = <span class="keyword" >false</span>;
};

<span class="keyword" >var</span> NextQuestState = <span class="keyword" >function</span> (){};
NextQuestState.prototype = <span class="keyword" >new</span> AbstractQuestState();
NextQuestState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(<span class="string" >'移動中...'</span>);

  quest.state = <span class="keyword" >this</span>.createState();
  quest.count = quest.count + 1;
};
NextQuestState.prototype.createState = <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(10 &lt; quest.count){
    <span class="keyword" >return</span> <span class="keyword" >new</span> QuestEndState();
  }
  <span class="keyword" >if</span>(quest.hero.hp &lt; 1){
    <span class="keyword" >return</span> <span class="keyword" >new</span> QuestEndState();
  }

  <span class="keyword" >var</span> rand = Math.floor(Math.random() * 10);
  <span class="keyword" >if</span>(rand &lt; 2){
    <span class="keyword" >return</span> <span class="keyword" >new</span> NextQuestState();
  }
  <span class="keyword" >if</span>(rand &lt; 9){
    <span class="keyword" >return</span> <span class="keyword" >new</span> BattleState();
  }
  <span class="keyword" >return</span> <span class="keyword" >new</span> RecoveryLakeState();
};

<span class="keyword" >var</span> BattleState = <span class="keyword" >function</span> (){};
BattleState.prototype = <span class="keyword" >new</span> AbstractQuestState();
BattleState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(<span class="string" >'モンスターに遭遇した'</span>);

  <span class="keyword" >var</span> monsters = [];
  <span class="keyword" >var</span> monsterCount = Math.floor(Math.random() * 10) + 1;
  <span class="keyword" >for</span>(<span class="keyword" >var</span> i = 0; i &lt; monsterCount; ++i){
    monsters.push(<span class="keyword" >this</span>.createMonster(quest.hero.level));
  }
  monsters.forEach(<span class="keyword" >function</span> (monster){
    <span class="keyword" >var</span> monsterLevel = Math.floor(Math.random() * quest.hero.level) + 1;
    monster.setLevel(monsterLevel);
  });

  <span class="keyword" >var</span> battle = <span class="keyword" >new</span> TurnBattle(quest.hero, monsters);
  battle.execute();

  <span class="keyword" >if</span>(quest.hero.hp &lt; 1){
    quest.state = <span class="keyword" >new</span> QuestEndState();
  } <span class="keyword" >else</span> {
    quest.state = <span class="keyword" >new</span> CheckExperienceState();
  }
};
BattleState.prototype.createMonster = <span class="keyword" >function</span>(heroLevel){
  <span class="keyword" >var</span> battleDifficulty = Math.floor(Math.random() * heroLevel);
  <span class="keyword" >if</span>(battleDifficulty &lt; 3){
    <span class="keyword" >return</span> <span class="keyword" >new</span> Slime();
  }
  <span class="keyword" >if</span>(battleDifficulty &lt; 6){
    <span class="keyword" >return</span> <span class="keyword" >new</span> HoimiSlime();
  }
  <span class="keyword" >return</span> <span class="keyword" >new</span> KingSlime();
};
<span class="keyword" >var</span> CheckExperienceState = <span class="keyword" >function</span> (){};
CheckExperienceState.prototype = <span class="keyword" >new</span> AbstractQuestState();
CheckExperienceState.prototype.execute = <span class="keyword" >function</span>(quest){
  <span class="keyword" >var</span> levelUpExperience = Hero.prototype.experience * quest.hero.level;
  <span class="keyword" >if</span>(levelUpExperience &lt;= quest.hero.experience){
    <span class="keyword" >var</span> nextLevel = quest.hero.level + 1;
    quest.hero.setLevel(nextLevel);
    quest.logger.log(quest.hero.name + <span class="string" >'はレベルが上がった: Lv.'</span> + nextLevel);
  }

  quest.state = <span class="keyword" >new</span> NextQuestState();
};
<span class="keyword" >var</span> RecoveryLakeState = <span class="keyword" >function</span> (){};
RecoveryLakeState.prototype = <span class="keyword" >new</span> AbstractQuestState();
RecoveryLakeState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(<span class="string" >'回復の泉を発見'</span>);

  <span class="keyword" >var</span> value = Math.floor(Math.random() * 49) + 1;
  quest.hero.hp += value;

  quest.logger.log(value + <span class="string" >'回復した'</span>);
  
  quest.state = <span class="keyword" >new</span> NextQuestState();
};

<span class="keyword" >var</span> Quest = <span class="keyword" >function</span>(hero){
  <span class="keyword" >this</span>.hero = hero;
  <span class="keyword" >this</span>.count = 0;
  <span class="keyword" >this</span>.state = <span class="keyword" >new</span> QuestStartState();
};
Quest.prototype.logger = <span class="keyword" >new</span> <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >typeof</span> console != <span class="string" >'undefined'</span>){
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      console.log(message);
    };
  } <span class="keyword" >else</span> {
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      print(message);
    };
  }
};
Quest.prototype.start = <span class="keyword" >function</span> (){
  Battle.prototype.logger = <span class="keyword" >this</span>.logger;
  TurnBattle.prototype.logger = <span class="keyword" >this</span>.logger;

  <span class="keyword" >while</span>(<span class="keyword" >this</span>.state.next){
    <span class="keyword" >this</span>.state.execute(<span class="keyword" >this</span>);
  }
};

<span class="keyword" >var</span> hero = <span class="keyword" >new</span> Hero();
<span class="keyword" >var</span> quest = <span class="keyword" >new</span> Quest(hero);
quest.start();
</pre>

<p>レベルアップは簡単にこんな感じでいいのだろうか</p>

<pre class="javascript">
<span class="keyword" >var</span> AbstractLevelStrategy = <span class="keyword" >function</span> (character, proto){
  <span class="keyword" >this</span>.character = character;
  <span class="keyword" >this</span>.proto = proto;
};
AbstractLevelStrategy.prototype.execute = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> SimpleLevelStrategy = <span class="keyword" >function</span>(){
  AbstractLevelStrategy.apply(<span class="keyword" >this</span>, arguments);
};
SimpleLevelStrategy.prototype.execute = <span class="keyword" >function</span> (){
  <span class="keyword" >var</span> level = <span class="keyword" >this</span>.character.level;
  <span class="keyword" >var</span> proto = <span class="keyword" >this</span>.proto;

  <span class="keyword" >this</span>.character.strength = level * proto.strength;
  <span class="keyword" >this</span>.character.agility = level * proto.agility;
  <span class="keyword" >this</span>.character.vitality = level * proto.vitality;
  <span class="keyword" >this</span>.character.intelligence = level * proto.intelligence;
  <span class="keyword" >this</span>.character.dexterity = level * proto.dexterity;
  <span class="keyword" >this</span>.character.lucky = level * proto.lucky;

  <span class="keyword" >this</span>.character.offence = <span class="keyword" >this</span>.character.strength + <span class="keyword" >this</span>.character.agility;
  <span class="keyword" >this</span>.character.defence = <span class="keyword" >this</span>.character.vitality + <span class="keyword" >this</span>.character.dexterity;
  <span class="keyword" >this</span>.character.hp = level * proto.hp;
};</pre>

<p>Character毎のprototype(根元の設定値)の参照とかコンストラクタでやってしまう</p>

<pre class="javascript">
<span class="keyword" >var</span> Slime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.battleStrategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
  <span class="keyword" >this</span>.levelStrategy = <span class="keyword" >new</span> SimpleLevelStrategy(<span class="keyword" >this</span>, Slime.prototype);
};
Slime.prototype = <span class="keyword" >new</span> AbstractCharacter();
Slime.prototype.name = <span class="string" >'スライム'</span>;
Slime.prototype.hp = 20;
Slime.prototype.experience = 10;
Slime.prototype.strength = 3;
Slime.prototype.agility = 3;
Slime.prototype.vitality = 3;
Slime.prototype.intelligence = 0;
Slime.prototype.dexterity = 3;
Slime.prototype.lucky = 0;</pre>

<p>んで、こんな感じに。</p>

<pre class="javascript">クエスト開始
移動中...
モンスターに遭遇した
勇者の戦闘開始: Lv.1 HP: 100
スライムの戦闘開始: Lv.1 HP: 20
スライムの戦闘開始: Lv.1 HP: 20
スライムの戦闘開始: Lv.1 HP: 20
スライムの戦闘開始: Lv.1 HP: 20
スライムの戦闘開始: Lv.1 HP: 20
スライムの戦闘開始: Lv.1 HP: 20
ターン： 1
勇者の攻撃: スライムに14のダメージ
スライムの攻撃: 勇者に0のダメージ
ターン： 2
勇者の攻撃: スライムに14のダメージ
スライムの攻撃: 勇者に0のダメージ
ターン： 3
勇者の攻撃: スライムに14のダメージ
スライムの攻撃: 勇者に0のダメージ
...
勇者はレベルが上がった: Lv.3
移動中...
モンスターに遭遇した
勇者の戦闘開始: Lv.3 HP: 300
スライムの戦闘開始: Lv.2 HP: 40
スライムの戦闘開始: Lv.1 HP: 20
スライムの戦闘開始: Lv.3 HP: 60
スライムの戦闘開始: Lv.1 HP: 20
ターン： 1
勇者の攻撃: スライムに42のダメージ
スライムの攻撃: 勇者に0のダメージ
ターン： 2
勇者の攻撃: スライムに54のダメージ
スライムは倒れた
スライムの攻撃: 勇者に0のダメージ
スライムは戦闘を離脱した
ターン： 3
勇者の攻撃: スライムに54のダメージ
スライムは倒れた
スライムの攻撃: 勇者に0のダメージ
スライムは戦闘を離脱した
...
勇者はレベルが上がった: Lv.7
移動中...
回復の泉を発見
1回復した
移動中...
モンスターに遭遇した
勇者の戦闘開始: Lv.7 HP: 701
スライムの戦闘開始: Lv.2 HP: 40
スライムの戦闘開始: Lv.4 HP: 80
ホイミスライムの戦闘開始: Lv.1 HP: 25
ホイミスライムの戦闘開始: Lv.6 HP: 150
スライムの戦闘開始: Lv.5 HP: 100
スライムの戦闘開始: Lv.1 HP: 20
キングスライムの戦闘開始: Lv.7 HP: 350
ターン： 1
勇者の攻撃: スライムに134のダメージ
スライムは倒れた
スライムの攻撃: 勇者に0のダメージ
スライムは戦闘を離脱した
ターン： 2
キングスライムの攻撃: 勇者に0のダメージ
勇者の攻撃: キングスライムに28のダメージ
ターン： 3
キングスライムの攻撃: 勇者に0のダメージ
勇者の攻撃: キングスライムに28のダメージ
ターン： 4
勇者の攻撃: スライムに128のダメージ
スライムは倒れた
スライムの攻撃: 勇者に0のダメージ
スライムは戦闘を離脱した
ターン： 5
勇者の攻撃: ホイミスライムに136のダメージ
ホイミスライムは倒れた
ホイミスライムの攻撃: 勇者に0のダメージ
ホイミスライムは戦闘を離脱した
...
キングスライムは倒れた
キングスライムは戦闘を離脱した
勇者は勝利した
勇者は戦闘を離脱した
勇者はレベルが上がった: Lv.8
移動中...
回復の泉を発見
20回復した
移動中...
12回のクエストを終えた
クエスト終了</pre>

<p>大きなロジックは終わったかな<br />
フロア移動とかボスとか装備品とかそんなのがあるとよさそうかも</p>]]>
</content:encoded>
</item><item>
<title>JavaScriptでRPCゲーム的な。その２</title>
<link>http://blog.xole.net/article.php?id=773</link>
<pubDate>Thu, 05 May 2011 21:28:58 +09:00</pubDate>
<description>
前回のあれだと、戦闘しかなかったので、もう少し色々歩きまわるようにしてみた

Function.prototype.bind = function(obj){
  var __method__ = this;
  var __ar...</description>
<content:encoded>
<![CDATA[<p>
<a href="http://blog.xole.net/article.php?id=772">前回のあれ</a>だと、戦闘しかなかったので、もう少し色々歩きまわるようにしてみた</p>

<pre class="javascript">Function.prototype.bind = <span class="keyword" >function</span>(obj){
  <span class="keyword" >var</span> __method__ = <span class="keyword" >this</span>;
  <span class="keyword" >var</span> __args__ = Array.prototype.slice.call(arguments);
  __args__.shift(); <span class="comment" >// obj</span>
  <span class="keyword" >return</span> <span class="keyword" >function</span> (){
    <span class="comment" >// concat args</span>
    <span class="keyword" >var</span> args = Array.prototype.slice.call(arguments);
    <span class="keyword" >return</span> __method__.apply(obj, __args__.concat(args));
  };
};

<span class="keyword" >var</span> AbstractBattleStrategy = <span class="keyword" >function</span> (character){
  <span class="keyword" >this</span>.character = character;
};
AbstractBattleStrategy.prototype.next = <span class="keyword" >function</span> (){};
AbstractBattleStrategy.prototype.attack = <span class="keyword" >function</span>(){};
AbstractBattleStrategy.prototype.guard = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> SimpleBattleStrategy = <span class="keyword" >function</span> (){
  AbstractBattleStrategy.apply(<span class="keyword" >this</span>, arguments);
};
SimpleBattleStrategy.prototype = AbstractBattleStrategy.prototype;
SimpleBattleStrategy.prototype.attack = <span class="keyword" >function</span>(){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.offence;
};
SimpleBattleStrategy.prototype.guard = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.defence;
};

<span class="keyword" >var</span> WoundedRecoveryBattleStrategy = <span class="keyword" >function</span> (){
  AbstractBattleStrategy.apply(<span class="keyword" >this</span>, arguments);
  <span class="keyword" >this</span>.recovery = <span class="keyword" >false</span>;
};
WoundedRecoveryBattleStrategy.prototype = AbstractBattleStrategy.prototype;
WoundedRecoveryBattleStrategy.prototype.attack = <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >this</span>.recovery){
    <span class="keyword" >this</span>.character.hp += 30;
    <span class="keyword" >return</span> 0;
  }
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.offence;
};
WoundedRecoveryBattleStrategy.prototype.guard = <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >this</span>.recovery){
    <span class="keyword" >return</span> 0;
  }
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.defence;
};
WoundedRecoveryBattleStrategy.prototype.next = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.recovery = <span class="keyword" >false</span>;

  <span class="keyword" >if</span>(<span class="keyword" >this</span>.character.hp &lt; 10){
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 3);
    <span class="keyword" >if</span>(1 == rand){
      <span class="keyword" >this</span>.recovery = <span class="keyword" >true</span>;
    }
  }
};

<span class="keyword" >var</span> AbstractCharacter = <span class="keyword" >function</span> (){};
AbstractCharacter.prototype.strength = 0;
AbstractCharacter.prototype.agility = 0;
AbstractCharacter.prototype.vitality = 0;
AbstractCharacter.prototype.intelligence = 0;
AbstractCharacter.prototype.dexterity = 0;
AbstractCharacter.prototype.lucky = 0;
AbstractCharacter.prototype.name = <span class="string" >''</span>;
AbstractCharacter.prototype.hp = 0;
AbstractCharacter.prototype.offence = 0;
AbstractCharacter.prototype.defence = 0;
AbstractCharacter.prototype.strategy = <span class="keyword" >null</span>;
AbstractCharacter.prototype.battle = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >new</span> Battle(<span class="keyword" >this</span>);
};

<span class="keyword" >var</span> Slime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.strategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
};
Slime.prototype = <span class="keyword" >new</span> AbstractCharacter();
Slime.prototype.name = <span class="string" >'スライム'</span>;
Slime.prototype.hp = 20;
Slime.prototype.lucky = 0;
Slime.prototype.offence = 3;
Slime.prototype.defence = 3;

<span class="keyword" >var</span> HoimiSlime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.strategy = <span class="keyword" >new</span> WoundedRecoveryBattleStrategy(<span class="keyword" >this</span>);
};
HoimiSlime.prototype = <span class="keyword" >new</span> AbstractCharacter();
HoimiSlime.prototype.name = <span class="string" >'ホイミスライム'</span>;
HoimiSlime.prototype.hp = 25;
HoimiSlime.prototype.offence = 2;
HoimiSlime.prototype.defence = 2;

<span class="keyword" >var</span> KingSlime = <span class="keyword" >function</span>() {
  <span class="keyword" >this</span>.strategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
};
KingSlime.prototype = <span class="keyword" >new</span> AbstractCharacter();
KingSlime.prototype.name = <span class="string" >'キングスライム'</span>;
KingSlime.prototype.hp = 50;
KingSlime.prototype.agility = 12;
KingSlime.prototype.lucky = 1;
KingSlime.prototype.offence = 8;
KingSlime.prototype.defence = 8;

<span class="keyword" >var</span> Hero = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.strategy = <span class="keyword" >new</span> SimpleBattleStrategy(<span class="keyword" >this</span>);
};
Hero.prototype = <span class="keyword" >new</span> AbstractCharacter();
Hero.prototype.name = <span class="string" >'勇者'</span>;
Hero.prototype.hp = 300;
Hero.prototype.agility = 10;
Hero.prototype.lucky = 10;
Hero.prototype.offence = 12;
Hero.prototype.defence = 12;

<span class="keyword" >var</span> Battle = <span class="keyword" >function</span>(character){
  <span class="keyword" >this</span>.character = character;
};
Battle.prototype.logger = <span class="keyword" >null</span>;
Battle.prototype.start = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'の戦闘開始'</span>);
};
Battle.prototype.attack = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.strategy.attack();
};
Battle.prototype.guard = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.strategy.guard();
};
Battle.prototype.next = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.strategy.next();
};
Battle.prototype.end = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'は戦闘を離脱した'</span>);
};
Battle.prototype.isDie = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.hp &lt; 1;
};
Battle.prototype.isAgilityQuick = <span class="keyword" >function</span>(target){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.agility &lt; target.character.agility;
};
Battle.prototype.setDamage = <span class="keyword" >function</span> (damage){
  <span class="keyword" >this</span>.character.hp -= damage;
};
Battle.prototype.getHp = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.hp;
};
Battle.prototype.getName = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.name;
};
Battle.prototype.getLucky = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.character.lucky;
};
Battle.prototype.offence = <span class="keyword" >function</span>(defencer){
  <span class="keyword" >var</span> attackValue = <span class="keyword" >this</span>.attack();
  <span class="keyword" >var</span> guardValue = defencer.guard();

  <span class="keyword" >if</span>(0 &lt; <span class="keyword" >this</span>.getLucky()){
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 100);
    <span class="keyword" >if</span>(<span class="keyword" >this</span>.getLucky() == rand){
      <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'のクリティカル！'</span>);
      attackValue = attackValue * 2;
    }
  }

  <span class="keyword" >var</span> damage = 0;
  <span class="keyword" >if</span>(guardValue &lt; attackValue){
    damage = attackValue - guardValue;
  }
  defencer.setDamage(damage);

  <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.getName() + <span class="string" >'の攻撃: '</span> + defencer.getName() + <span class="string" >'に'</span> + damage + <span class="string" >'のダメージ'</span>);

  <span class="keyword" >if</span>(defencer.isDie()){
    <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(defencer.getName() + <span class="string" >'は倒れた'</span>);
  }
};

<span class="keyword" >var</span> TurnBattle = <span class="keyword" >function</span>(hero, monsterList){
  <span class="keyword" >this</span>.hero = hero;
  <span class="keyword" >this</span>.monsterList = monsterList;
};
TurnBattle.prototype.logger = <span class="keyword" >null</span>;
TurnBattle.prototype.execute = <span class="keyword" >function</span>(){
  <span class="keyword" >var</span> heroBattle = <span class="keyword" >this</span>.hero.battle();
  <span class="keyword" >try</span> {
    heroBattle.start();

    <span class="keyword" >var</span> monsterBattles = <span class="keyword" >this</span>.monsterList.map(<span class="keyword" >function</span>(monster){
      <span class="keyword" >var</span> battle = monster.battle();
      battle.start();
      <span class="keyword" >return</span> battle;
    });

    <span class="keyword" >var</span> turn = 1;
    <span class="keyword" >while</span>(<span class="keyword" >true</span>){
      <span class="keyword" >this</span>.logger.log(<span class="string" >'ターン： '</span> + turn);

      <span class="comment" >// 攻撃対象をランダムで設定</span>
      <span class="keyword" >var</span> rand = Math.floor(Math.random() * monsterBattles.length);
      <span class="keyword" >var</span> monsterBattle = monsterBattles[rand];

      <span class="keyword" >this</span>.battle(heroBattle, monsterBattle);

      monsterBattles = monsterBattles.filter((<span class="keyword" >function</span>(monsterBattle){
        <span class="keyword" >if</span>(monsterBattle.isDie()){
          monsterBattle.end();
          <span class="keyword" >return</span> <span class="keyword" >false</span>;
        }
        <span class="keyword" >return</span> <span class="keyword" >true</span>;
      }).bind(<span class="keyword" >this</span>));

      <span class="keyword" >if</span>(monsterBattles.length &lt; 1){
        <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(heroBattle.getName() + <span class="string" >'は勝利した'</span>);
      }
      <span class="keyword" >if</span>(heroBattle.isDie()){
        <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(heroBattle.getName() + <span class="string" >'は死んでしまった'</span>);
      }

      turn = turn + 1;
    }
  } <span class="keyword" >finally</span> {
    heroBattle.end();
  }
}
TurnBattle.prototype.battle = <span class="keyword" >function</span> (hero, monster){
  <span class="keyword" >try</span> {
    <span class="keyword" >if</span>(hero.isAgilityQuick(monster)){
      monster.offence(hero);
      hero.offence(monster);
    } <span class="keyword" >else</span> {
      hero.offence(monster);
      monster.offence(hero);
    }
  } <span class="keyword" >finally</span> {
    hero.next();
    monster.next();
  }

  <span class="keyword" >this</span>.logger.log(<span class="string" >'-------------------'</span>);
  <span class="keyword" >this</span>.logger.log(hero.getName() + <span class="string" >'のHP: '</span> + hero.getHp());
  <span class="keyword" >this</span>.logger.log(monster.getName() + <span class="string" >'のHP: '</span> + monster.getHp());
  <span class="keyword" >this</span>.logger.log(<span class="string" >'-------------------'</span>);
};

<span class="keyword" >var</span> AbstractQuestState = <span class="keyword" >function</span>(){};
AbstractQuestState.prototype.next = <span class="keyword" >true</span>;
AbstractQuestState.prototype.execute = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> QuestStartState = <span class="keyword" >function</span> (){};
QuestStartState.prototype = <span class="keyword" >new</span> AbstractQuestState();
QuestStartState.prototype.execute = <span class="keyword" >function</span> (quest){
  quest.logger.log(<span class="string" >'クエスト開始'</span>);
  quest.count = 0;
  quest.state = <span class="keyword" >new</span> NextQuestState();
};

<span class="keyword" >var</span> QuestEndState = <span class="keyword" >function</span> (){};
QuestEndState.prototype = <span class="keyword" >new</span> AbstractQuestState();
QuestEndState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(quest.count + <span class="string" >'回のクエストを終えた'</span>);
  quest.logger.log(<span class="string" >'クエスト終了'</span>);
  quest.state.next = <span class="keyword" >false</span>;
};

<span class="keyword" >var</span> NextQuestState = <span class="keyword" >function</span> (){};
NextQuestState.prototype = <span class="keyword" >new</span> AbstractQuestState();
NextQuestState.prototype.execute = <span class="keyword" >function</span>(quest){
  <span class="keyword" >if</span>(10 &lt; quest.count){
    quest.state = <span class="keyword" >new</span> QuestEndState();
  } <span class="keyword" >else</span> {
    <span class="keyword" >if</span>(quest.hero.hp &lt; 1){
      quest.state = <span class="keyword" >new</span> QuestEndState();
    } <span class="keyword" >else</span> {
      quest.state = <span class="keyword" >new</span> BattleState();
    }
  }
  quest.count = quest.count + 1;
};

<span class="keyword" >var</span> BattleState = <span class="keyword" >function</span> (){};
BattleState.prototype = <span class="keyword" >new</span> AbstractQuestState();
BattleState.prototype.execute = <span class="keyword" >function</span>(quest){
  <span class="keyword" >var</span> monsters = [<span class="keyword" >new</span> Slime(), <span class="keyword" >new</span> HoimiSlime(), <span class="keyword" >new</span> KingSlime()];
  <span class="keyword" >var</span> battle = <span class="keyword" >new</span> TurnBattle(quest.hero, monsters);
  battle.execute();

  <span class="keyword" >if</span>(quest.hero.hp &lt; 1){
    quest.state = <span class="keyword" >new</span> QuestEndState();
  } <span class="keyword" >else</span> {
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 3);
    <span class="keyword" >switch</span>(rand){
    <span class="keyword" >case</span> 0:
      quest.state = <span class="keyword" >new</span> NextQuestState();
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> 1:
      quest.state = <span class="keyword" >new</span> BattleState();
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> 2:
      quest.state = <span class="keyword" >new</span> RecoveryLakeState();
      <span class="keyword" >break</span>;
    }
  }
};
<span class="keyword" >var</span> RecoveryLakeState = <span class="keyword" >function</span> (){};
RecoveryLakeState.prototype = <span class="keyword" >new</span> AbstractQuestState();
RecoveryLakeState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(<span class="string" >'回復の泉を発見'</span>);

  <span class="keyword" >var</span> value = Math.floor(Math.random() * 49) + 1;
  quest.hero.hp += value;

  quest.logger.log(value + <span class="string" >'回復した'</span>);
  
  quest.state = <span class="keyword" >new</span> NextQuestState();
};

<span class="keyword" >var</span> Quest = <span class="keyword" >function</span>(hero){
  <span class="keyword" >this</span>.hero = hero;
  <span class="keyword" >this</span>.count = 0;
  <span class="keyword" >this</span>.state = <span class="keyword" >new</span> QuestStartState();
};
Quest.prototype.logger = <span class="keyword" >new</span> <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >typeof</span> console != <span class="string" >'undefined'</span>){
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      console.log(message);
    };
  } <span class="keyword" >else</span> {
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      print(message);
    };
  }
};
Quest.prototype.start = <span class="keyword" >function</span> (){
  Battle.prototype.logger = <span class="keyword" >this</span>.logger;
  TurnBattle.prototype.logger = <span class="keyword" >this</span>.logger;

  <span class="keyword" >while</span>(<span class="keyword" >this</span>.state.next){
    <span class="keyword" >this</span>.state.execute(<span class="keyword" >this</span>);
  }
};

<span class="keyword" >var</span> hero = <span class="keyword" >new</span> Hero();
<span class="keyword" >var</span> quest = <span class="keyword" >new</span> Quest(hero);
quest.start();</pre>

<p>Stateっぽく、してQuestをこなすようにしてみた</p>

<pre class="javascript">
<span class="keyword" >var</span> AbstractQuestState = <span class="keyword" >function</span>(){};
AbstractQuestState.prototype.next = <span class="keyword" >true</span>;
AbstractQuestState.prototype.execute = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> QuestStartState = <span class="keyword" >function</span> (){};
QuestStartState.prototype = <span class="keyword" >new</span> AbstractQuestState();
QuestStartState.prototype.execute = <span class="keyword" >function</span> (quest){
  quest.logger.log(<span class="string" >'クエスト開始'</span>);
  quest.count = 0;
  quest.state = <span class="keyword" >new</span> NextQuestState();
};

<span class="keyword" >var</span> QuestEndState = <span class="keyword" >function</span> (){};
QuestEndState.prototype = <span class="keyword" >new</span> AbstractQuestState();
QuestEndState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(quest.count + <span class="string" >'回のクエストを終えた'</span>);
  quest.logger.log(<span class="string" >'クエスト終了'</span>);
  quest.state.next = <span class="keyword" >false</span>;
};

<span class="keyword" >var</span> NextQuestState = <span class="keyword" >function</span> (){};
NextQuestState.prototype = <span class="keyword" >new</span> AbstractQuestState();
NextQuestState.prototype.execute = <span class="keyword" >function</span>(quest){
  <span class="keyword" >if</span>(10 &lt; quest.count){
    quest.state = <span class="keyword" >new</span> QuestEndState();
  } <span class="keyword" >else</span> {
    <span class="keyword" >if</span>(quest.hero.hp &lt; 1){
      quest.state = <span class="keyword" >new</span> QuestEndState();
    } <span class="keyword" >else</span> {
      quest.state = <span class="keyword" >new</span> BattleState();
    }
  }
  quest.count = quest.count + 1;
};

<span class="keyword" >var</span> BattleState = <span class="keyword" >function</span> (){};
BattleState.prototype = <span class="keyword" >new</span> AbstractQuestState();
BattleState.prototype.execute = <span class="keyword" >function</span>(quest){
  <span class="keyword" >var</span> monsters = [<span class="keyword" >new</span> Slime(), <span class="keyword" >new</span> HoimiSlime(), <span class="keyword" >new</span> KingSlime()];
  <span class="keyword" >var</span> battle = <span class="keyword" >new</span> TurnBattle(quest.hero, monsters);
  battle.execute();

  <span class="keyword" >if</span>(quest.hero.hp &lt; 1){
    quest.state = <span class="keyword" >new</span> QuestEndState();
  } <span class="keyword" >else</span> {
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 3);
    <span class="keyword" >switch</span>(rand){
    <span class="keyword" >case</span> 0:
      quest.state = <span class="keyword" >new</span> NextQuestState();
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> 1:
      quest.state = <span class="keyword" >new</span> BattleState();
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> 2:
      quest.state = <span class="keyword" >new</span> RecoveryLakeState();
      <span class="keyword" >break</span>;
    }
  }
};
<span class="keyword" >var</span> RecoveryLakeState = <span class="keyword" >function</span> (){};
RecoveryLakeState.prototype = <span class="keyword" >new</span> AbstractQuestState();
RecoveryLakeState.prototype.execute = <span class="keyword" >function</span>(quest){
  quest.logger.log(<span class="string" >'回復の泉を発見'</span>);

  <span class="keyword" >var</span> value = Math.floor(Math.random() * 49) + 1;
  quest.hero.hp += value;

  quest.logger.log(value + <span class="string" >'回復した'</span>);
  
  quest.state = <span class="keyword" >new</span> NextQuestState();
};

<span class="keyword" >var</span> Quest = <span class="keyword" >function</span>(hero){
  <span class="keyword" >this</span>.hero = hero;
  <span class="keyword" >this</span>.count = 0;
  <span class="keyword" >this</span>.state = <span class="keyword" >new</span> QuestStartState();
};
Quest.prototype.logger = <span class="keyword" >new</span> <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >typeof</span> console != <span class="string" >'undefined'</span>){
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      console.log(message);
    };
  } <span class="keyword" >else</span> {
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      print(message);
    };
  }
};
Quest.prototype.start = <span class="keyword" >function</span> (){
  Battle.prototype.logger = <span class="keyword" >this</span>.logger;
  TurnBattle.prototype.logger = <span class="keyword" >this</span>.logger;

  <span class="keyword" >while</span>(<span class="keyword" >this</span>.state.next){
    <span class="keyword" >this</span>.state.execute(<span class="keyword" >this</span>);
  }
};</pre>

<p>これで、動かす</p>

<pre class="javascript">クエスト開始
勇者の戦闘開始
スライムの戦闘開始
ホイミスライムの戦闘開始
キングスライムの戦闘開始
ターン： 1
キングスライムの攻撃: 勇者に0のダメージ
勇者の攻撃: キングスライムに4のダメージ
-------------------
勇者のHP: 300
キングスライムのHP: 46
-------------------
ターン： 2
勇者の攻撃: ホイミスライムに10のダメージ
ホイミスライムの攻撃: 勇者に0のダメージ
-------------------
勇者のHP: 300
ホイミスライムのHP: 15
-------------------
ターン： 3

...

キングスライムの攻撃: 勇者に0のダメージ
勇者の攻撃: キングスライムに4のダメージ
-------------------
勇者のHP: 388
キングスライムのHP: 10
-------------------
ターン： 17
キングスライムの攻撃: 勇者に0のダメージ
勇者のクリティカル！
勇者の攻撃: キングスライムに16のダメージ
キングスライムは倒れた
-------------------
勇者のHP: 388
キングスライムのHP: -6
-------------------
キングスライムは戦闘を離脱した
勇者は勝利した
勇者は戦闘を離脱した
12回のクエストを終えた
クエスト終了</pre>

<p>レベルアップとか欲しくなるね</p>]]>
</content:encoded>
</item><item>
<title>JavaScriptでStrategyパターン。RPG風</title>
<link>http://blog.xole.net/article.php?id=772</link>
<pubDate>Thu, 05 May 2011 04:42:07 +09:00</pubDate>
<description>結構前から遊んでたんだけど、ゆけ勇者が面白いなぁ。と思って、こんな感じなのを作るとするとどうなるんだろうかと思って、とりあえずStrategyパターンを使いつつ

(中略)

今回の元ネタは
ref- 指向性メモ::2005-02...</description>
<content:encoded>
<![CDATA[<p>結構前から遊んでたんだけど、<a href="http://www.xhachiapps.com/">ゆけ勇者</a>が面白いなぁ。と思って、こんな感じなのを作るとするとどうなるんだろうかと思って、とりあえずStrategyパターンを使いつつ</p>

<p>(中略)</p>

<p>今回の元ネタは<br />
ref- <a href="http://yudai.arielworks.com/memo/2005/02/24/090533">指向性メモ::2005-02-24::JavaScriptでデザインパターンその3</a>
<br />
になります。かなりパクった感じです。<br />
# もう、6年も前のネタだったのか。ずっとブックマークにあった。重宝してます。</p>

<p>さっそく、実装コードはこんな感じ</p>

<pre class="javascript">Function.prototype.bind = <span class="keyword" >function</span>(obj){
  <span class="keyword" >var</span> __method__ = <span class="keyword" >this</span>;
  <span class="keyword" >var</span> __args__ = Array.prototype.slice.call(arguments);
  __args__.shift(); <span class="comment" >// obj</span>
  <span class="keyword" >return</span> <span class="keyword" >function</span> (){
    <span class="comment" >// concat args</span>
    <span class="keyword" >var</span> args = Array.prototype.slice.call(arguments);
    <span class="keyword" >return</span> __method__.apply(obj, __args__.concat(args));
  };
};

<span class="keyword" >var</span> AbstractStrategy = <span class="keyword" >function</span> (){};
AbstractStrategy.prototype.execute = <span class="keyword" >function</span>(character){};

<span class="keyword" >var</span> StrategyEveryAttack = <span class="keyword" >function</span> (){};
StrategyEveryAttack.prototype.execute = <span class="keyword" >function</span>(character){
  <span class="keyword" >if</span>(0 &lt; character.lucky){
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 100);
    <span class="keyword" >if</span>(character.lucky == rand){
      <span class="keyword" >return</span> character.offence * 2;
    }
  }
  <span class="keyword" >return</span> character.offence;
};

<span class="keyword" >var</span> StrategyWoundedRecoverHp = <span class="keyword" >function</span> (){};
StrategyWoundedRecoverHp.prototype.execute = <span class="keyword" >function</span>(character){
  <span class="keyword" >var</span> rand = Math.floor(Math.random() * 3);
  <span class="keyword" >if</span>(character.hp &lt; 10 &amp;&amp; rand == 1){
    character.hp += 30;
    <span class="keyword" >return</span> 0;
  }
  <span class="keyword" >return</span> character.offence;
};

<span class="keyword" >var</span> AbstractCharacter = <span class="keyword" >function</span> (){};
AbstractCharacter.prototype.strength = 0;
AbstractCharacter.prototype.agility = 0;
AbstractCharacter.prototype.vitality = 0;
AbstractCharacter.prototype.intelligence = 0;
AbstractCharacter.prototype.dexterity = 0;
AbstractCharacter.prototype.lucky = 0;
AbstractCharacter.prototype.name = <span class="string" >''</span>;
AbstractCharacter.prototype.hp = 0;
AbstractCharacter.prototype.offence = 0;
AbstractCharacter.prototype.defence = 0;
AbstractCharacter.prototype.action = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> AbstractMonster = <span class="keyword" >function</span>() {};
AbstractMonster.prototype = <span class="keyword" >new</span> AbstractCharacter();
AbstractMonster.prototype.action = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.attack();
};
AbstractMonster.prototype.attack = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.strategy.execute(<span class="keyword" >this</span>);
};

<span class="keyword" >var</span> Slime = <span class="keyword" >function</span>() {};
Slime.prototype = <span class="keyword" >new</span> AbstractMonster();
Slime.prototype.name = <span class="string" >'スライム'</span>;
Slime.prototype.hp = 20;
Slime.prototype.lucky = 0;
Slime.prototype.offence = 3;
Slime.prototype.defence = 3;
Slime.prototype.strategy = <span class="keyword" >new</span> StrategyEveryAttack();

<span class="keyword" >var</span> HoimiSlime = <span class="keyword" >function</span>() {};
HoimiSlime.prototype = <span class="keyword" >new</span> AbstractMonster();
HoimiSlime.prototype.name = <span class="string" >'ホイミスライム'</span>;
HoimiSlime.prototype.hp = 25;
HoimiSlime.prototype.offence = 2;
HoimiSlime.prototype.defence = 2;
HoimiSlime.prototype.strategy = <span class="keyword" >new</span> StrategyWoundedRecoverHp();

<span class="keyword" >var</span> KingSlime = <span class="keyword" >function</span>() {};
KingSlime.prototype = <span class="keyword" >new</span> AbstractMonster();
KingSlime.prototype.name = <span class="string" >'キングスライム'</span>;
KingSlime.prototype.hp = 50;
KingSlime.prototype.agility = 12;
KingSlime.prototype.lucky = 1;
KingSlime.prototype.offence = 4;
KingSlime.prototype.defence = 5;
KingSlime.prototype.strategy = <span class="keyword" >new</span> StrategyEveryAttack();

<span class="keyword" >var</span> Hero = <span class="keyword" >function</span> (){};
Hero.prototype = <span class="keyword" >new</span> AbstractCharacter();
Hero.prototype.name = <span class="string" >'勇者'</span>;
Hero.prototype.hp = 500;
Hero.prototype.agility = 10;
Hero.prototype.lucky = 10;
Hero.prototype.offence = 12;
Hero.prototype.defence = 10;
Hero.prototype.action = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.offence;
};

<span class="keyword" >var</span> Battle = <span class="keyword" >function</span>(hero, monsterList){
  <span class="keyword" >this</span>.turn = 1;
  <span class="keyword" >this</span>.hero = hero;
  <span class="keyword" >this</span>.monsterList = monsterList;
};
Battle.prototype.logger = <span class="keyword" >new</span> <span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >typeof</span> console != <span class="string" >'undefined'</span>){
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      console.log(message);
    };
  } <span class="keyword" >else</span> {
    <span class="keyword" >this</span>.log = <span class="keyword" >function</span>(message){
      print(message);
    };
  }
};
Battle.prototype.start = <span class="keyword" >function</span> (){
  <span class="keyword" >while</span>(<span class="keyword" >true</span>){
    <span class="keyword" >if</span>(<span class="keyword" >this</span>.monsterList.length &lt; 1){
      <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.hero.name + <span class="string" >' は勝利した'</span>);
    }
    <span class="keyword" >if</span>(<span class="keyword" >this</span>.hero.hp &lt; 1){
      <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(<span class="keyword" >this</span>.hero.name + <span class="string" >' は死んでしまった'</span>);
    }
    <span class="keyword" >this</span>.next();
  }
};
Battle.prototype.next = <span class="keyword" >function</span> (){
  <span class="keyword" >try</span> {
    <span class="keyword" >this</span>.logger.log(<span class="string" >'ターン: '</span> + <span class="keyword" >this</span>.turn);

    <span class="comment" >// 攻撃対象をランダムで設定</span>
    <span class="keyword" >var</span> targetIndex = Math.floor(Math.random() * <span class="keyword" >this</span>.monsterList.length);
    <span class="keyword" >var</span> monster = <span class="keyword" >this</span>.monsterList[targetIndex];

    <span class="comment" >// 素早さの早い方からattack</span>
    <span class="keyword" >if</span>(<span class="keyword" >this</span>.hero.agility &lt; monster.agility){
      <span class="keyword" >this</span>.attack(monster, <span class="keyword" >this</span>.hero);
    } <span class="keyword" >else</span> {
      <span class="keyword" >this</span>.attack(<span class="keyword" >this</span>.hero, monster);
    }

    <span class="comment" >// モンスターリストの再構築: hp が 0 以下のモノは取り除く</span>
    <span class="keyword" >this</span>.monsterList = <span class="keyword" >this</span>.monsterList.filter((<span class="keyword" >function</span>(monster){
      <span class="keyword" >if</span>(monster.hp &lt;= 0){
        <span class="keyword" >this</span>.logger.log(monster.name + <span class="string" >'は死んだ'</span>);
        <span class="keyword" >return</span> <span class="keyword" >false</span>;
      }
      <span class="keyword" >return</span> <span class="keyword" >true</span>;
    }).bind(<span class="keyword" >this</span>));
  } <span class="keyword" >finally</span> {
    <span class="keyword" >this</span>.turn = <span class="keyword" >this</span>.turn + 1;
  }
};
Battle.prototype.attack = <span class="keyword" >function</span> (attacker, defencer, once){
  <span class="keyword" >var</span> attackValue = attacker.action();
  <span class="keyword" >var</span> damage = defencer.defence - attackValue;

  <span class="keyword" >var</span> diff = defencer.defence - attackValue;
  <span class="keyword" >var</span> damage = Math.abs(diff);
  defencer.hp -= damage;

  <span class="keyword" >this</span>.logger.log(attacker.name + <span class="string" >'の攻撃: '</span> + defencer.name + <span class="string" >'に'</span> + damage + <span class="string" >'のダメージ'</span>);

  <span class="keyword" >this</span>.logger.log(<span class="string" >'-------------------'</span>);
  <span class="keyword" >this</span>.logger.log(attacker.name + <span class="string" >'のHP: '</span> + attacker.hp);
  <span class="keyword" >this</span>.logger.log(defencer.name + <span class="string" >'のHP: '</span> + defencer.hp);
  <span class="keyword" >this</span>.logger.log(<span class="string" >'-------------------'</span>);

  <span class="keyword" >if</span>(defencer.hp &lt;= 0){
    <span class="keyword" >return</span> <span class="keyword" >this</span>.logger.log(defencer.name + <span class="string" >'は倒れた'</span>);
  }

  <span class="comment" >// 再起しない場合は、抜ける</span>
  <span class="keyword" >if</span>(once){
    <span class="keyword" >return</span>;
  }

  <span class="comment" >// attackerとdefencerを入れ替えて attack 再開</span>
  <span class="keyword" >return</span> <span class="keyword" >this</span>.attack(defencer, attacker, <span class="keyword" >true</span>);
};

<span class="comment" >// デモコード</span>
<span class="comment" >// スライム * 2、ホイミスライム、キングスライムの組み合わせ</span>
<span class="keyword" >var</span> monsters = [<span class="keyword" >new</span> Slime(), <span class="keyword" >new</span> Slime(), <span class="keyword" >new</span> HoimiSlime(), <span class="keyword" >new</span> KingSlime()];
<span class="keyword" >var</span> hero = <span class="keyword" >new</span> Hero();
<span class="keyword" >var</span> btl = <span class="keyword" >new</span> Battle(hero, monsters);
btl.start();</pre>

<p>もう、元ネタからは、モンスター名とロジックの一部くらいしか残ってないですが、ほぼ同じようなコードになってます。<br />
strategy pattern っぽく、各モンスターたちは何らかの戦略を持つようにしてます。</p>

<pre class="javascript">
<span class="keyword" >var</span> AbstractStrategy = <span class="keyword" >function</span> (){};
AbstractStrategy.prototype.execute = <span class="keyword" >function</span>(character){};

<span class="keyword" >var</span> StrategyEveryAttack = <span class="keyword" >function</span> (){};
StrategyEveryAttack.prototype.execute = <span class="keyword" >function</span>(character){
  <span class="keyword" >if</span>(0 &lt; character.lucky){
    <span class="keyword" >var</span> rand = Math.floor(Math.random() * 100);
    <span class="keyword" >if</span>(character.lucky == rand){
      <span class="keyword" >return</span> character.offence * 2;
    }
  }
  <span class="keyword" >return</span> character.offence;
};

<span class="keyword" >var</span> StrategyWoundedRecoverHp = <span class="keyword" >function</span> (){};
StrategyWoundedRecoverHp.prototype.execute = <span class="keyword" >function</span>(character){
  <span class="keyword" >var</span> rand = Math.floor(Math.random() * 3);
  <span class="keyword" >if</span>(character.hp &lt; 10 &amp;&amp; rand == 1){
    character.hp += 30;
    <span class="keyword" >return</span> 0;
  }
  <span class="keyword" >return</span> character.offence;
};

<span class="keyword" >var</span> AbstractCharacter = <span class="keyword" >function</span> (){};
AbstractCharacter.prototype.agility = 0;
AbstractCharacter.prototype.lucky = 0;
AbstractCharacter.prototype.name = <span class="string" >''</span>;
AbstractCharacter.prototype.hp = 0;
AbstractCharacter.prototype.offence = 0;
AbstractCharacter.prototype.defence = 0;
AbstractCharacter.prototype.action = <span class="keyword" >function</span> (){};

<span class="keyword" >var</span> AbstractMonster = <span class="keyword" >function</span>() {};
AbstractMonster.prototype = <span class="keyword" >new</span> AbstractCharacter();
AbstractMonster.prototype.action = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.attack();
};
AbstractMonster.prototype.attack = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.strategy.execute(<span class="keyword" >this</span>);
};

<span class="keyword" >var</span> Slime = <span class="keyword" >function</span>() {};
Slime.prototype = <span class="keyword" >new</span> AbstractMonster();
Slime.prototype.name = <span class="string" >'スライム'</span>;
Slime.prototype.hp = 20;
Slime.prototype.lucky = 0;
Slime.prototype.offence = 3;
Slime.prototype.defence = 3;
Slime.prototype.strategy = <span class="keyword" >new</span> StrategyEveryAttack();

<span class="keyword" >var</span> HoimiSlime = <span class="keyword" >function</span>() {};
HoimiSlime.prototype = <span class="keyword" >new</span> AbstractMonster();
HoimiSlime.prototype.name = <span class="string" >'ホイミスライム'</span>;
HoimiSlime.prototype.hp = 25;
HoimiSlime.prototype.offence = 2;
HoimiSlime.prototype.defence = 2;
HoimiSlime.prototype.strategy = <span class="keyword" >new</span> StrategyWoundedRecoverHp();

<span class="keyword" >var</span> KingSlime = <span class="keyword" >function</span>() {};
KingSlime.prototype = <span class="keyword" >new</span> AbstractMonster();
KingSlime.prototype.name = <span class="string" >'キングスライム'</span>;
KingSlime.prototype.hp = 50;
KingSlime.prototype.agility = 12;
KingSlime.prototype.lucky = 1;
KingSlime.prototype.offence = 4;
KingSlime.prototype.defence = 5;
KingSlime.prototype.strategy = <span class="keyword" >new</span> StrategyEveryAttack();</pre>

<p>といっても、RPGの基本となる戦略はほとんどがランダムなので、あまり使い物になってないという。。</p>

<p>実行するとこんな感じで動きます(rhinoくらいでしか動かしてない)</p>
<pre class="javascript">ターン: 1
勇者の攻撃: スライムに9のダメージ
-------------------
勇者のHP: 500
スライムのHP: 11
-------------------
スライムの攻撃: 勇者に7のダメージ
-------------------
スライムのHP: 11
勇者のHP: 493
-------------------
ターン: 2
キングスライムの攻撃: 勇者に6のダメージ
-------------------
キングスライムのHP: 50
勇者のHP: 487
-------------------
勇者の攻撃: キングスライムに7のダメージ
-------------------
勇者のHP: 487
キングスライムのHP: 43
-------------------
ターン: 3
勇者の攻撃: スライムに9のダメージ
-------------------
勇者のHP: 487
スライムのHP: 2
-------------------
スライムの攻撃: 勇者に7のダメージ
-------------------
スライムのHP: 2
勇者のHP: 480
-------------------
ターン: 4
勇者の攻撃: スライムに9のダメージ
-------------------
勇者のHP: 480
スライムのHP: -7
-------------------
スライムは倒れた
スライムは死んだ
(snip)
ターン: 18
キングスライムの攻撃: 勇者に6のダメージ
-------------------
キングスライムのHP: 1
勇者のHP: 394
-------------------
勇者の攻撃: キングスライムに7のダメージ
-------------------
勇者のHP: 394
キングスライムのHP: -6
-------------------
キングスライムは倒れた
キングスライムは死んだ
ターン: 19
勇者の攻撃: ホイミスライムに10のダメージ
-------------------
勇者のHP: 394
ホイミスライムのHP: 5
-------------------
ホイミスライムの攻撃: 勇者に8のダメージ
-------------------
ホイミスライムのHP: 5
勇者のHP: 386
-------------------
ターン: 20
勇者の攻撃: ホイミスライムに10のダメージ
-------------------
勇者のHP: 386
ホイミスライムのHP: -5
----------------
ホイミスライムは倒れた
ホイミスライムは死んだ
勇者は勝利した
</pre>

<p>色々なstateとかキャラクターのstatusとか永続化できれば、ネットワーク越しでも遊べるようになりそう。<br />
あと、ステータス(agilityとかdexterityとか)の値をレベルアップできるようになったり、装備品とかでoffence/diffence補正ができれば、RPGっぽくなる。かも<br />
うむむ。もう少し</p>]]>
</content:encoded>
</item><item>
<title>やったーjavascriptでScala風のActorできたよー＼(^o^)／</title>
<link>http://blog.xole.net/article.php?id=771</link>
<pubDate>Mon, 04 Apr 2011 00:28:36 +09:00</pubDate>
<description>まだ、loopとかreactとか、その辺りが動くかなーって感じで中途ハンパだけど。

元scalaのコード(http://www.scala-lang.org/node/54から転載)

abstract class PingMes...</description>
<content:encoded>
<![CDATA[<p>まだ、loopとかreactとか、その辺りが動くかなーって感じで中途ハンパだけど。</p>

<p>元scalaのコード(<a href="http://www.scala-lang.org/node/54">http://www.scala-lang.org/node/54</a>から転載)</p>

<pre class="python">abstract <span class="keyword" >class</span> PingMessage
case <span class="keyword" >object</span> Start extends PingMessage
case <span class="keyword" >object</span> SendPing extends PingMessage
case <span class="keyword" >object</span> Pong extends PingMessage

abstract <span class="keyword" >class</span> PongMessage
case <span class="keyword" >object</span> Ping extends PongMessage
case <span class="keyword" >object</span> Stop extends PongMessage

<span class="keyword" >object</span> pingpong extends Application {
  val pong = <span class="commonlibs" >new</span> Pong
  val ping = <span class="commonlibs" >new</span> Ping(100000, pong)
  ping.start
  pong.start
  ping ! Start
}

<span class="keyword" >class</span> Ping(<span class="builtins" >count</span>: Int, pong: Actor) extends Actor {
  <span class="keyword" >def</span> act() {
    println("Ping: Initializing with <span class="builtins" >count</span> "+<span class="builtins" >count</span>+": "+pong)
    var pingsLeft = <span class="builtins" >count</span>
    loop {
      react {
        case Start =&gt;
          println("Ping: starting.")
          pong ! Ping
          pingsLeft = pingsLeft - 1
        case SendPing =&gt;
          pong ! Ping
          pingsLeft = pingsLeft - 1
        case Pong =&gt;
          <span class="keyword" >if</span> (pingsLeft % 1000 == 0)
            println("Ping: pong <span class="keyword" >from</span>: "+sender)
          <span class="keyword" >if</span> (pingsLeft &gt; 0)
            <span class="builtins" >self</span> ! SendPing
          <span class="keyword" >else</span> {
            println("Ping: Stop.")
            pong ! Stop
            exit('stop)
          }
      }
    }
  }
}

<span class="keyword" >class</span> Pong extends Actor {
  <span class="keyword" >def</span> act() {
    var pongCount = 0
    loop {
      react {
        case Ping =&gt;
          <span class="keyword" >if</span> (pongCount % 1000 == 0)
            println("Pong: ping "+pongCount+" <span class="keyword" >from</span> "+sender)
          sender ! Pong
          pongCount = pongCount + 1
        case Stop =&gt;
          println("Pong: Stop.")
          exit('stop)
      }
    }
  }
}</pre>

<p>これが、こんな感じになる</p>

<pre class="javascript">
<span class="comment" >//</span>
<span class="comment" >// PingPong:</span>
<span class="comment" >// http://www.scala-lang.org/node/54</span>
<span class="comment" >//</span>

<span class="keyword" >var</span> MSG_Start = 1;
<span class="keyword" >var</span> MSG_SendPing = 2;
<span class="keyword" >var</span> MSG_Pong = 3;
<span class="keyword" >var</span> MSG_Ping = 4;
<span class="keyword" >var</span> MSG_Stop = 5;

<span class="keyword" >var</span> Ping = <span class="keyword" >function</span>(count, pong) {
  <span class="keyword" >this</span>.count = count;
  <span class="keyword" >this</span>.pong = pong;
};
Ping.prototype = <span class="keyword" >new</span> Actor();
Ping.prototype.toString = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="string" >'Ping[count: '</span> + <span class="keyword" >this</span>.count + <span class="string" >', pong: '</span> + <span class="keyword" >this</span>.pong + <span class="string" >']'</span>;
};
Ping.prototype.act = <span class="keyword" >function</span>(actor){
  console.log(<span class="string" >'Ping initializing with count:'</span> + <span class="keyword" >this</span>.count + <span class="string" >': '</span> + <span class="keyword" >this</span>.pong);
  <span class="keyword" >var</span> pingsLeft = <span class="keyword" >this</span>.count;
  actor.loop(actor.react(<span class="keyword" >function</span>(message, sender){
    <span class="keyword" >switch</span>(message){
    <span class="keyword" >case</span> MSG_Start:
      console.log(<span class="string" >'Ping:start'</span>);
      <span class="keyword" >this</span>.pong[<span class="string" >'!'</span>](MSG_Ping, <span class="keyword" >this</span>);
      pingsLeft = pingsLeft - 1;
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> MSG_SendPing:
      <span class="keyword" >this</span>.pong[<span class="string" >'!'</span>](MSG_Ping, <span class="keyword" >this</span>);
      pingsLeft = pingsLeft - 1;
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> MSG_Pong:
      <span class="keyword" >if</span>(0 === (pingsLeft % 1000)){
        console.log(<span class="string" >'Ping pong from: '</span> + sender);
      }
      <span class="keyword" >if</span>(0 &lt; pingsLeft){
        <span class="keyword" >this</span>[<span class="string" >'!'</span>](MSG_SendPing, <span class="keyword" >this</span>);
      } <span class="keyword" >else</span> {
        console.log(<span class="string" >'Ping Stop'</span>);
        <span class="keyword" >this</span>.pong[<span class="string" >'!'</span>](MSG_Stop, <span class="keyword" >this</span>);
        <span class="keyword" >this</span>.stop();
      }
      <span class="keyword" >break</span>;
    }
  }));
};
<span class="keyword" >var</span> Pong = <span class="keyword" >function</span> (){};
Pong.prototype = <span class="keyword" >new</span> Actor();
Pong.prototype.toString = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="string" >'[Pong]'</span>;
};
Pong.prototype.act = <span class="keyword" >function</span>(actor){
  <span class="keyword" >var</span> pongCount = 0;
  actor.loop(actor.react(<span class="keyword" >function</span>(message, sender){
    <span class="keyword" >switch</span>(message){
    <span class="keyword" >case</span> MSG_Ping:
      <span class="keyword" >if</span>(0 === (pongCount % 1000)){
        console.log(<span class="string" >'Pong ping:'</span> + pongCount + <span class="string" >' from '</span> + sender);
      }
      sender[<span class="string" >'!'</span>](MSG_Pong, <span class="keyword" >this</span>);
      pongCount = pongCount + 1;
      <span class="keyword" >break</span>;
    <span class="keyword" >case</span> MSG_Stop:
      console.log(<span class="string" >'Pong Stop'</span>);
      <span class="keyword" >this</span>.stop();
      <span class="keyword" >break</span>;
    }
  }));
};

<span class="keyword" >var</span> pong = <span class="keyword" >new</span> Pong();
<span class="keyword" >var</span> ping = <span class="keyword" >new</span> Ping(100000, pong);

ping.start();
pong.start();

ping[<span class="string" >'!'</span>](MSG_Start);</pre>

<p>actor['!']とかsenderが引数で与えられているトコ以外は似てきたかな？</p>

<p>んで、Actorの実装は</p>

<pre class="javascript">Function.prototype.bind = <span class="keyword" >function</span>(obj){
  <span class="keyword" >var</span> __method__ = <span class="keyword" >this</span>;
  <span class="keyword" >var</span> __args__ = Array.prototype.slice.call(arguments);
  __args__.shift(); <span class="comment" >// obj</span>
  <span class="keyword" >return</span> <span class="keyword" >function</span> (){
    <span class="keyword" >var</span> args = Array.prototype.slice.call(arguments);
    <span class="keyword" >return</span> __method__.apply(obj, __args__.concat(args));
  };
};

Function.prototype.curry = <span class="keyword" >function</span> (){
  <span class="keyword" >var</span> __method__ = <span class="keyword" >this</span>;
  <span class="keyword" >var</span> __args__ = Array.prototype.slice.call(arguments);
  <span class="keyword" >return</span> <span class="keyword" >function</span> (){
    <span class="keyword" >var</span> args = Array.prototype.slice.call(arguments);
    <span class="keyword" >return</span> __method__.apply(<span class="keyword" >this</span>, __args__.concat(args));
  };
};

<span class="keyword" >var</span> GlobalActor = <span class="keyword" >new</span> <span class="keyword" >function</span> (){
  <span class="keyword" >var</span> call = <span class="keyword" >function</span>(callback){
    setTimeout(callback, 0);
  };

  <span class="keyword" >var</span> Queue = <span class="keyword" >function</span> (){};
  Queue.prototype.enqueue = <span class="keyword" >function</span> (callback){
    call(callback);
  };

  <span class="keyword" >var</span> AbstractActor = <span class="keyword" >function</span>(){
    <span class="keyword" >this</span>.queue = <span class="keyword" >new</span> Queue();
  };
  AbstractActor.prototype.act = <span class="keyword" >function</span> (){
    <span class="keyword" >throw</span> <span class="keyword" >new</span> Error(<span class="string" >'override actor'</span>);
  };
  AbstractActor.prototype.start = <span class="keyword" >function</span> (){
    <span class="keyword" >this</span>.queue.enqueue((<span class="keyword" >function</span> (){
      <span class="keyword" >this</span>.act.call(<span class="keyword" >this</span>, <span class="keyword" >this</span>);
    }).bind(<span class="keyword" >this</span>));
  };
  AbstractActor.prototype.stop = <span class="keyword" >function</span> (){
    <span class="keyword" >this</span>.queue.enqueue = <span class="keyword" >function</span> (){
      <span class="comment" >// nop</span>
    };
  };
  <span class="keyword" >this</span>.AbstractActor = AbstractActor;
};

<span class="keyword" >var</span> Actor = <span class="keyword" >function</span> (){
  <span class="keyword" >this</span>.messages = [];
};
Actor.MESSAGE_BANG = 1;
Actor.MESSAGE_BANGBANG = 2;
Actor.MESSAGE_BANGQMARK = 3;
Actor.prototype = <span class="keyword" >new</span> GlobalActor.AbstractActor();
Actor.prototype.loop = <span class="keyword" >function</span>(callback){
  <span class="keyword" >var</span> self = <span class="keyword" >this</span>;
  <span class="keyword" >var</span> loop = <span class="keyword" >function</span> (){
    <span class="keyword" >try</span> {
      callback.apply(self);
      self.queue.enqueue(loop);
    } <span class="keyword" >catch</span>(e) {
      console.error(e);
    }
  };
  self.queue.enqueue(loop);
};
Actor.prototype.performMessage = <span class="keyword" >function</span>(callback){
  <span class="comment" >// clear reply</span>
  <span class="keyword" >this</span>.reply = <span class="keyword" >function</span> (){};
  <span class="comment" >// dequeue message</span>
  <span class="keyword" >var</span> message = <span class="keyword" >this</span>.messages.shift();

  <span class="keyword" >switch</span>(message.type){
  <span class="keyword" >case</span> Actor.MESSAGE_BANG:
    <span class="comment" >// no reply</span>
    <span class="keyword" >return</span> callback.call(<span class="keyword" >this</span>, message.parameter, message.sender);
  <span class="keyword" >case</span> Actor.MESSAGE_BANGBANG:
    <span class="comment" >// reply async</span>
    <span class="keyword" >this</span>.reply = <span class="keyword" >function</span>(value){
      <span class="keyword" >var</span> partialResult = message.partialFunction(value);
      <span class="keyword" >var</span> future = message.future;
      <span class="keyword" >this</span>.queue.enqueue(future.callback.curry(partialResult));
    };
    <span class="keyword" >return</span> callback.call(<span class="keyword" >this</span>, message.parameter, message.sender);
  <span class="keyword" >case</span> Actor.MESSAGE_BANGQMARK:
    <span class="keyword" >throw</span> <span class="keyword" >new</span> Error(<span class="string" >'not yet supported'</span>);
  }
};
Actor.prototype.react = <span class="keyword" >function</span>(callback){
  <span class="keyword" >var</span> self = <span class="keyword" >this</span>;
  <span class="keyword" >return</span> <span class="keyword" >function</span> (){
    <span class="keyword" >if</span>(0 &lt; self.messages.length){
      self.performMessage.apply(self, [callback]);
    }
  };
};
Actor.prototype.receive = <span class="keyword" >function</span>(callback){
  <span class="keyword" >throw</span> <span class="keyword" >new</span> Error(<span class="string" >'not yet supported'</span>);
};
Actor.prototype[<span class="string" >'!'</span>] = <span class="keyword" >function</span> (_parameter, _sender){
  <span class="keyword" >this</span>.messages.push({
    type: Actor.MESSAGE_BANG,
    parameter: _parameter,
    sender: _sender,
    partialFunction: <span class="keyword" >null</span>,
    future: <span class="keyword" >null</span>
  });
};
Actor.prototype[<span class="string" >'!!'</span>] = <span class="keyword" >function</span>(_parameter, _partialFunction, _sender){
  <span class="keyword" >var</span> __future__ = <span class="keyword" >function</span>(callback){
    arguments.callee.callback = callback;
  };
  __future__.callback = <span class="keyword" >null</span>;

  <span class="keyword" >this</span>.messages.push({
    type: Actor.MESSAGE_BANGBANG,
    parameter: _parameter,
    sender: _sender,
    partialFunction: _partialFunction,
    future: __future__
  });
  <span class="keyword" >return</span> __future__;
};
Actor.prototype[<span class="string" >'!?'</span>] = <span class="keyword" >function</span>(parameter){
  <span class="keyword" >throw</span> <span class="keyword" >new</span> Error(<span class="string" >'not yet supported'</span>);
};</pre>

<p>といった感じ。<br />
本来のScalaのActorならThreadを使ってたりするけど、ここでは setTimeout(callback, 0)になってるんで、少し挙動が違う(どちらかというと、io-languageのActorに似てるかも)<br />
actor.loopからactor.reactもなんか違うかも。。</p>

<p>まだまだ修正中<br />
<a href="https://gist.github.com/889276">https://gist.github.com/889276</a>
</p>]]>
</content:encoded>
</item><item>
<title>Re: Titanium Mobileの暗黒面</title>
<link>http://blog.xole.net/article.php?id=770</link>
<pubDate>Mon, 07 Mar 2011 02:06:51 +09:00</pubDate>
<description>面白いものをみてしまったので、僕も少しだけ
ref - Titanium Mobileの暗黒ノウハウを公開します。 - このブログは証明できない。


Object の wrap って出来ないね。ってやつ


Titanium.D...</description>
<content:encoded>
<![CDATA[<p>面白いものをみてしまったので、僕も少しだけ<br />
ref - <a href="http://d.hatena.ne.jp/shunsuk/20110304/1299229674">Titanium Mobileの暗黒ノウハウを公開します。 - このブログは証明できない。</a>
</p>

<h2>Object の wrap って出来ないね。ってやつ</h2>

<p>
<a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.Database-module">Titanium.Database</a> まわりを実装していたときのことなんですが</p>
<p>DB の処理って大半は CRUD しかないから、それらを楽に扱えるように、wrapper を書こうと思って下記のようなコードを用意してみました。</p>

<pre class="javascript">Titanium.UI.setBackgroundColor(<span class="string" >'#000'</span>);

<span class="keyword" >var</span> copyArray = <span class="keyword" >function</span>(obj){
  <span class="keyword" >var</span> result = [];
  <span class="keyword" >for</span>(<span class="keyword" >var</span> i = 0; i &lt; obj.length; ++i){
    result.push(obj[i]);
  }
  <span class="keyword" >return</span> result;
};

<span class="keyword" >var</span> DatabaseWrapper = <span class="keyword" >function</span>(name){
  <span class="keyword" >this</span>.name = name;
  <span class="keyword" >this</span>.db = Titanium.Database.open(name);
};
DatabaseWrapper.prototype.select = <span class="keyword" >function</span> (){
  <span class="keyword" >var</span> args = copyArray(arguments);
  <span class="keyword" >var</span> sql = args.shift();
  <span class="keyword" >var</span> parameters = args;
  <span class="keyword" >var</span> rs = <span class="keyword" >this</span>.db.execute.apply(<span class="keyword" >this</span>.db, args);
  <span class="keyword" >try</span> {
    <span class="keyword" >var</span> returnValues = [];
    <span class="keyword" >while</span>(rs.isValidRow()){
      returnValues.push(rs.fieldByName(<span class="string" >'hoge'</span>));
    }
    <span class="keyword" >return</span> returnValues;
  } <span class="keyword" >finally</span> {
    rs.close();
  }
};
DatabaseWrapper.prototype.insert = <span class="keyword" >function</span> (){
  <span class="comment" >//</span>
  <span class="comment" >// doSomething</span>
  <span class="comment" >//</span>
};
</pre>

<p>実装はとても簡単で、 <a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.Database.DB-object.html">Titanium.Database.execute</a> を直接触るのではなく DatabaseWrapper.select を触るようにしたもの。</p>

<p>実行はこんな感じで行ないました。</p>

<pre class="javascript">
<span class="comment" >//</span>
<span class="comment" >// hoge.sqlite</span>
<span class="comment" >// sqlite&gt; .dump</span>
<span class="comment" >// BEGIN TRANSACTION;</span>
<span class="comment" >// CREATE TABLE tbl (id int not null, primary key(id));</span>
<span class="comment" >// INSERT INTO "tbl" VALUES(100);</span>
<span class="comment" >// INSERT INTO "tbl" VALUES(200);</span>
<span class="comment" >// INSERT INTO "tbl" VALUES(300);</span>
<span class="comment" >// COMMIT;</span>
<span class="comment" >//</span>
Titanium.Database.install('hoge.sqlite', 'hoge');

var db = new DatabaseWrapper('hoge');
Titanium.API.debug(db.select('SELECT id FROM tbl WHERE id &gt; ?', 10)); <span class="comment" >// ERROR</span>

/*
[ERROR] Script Error = Result of expression 'this.db.execute.apply' [undefined] is not a <span class="keyword" >function</span>. at app.js (line 21).
*/</pre>

<p>エラーと言われて動きません。。下記のようなコードに変えることで、動作するので、どうも [object TiDabase] や Kroll Proxy あたりの実装に問題がありそうです。</p>

<pre class="javascript">
<span class="comment" >//var db = new DatabaseWrapper('hoge');</span>
<span class="comment" >//Titanium.API.debug(db.select('SELECT id FROM tbl WHERE id &gt; ?', 10));</span>
<span class="keyword" >var</span> hoge = <span class="keyword" >new</span> DatabaseWrapper(<span class="string" >'hoge'</span>);
Titanium.API.debug(hoge.db.execute(<span class="string" >'SELECT id FROM tbl WHERE id &gt; ?'</span>, 10)); <span class="comment" >// [object TiDatabaseResultSet]</span>
</pre>

<h2>Objectのwrapどころか、上書きしても動いてくれないコードになるよね。ってやつ</h2>

<p>すでに javascripter としては、上記の問題で愕然としているわけですが、懲りずに（というか上記みたいにエラーがでないので）やっちまいがちなのが、 View まわりです</p>

<p>まず、下記のコードは、真っ黒な画面に 四角い(200x200)の view を 1000ms 後に表示するってコード</p>

<pre class="javascript">Titanium.UI.setBackgroundColor(<span class="string" >'#000'</span>);

<span class="keyword" >var</span> view = Titanium.UI.createView({width: 200, height: 200, backgroundColor: <span class="string" >'#fff'</span>});
view.hide();

<span class="keyword" >var</span> win = Titanium.UI.createWindow();
win.add(view);

setTimeout(<span class="keyword" >function</span> (){
  view.show();
}, 1000);

win.open({fullscreen: <span class="keyword" >true</span>});</pre>

<p>この 1000ms 後によばれる show に何かしらの処理を埋め込みたくて、下記のように alert を埋め込んでみた</p>

<pre class="javascript">
<span class="keyword" >var</span> view = Titanium.UI.createView({width: 200, height: 200, backgroundColor: <span class="string" >'#fff'</span>});
view.hide();

<span class="keyword" >var</span> win = Titanium.UI.createWindow();
win.add(view);

setTimeout(<span class="keyword" >function</span> (){
  view.show();
}, 1000);

<span class="keyword" >var</span> originalShowFunc = view.show;
view.show = <span class="keyword" >function</span> (){
  alert(<span class="string" >'hello world!'</span>); <span class="comment" >// =&gt; 呼ばれない</span>
  <span class="keyword" >return</span> originalShowFunc.apply(view, []);
};

win.open({fullscreen: <span class="keyword" >true</span>});</pre>

<p>同じインスタンスのメソッド(func)を上書きしてんだから、ふつーに考えればフツーにうごきそうですが、動きません。</p>
ちなみに、これを上手く使うことで、 <a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.WebView-object.html">Titanium.UI.WebView</a> の repaint メソッドが android で呼びだすとエラーになってしまう(undefinedだったかな)の問題は対処できます。</p>

<pre class="javascript">
<span class="keyword" >var</span> utils = {
  createWebView: <span class="keyword" >function</span>(options){
    <span class="keyword" >var</span> webView = Titanium.UI.createWebView(options);
    <span class="comment" >// android only</span>
    <span class="comment" >// webView.repaint was undefined: then android webView</span>
    webView.repaint = <span class="keyword" >function</span> (){};
    <span class="keyword" >return</span> webView;
  }
};</pre>

<p>ちなみに、ちなみに、object.methodName.apply な呼出は委譲を使うときに使ったりするときに使ったり使わなかったり</p>

<pre class="javascript">
<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (name){
  <span class="keyword" >this</span>.name = name;
};
Foo.prototype.getName = <span class="keyword" >function</span> (){
  <span class="keyword" >return</span> <span class="keyword" >this</span>.name;
};

<span class="keyword" >var</span> foo1 = <span class="keyword" >new</span> Foo(<span class="string" >'name_1'</span>);
<span class="keyword" >var</span> foo2 = <span class="keyword" >new</span> Foo(<span class="string" >'name_2'</span>);

foo1.getName.apply(foo2, []); <span class="comment" >// =&gt; name_2</span>
</pre>

<h2>NavigationGroup がないと、Titleやcontrolがでないのってどうなの</h2>

<p>少し設計まわりネタなのですが、Titanium Mobile でプロジェクトを生成するとこんなコードが生成されます。(大幅に変更してるので、「こんな感じ」のコードと思ってください)</p>

<pre class="javascript">Titanium.UI.setBackgroundColor(<span class="string" >'#000'</span>);

<span class="keyword" >var</span> win1 = Titanium.UI.createWindow({  
  backgroundColor: <span class="string" >'#fff'</span>
});
win1.title = <span class="string" >'win 1'</span>;

<span class="keyword" >var</span> win2 = Titanium.UI.createWindow({  
  backgroundColor: <span class="string" >'#fff'</span>
});
win2.title = <span class="string" >'win 2'</span>;

<span class="keyword" >var</span> tab1 = Titanium.UI.createTab({  
  icon: <span class="string" >'KS_nav_views.png'</span>,
  title: <span class="string" >'Tab 1'</span>,
  window: win1
});

<span class="keyword" >var</span> tab2 = Titanium.UI.createTab({  
  icon: <span class="string" >'KS_nav_ui.png'</span>,
  title: <span class="string" >'Tab 2'</span>,
  window: win2
});

<span class="keyword" >var</span> tabGroup = Titanium.UI.createTabGroup();
tabGroup.addTab(tab1);  
tabGroup.addTab(tab2);  

tabGroup.open();</pre>

<p>このコードで実行すると、こんな画面が表示されます。<br />
<img src="http://blog.xole.net/resources/2011_03_07_0_15_15_.png" />
</p>

<p>下記のTabGroupいらないような画面だったので、使わないコードにすると、title が残るようになるのかなーと思うと、そうはいかなかったりする。</p>

<pre class="javascript">Titanium.UI.setBackgroundColor(<span class="string" >'#000'</span>);

<span class="keyword" >var</span> win1 = Titanium.UI.createWindow({  
  backgroundColor: <span class="string" >'#fff'</span>
});
win1.title = <span class="string" >'win 1'</span>;

<span class="keyword" >var</span> win2 = Titanium.UI.createWindow({  
  backgroundColor: <span class="string" >'#fff'</span>
});
win2.title = <span class="string" >'win 2'</span>;

<span class="keyword" >var</span> tab1 = Titanium.UI.createTab({  
  icon: <span class="string" >'KS_nav_views.png'</span>,
  title: <span class="string" >'Tab 1'</span>,
  window: win1
});

<span class="keyword" >var</span> tab2 = Titanium.UI.createTab({  
  icon: <span class="string" >'KS_nav_ui.png'</span>,
  title: <span class="string" >'Tab 2'</span>,
  window: win2
});

<span class="comment" >/*
var tabGroup = Titanium.UI.createTabGroup();
tabGroup.addTab(tab1);  
tabGroup.addTab(tab2);  

tabGroup.open();
*/</span>

win1.open();</pre>

<p>このコードを実行すると、下記のように真っ白になってしまうのである。<br />
<img src="http://blog.xole.net/resources/2011_03_07_0_20_10_.png" />
</p>

<p>確かに iPhone などの実装をみると NavigationGroup に入ってる必要がある(?)ので、上記のことをやろうとするならば、<a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.iPhone.NavigationGroup-object.html">Titanium.UI.iPhone.NavigationGroup</a>で書きなおせば動くけど、Titanium.UI.currentTab 的な便利なのもなくなってしまうので、Titanium.UI.currentTab.open(window, props)もできなくなってしまうのである。<br />
困ったなーと思って API を見ていると  <a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.Window-object">Titanium.UI.Window の property に tabBarHidden</a> なんてものがある</p>

<p>確かに下記のようなコードにすることで、やりたいことが実現出来ているのだが...<br />
<img src="http://blog.xole.net/resources/2011_03_07_0_32_55_.png" />
</p>

<pre class="javascript">:
:
:
win1.tabBarHidden = <span class="keyword" >true</span>;

<span class="keyword" >var</span> tabGroup = Titanium.UI.createTabGroup();
tabGroup.addTab(tab1);  
tabGroup.addTab(tab2);  

tabGroup.open();</pre>

<p>これらは window の property ではなく Tab もしくは TagGroup にある方がいいんではないだろうか。<br />
Ti.currentTab.windowTitle = 'hoge'; とかね。</p>

<h2>グローバルがないっぽいから、Titanium.App に入れてしまおうね。ってやつ</h2>

<p>@masuidrive に教えてもらったのをそのまま、なんですが、Titanium.App をGlobal代わりに使ってしまうやつです。<br />
例(下記サンプルコード)が悪いですが、何度も初期化したくないようなコードは一旦 Titanium.App につめて、必要に応じてそこの値を参照するようにしてみました。</p>

<pre class="javascript">
<span class="comment" >// utils.js</span>
(<span class="keyword" >function</span> (){
  <span class="keyword" >if</span>(<span class="keyword" >typeof</span> Titanium.App.StringTemplate == <span class="string" >'undefined'</span>){
    <span class="keyword" >var</span> StringTemplate = <span class="keyword" >function</span> (template, pattern){
      <span class="keyword" >this</span>.template = template;
      <span class="keyword" >this</span>.pattern = pattern || StringTemplate.Pattern;
    };
    StringTemplate.Pattern = /#\{((\d|\w)+)\}/g;
    StringTemplate.prototype.evaluate = <span class="keyword" >function</span>(parameter){
      <span class="keyword" >return</span> <span class="keyword" >this</span>.template.replace(<span class="keyword" >this</span>.pattern, <span class="keyword" >function</span> (target, match){
        <span class="keyword" >return</span> parameter[match];
      });
    };
  }

  String.prototype.template = <span class="keyword" >function</span> (){
    <span class="keyword" >var</span> proto = Titanium.App.StringTemplate;
    <span class="keyword" >return</span> <span class="keyword" >new</span> proto(<span class="keyword" >this</span>);
  };
  String.prototype.interpolate = <span class="keyword" >function</span>(obj){
    <span class="keyword" >return</span> <span class="keyword" >this</span>.template().evaluate(obj);
  };
})();

<span class="comment" >// foo.js</span>
require(<span class="string" >'utils'</span>);
<span class="keyword" >var</span> tpl = <span class="string" >'#{hoge} #{foo}'</span>.template();
<span class="keyword" >var</span> value = tpl.interpolate({hoge: <span class="string" >'hello'</span>, foo: <span class="string" >'world'</span>});

<span class="comment" >// bar.js</span>
require(<span class="string" >'utils'</span>);
<span class="keyword" >var</span> value = <span class="string" >'is #{bar} value'</span>.interpolate({bar: <span class="string" >'bar'</span>});</pre>

<h2>Titanium.App.appURLToPath('app://path/to') っていう相対パス参照の方法がある。らしい</h2>

<p>Titanium.include だったり、 Titanium.UI.Window.url だったり Titanium.UI.ImageView:image だったりパス指定の相対パスでハマりがち(特にandroidで)な、相対パス問題なんですが、<br />
今までこんなコードを書いてどうにか対処してました。</p>

<pre class="javascript">Titanium.Platform.isAndroid = /android/i.test(Titanium.Platform.osname);
<span class="keyword" >var</span> utils = {
  resourceFile: <span class="keyword" >function</span>(resourceDirRelativeDirPath, fileName){
    <span class="keyword" >var</span> dirPath = Titanium.Filesystem.resourcesDirectory + resourceDirRelativeDirPath;
    <span class="keyword" >if</span>(Titanium.Platform.isAndroid){
      dirPath = dirPath.replace(<span class="string" >'//'</span>, <span class="string" >'/'</span>);
    }
    <span class="keyword" >return</span> Titanium.Filesystem.getFile(dirPath, fileName);
  },
  resourcePath: <span class="keyword" >function</span>(resourceDirRelativeDirPath, fileName){
    <span class="keyword" >var</span> file = utils.resourceNativeFile(resourceDirRelativeDirPath, fileName);
    <span class="keyword" >var</span> nativePath = file.nativePath;
    <span class="keyword" >if</span>(Titanium.Platform.isAndroid){
      <span class="keyword" >return</span> nativePath.replace(/^(file\:\/\/\/android_asset\/Resources)/, <span class="string" >''</span>);
    }
    <span class="keyword" >return</span> nativePath;
  }
};</pre>

<p>どうやら、<a href="http://hamasyou.com/blog/archives/000398">Titanium.App.appURLToPath('app://path/to') で参照する方法</a>があるらしい。</p>

<p>調べてみると、<a href="http://developer.appcelerator.com/apidoc/desktop/latest/Titanium.App.appURLToPath-method">Titanium Desktop のメソッド</a>らしい<br />
<a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.App-module">Titanium Mobile の Titanium.App の API docs</a>には無いみたいだけど、使えるならばこっちを使うほうが良さそう</p>

<h2>setTimeout とかは気をつけましょうね。ってやつ</h2>

<p>すごく単純で。window 単位とかで setTimeout するとコードが実行前に close しちゃってハマるかも。ってやつです。</p>

<pre class="javascript">
<span class="comment" >// app.js</span>
Titanium.UI.setBackgroundColor(<span class="string" >'#fff'</span>);

<span class="keyword" >var</span> win = Titanium.UI.createWindow({
  url: <span class="string" >'win1.js'</span>
});

<span class="keyword" >var</span> root = Titanium.UI.createWindow();
<span class="keyword" >var</span> label = Titanium.UI.createLabel({
  top: 0,
  text: <span class="string" >'win1 value = '</span>
});
root.add(label);
root.add(win);
root.open();

setInterval(<span class="keyword" >function</span> (){
  label.text = win.extern.value;
}, 100);

win.open();</pre>

<p>このコードは、win1.js っていう別の window を開いて、win.extern (画面間で引き継ぐ値をいれとくハコ) の値を watch してるやつ<br />
win1.jsの実装はこんな感じ。いつでも閉じれるように close ボタンを用意している</p>

<pre class="javascript">
<span class="comment" >// win1.js</span>
<span class="keyword" >var</span> win = Titanium.UI.currentWindow;
win.extern = {
  value: <span class="string" >'before timeout'</span>
};

<span class="keyword" >var</span> label = Titanium.UI.createLabel({
  text: <span class="string" >'hello world'</span>
});
<span class="keyword" >var</span> button = Titanium.UI.createButton({
  title: <span class="string" >'close'</span>
});
button.addEventListener(<span class="string" >'click'</span>, <span class="keyword" >function</span> (){
  win.close();
});

win.add(label);
win.add(button);

setTimeout(<span class="keyword" >function</span> (){
  label.text = <span class="string" >'timeouted!'</span>;
  win.extern = {
    value: <span class="string" >'timeout!'</span>
  };
}, 5000);</pre>

<p>実行してもらえるとわかるけど、setTimeout が発生しているタイミングによっては、値が入ってなかったりする(サンプルコードが分かりにくいけど)</p>

<p>なので、こういうコードは close を押させないようにするとか、実行するデータは database に書いておくとか、復帰可能なコードを残しておく必要があるようです。<br />
少なくとも window が閉じると、そのコンテキストで動作していた値の参照はできなくなるようです。</p>

<h2>その他</h2>
<p>否定的なことばかりな事ばかり書いてるけど、こんなに簡単に(しかも javascript で!) iPhone とか android のコードが書ける Titanium Mobile は好きです。(マゾではないですよ。一応)</p>

<p>思い出したら、新しく書き起こす。きっと。。</p>]]>
</content:encoded>
</item>
</channel>
</rss>
