2014年11月26日水曜日

麻雀の牌を表示するJavascriptライブラリ


以前に公開したJavaの麻雀AIサーバであるSmartBirdから麻雀の牌を描画する部分だけ取り出してライブラリ化しました。

https://github.com/kentan/MahjongTileDrawingJS/tree/master




麻雀の牌はフリー素材などがいっぱいあったりしますが、今回公開したのはHTML5のcanvasタグを使ってその場で描画するタイプです。
使い方は下記のようにシンプル。
萬子はCharacter,索子はBamboo,筒子はCircleで表現しているので例えば5索を書きたい場合はdrawBamboo5("canvasタグのID")といったふうに呼び出せば、引数に指定したcanvasタグに5索が描かれます。

サンプル用のhtmlファイル
<html>
   <head></head>
   <body onload="startDrawing()">
   <canvas id = "canvas1" width="50px" height="60px"></canvas>
</body>
ライブラリを呼び出すサンプル用のjavascript
  function startDrawing(){
      tileDrawing.drawCharactor1("canvas1");
  }

詳細はgithubにあるサンプルコードを見てください

Javaの文字コード変換サンプルコード2

java.nio.charset.Charsetにあるdecodeとencodeの挙動が気になったのでサンプルを書いてみた。

まず、仕様の簡単な説明。

Charset.encodeは、入力としてStringを受け取り、出力として任意の文字コードのバイト列を出力する。

Charset.decodeは、入力として、UTF-8とかShift-JISでエンコードされた文字のバイト列を与えると、出力としてUnicode(UTF-16)で変換された文字のバイト列が出力される。

文字コードの指定は下記のようにCharsetのインスタンスを取得する過程で行う。

Charset cs = Charset.forName("Shift-JIS")
cs.encode(new String("エンコードしたい文字")); 

次に、サンプルコードを書いて、encode/decodeした時にどのようなバイト列が格納されているのか確認してみた。


public static void sampleEncoder(String target,Charset cs){
 /** まずは元のStringデータをCharに分解して確認してみる**/
 System.out.print("Original: ");
 char[] chars = new char[target.length()];
 target.getChars(0,target.length(),chars,0);
 for(int i = 0; i < chars.length; i++){
  System.out.print((int)chars[i] + " ");
 }
 System.out.println();

 /** 次にStringを指定されたEncodeに変換し、バイト表現を確認する **/
 ByteBuffer bf = cs.encode(target);
 System.out.print(cs.toString() + ": ");
 for(int i = 0 ; i < bf.array().length; i++) {
  System.out.print((int)bf.array()[i] + " ");
 }
 System.out.println();
 /** 最後にEncodeした文字を、同じ文字コードで再びDecodeし、Charとしての表現を確認する **/

 CharBuffer cb = null;
 cb = cs.decode(bf);

 StringBuffer sb = new StringBuffer();
 System.out.print("Unicode(UTF-16): ");

 for(int i = 0 ; i < cb.array().length; i++) {
  System.out.print((int)cb.array()[i] + " ");
  sb.append(cb.array()[i]);
 }

 System.out.println(" :" + sb.toString());
 System.out.println();
}
public static void main(String args[]){
 sampleEncoder("日本語",Charset.forName("ISO-2022-JP"));
 sampleEncoder("日本語",Charset.forName("Shift-JIS"));
 sampleEncoder("日本語",Charset.forName("EUC-JP"));
}
これの実行結果は次のようになる。

Original: 26085 26412 35486 
ISO-2022-JP: 27 36 66 70 124 75 92 56 108 27 40 66 
Unicode(UTF-16): 26085 26412 35486 0 0 0  :日本語


Original: 26085 26412 35486 
Shift_JIS: -109 -6 -106 123 -116 -22 
Unicode(UTF-16): 26085 26412 35486  :日本語

Original: 26085 26412 35486 
EUC-JP: -58 -4 -53 -36 -72 -20 0 0 0 
Unicode(UTF-16): 26085 26412 35486  :日本語


見方を少し説明すると、Originalと書いてある行には、"日本語"を代入したStringのchar表現が出力されている。
次の行はそれぞれの文字コードでエンコードした結果のバイト列表現。
そして、3行目にはエンコードした結果を再度デコードし、一行目の値と同じか確認をしている。

出力結果を見ると、encodeした結果はそれぞれ違うバイト表現が入っているので、おそらくその文字コードでエンコードされた結果が入っていることが確認できる。そして、それらをdecodeした結果は再度String型の時と同じ表現に戻っているようだ。

ISO-2022-JPの時には、最後に0,0,0と他にはないバイトが入っているが。これはなぜだろうか。。
標準出力自体は"日本語"と正しく出力できているようだが。

2014年11月25日火曜日

CodingErrorAction.IGNOREとCodingErrorAction.REPLACE とCodingErrorAction.REPORT

CodingErrorAction.IGNOREとCodingErrorAction.REPLACE とCodingErrorAction.REPORTを使ったサンプルコードを書いてみました。


public static void sampleCodingErroAction(String hex, CodingErrorAction actionOnMalformedInput, CodingErrorAction actionOnUnmappableCharacter){


 ByteBuffer buff = ByteBuffer.allocate(hex.length()/2);
 for (int i = 0; i < hex.length(); i+=2) {
  buff.put((byte)Integer.parseInt(hex.substring(i, i+2), 16));
 }
 buff.rewind();
 Charset cs = Charset.forName("shift-jis");
 CharBuffer cb = null;
 try {
  cb = cs.newDecoder()
   .onMalformedInput(actionOnMalformedInput)
   .onUnmappableCharacter(actionOnUnmappableCharacter)
   .decode(buff);
  System.out.println(cb.toString());
 }catch(Exception e){
  System.out.println("error");
 }

}
public static void main(String args[]){
 String hex1 = "93FA967B8CEA";                   //日本語
 String hex2 = "93FA967B8C00EA";                   //不正な文字
 System.out.print("正しいShift-JIS ");//A
 sampleCodingErroAction(hex1, CodingErrorAction.IGNORE, CodingErrorAction.IGNORE);

 System.out.print("不正なShift-JIS(Ignore) ");//B
 sampleCodingErroAction(hex2, CodingErrorAction.IGNORE, CodingErrorAction.IGNORE);
 System.out.print("不正なShift-JIS(Replace) ");//C
 sampleCodingErroAction(hex2, CodingErrorAction.REPLACE, CodingErrorAction.REPLACE);
 System.out.print("不正なShift-JIS(Report) ");//D
 sampleCodingErroAction(hex2, CodingErrorAction.REPORT, CodingErrorAction.REPORT);

}

hex1はshift-jisで書かれた"日本語"という文字列の16進法表記です。
hex2は、hex1の間に不正なCharacterである00を挟んだ"不正な"文字列です。

これを実行すると出力結果は下記のようになります。


正しいShift-JIS 日本語
不正なShift-JIS(Ignore) 日本
不正なShift-JIS(Replace) 日本��
不正なShift-JIS(Report) error

Aの場合正しいShift-JISコードを変換しているので、「日本語」と正しくでます。CodingErrorAction.IGNOREを指定していますが、不正な入力はないので、この値は使われることはなく、何を指定しても同じです。

B~Cの場合は、不正な値があるので、onMalformedInput、onUnmappableCharacterの指定の仕方によって出力結果が違います。

CodingErrorAction.IGNOREを指定した場合、不正な値は無視されますので、正しく解釈できた「日本」までが出力されています。なお、「日本」の直後は空白文字などが出力されているわけではなく、改行されています。

Cの場合は、不正な値をReplaceしているので、��というReplaceされた値が出力されています。

Dの場合は、不正な場合はエラーをReportするように指定しているので、Exceptionが発生しdecodeがされないままで終了しています。




2014年11月22日土曜日

javaの文字コード変換サンプルコード

javaの文字コード変換はString.getBytes()を使う方法が主流な様子だけど、別の書き方を見つけたのでメモしておく。

 public void testEncoding(){
  String target = "日本語"; 
  Charset cs = Charset.forName("UTF-8");
  //Charset cs = Charset.forName("Shift-JIS"); // SHIFT-JISに変換したい場合はこれを使う。
  //Charset cs = Charset.forName("EUC-JP");// EUC-JPに変換したい場合はこれを。
  //Charset cs = Charset.forName("ISO-2022-JP");//ISO-2022-JPに変換したい場合はこれを。
  ByteBuffer bf = cs.encode(target);

  try {
   File file = new File("output.txt");
   FileOutputStream fos = new FileOutputStream(file);
   fos.write(bf.array());
   fos.close();

  }catch(Exception e){
   e.printStackTrace();
  }

 }


なお、正しく出力されているのか確認したい場合はブラウザを使うと手っ取り早い。テキストファイルをブラウザで開き、文字コードを変更することによって希望のエンコーディングがされているか確認できる。


参考文献
JavaでUTF-8の文字コードを判定する
[Java]いいから聞け! 俺が文字コードについて教えてやるよ その2(Javaの文字コード編)

2014年11月14日金曜日

アルゴリズムが世界を支配する

タイトルにアルゴリズムとあるが、中身の多くは最近良く話題になる人工知能について割かれている。アルゴリズム自体の話よりも、いかに使われてきたか、それを開発した人たちにどのようなドラマがあったかを中心にまとめられている。

ウォール・ストリートの株式市場で人工知能が使われていることは有名だが、本書によると1987年ころには早くも使われていたとのことだ。正直、インターネットも一般的に普及していないような時代から利用されていたとは意外であった。

また、本書の別の箇所では、山をぶちぬいて二地点間を直線に高速回線を引いてしまうエピソードが載せられている。「最低でも2億ドル」と本書が述べる工事予算に出資していまう投資家がさらりと現れるところが、なんともアメリカらしい逸話だ。

金融以外では、音楽の自動作曲を試みた大学教授の話が興味深い。楽曲自体は優れたものでありながらも、自動生成されたということを知った途端、聴衆の興味が削がれている様など、古くから人工知能の限界に挑んでいた様子が詳細に描かれている。


Hatching Twitter(Twitter創業物語)


Twitterの創業にまつわるエピソードを書いた本。

Twitterを立ち上げる前に取り組んでいたサービスなども詳細に書かれており、どのような経緯でTwitterが生まれていったかよく分かる。

また創業者間の人間関係の確執なども、せきららに書かれており、Twitterの創業の場に居合わせたかのような臨場感を味わうことが出来る。正直、よく許可を出したなと思えるくらい恥部が満載なのも読み手としては非常におもしろい。

読んでいて興味深いと思ったことをいくつか纏めてみる。

1. 創業者の一人(Evan)はすでに、スタートアップで実績があった
ずっと利益0の状態なのに、なぜか投資家による資金投入が行われているのは本を読むから知っていて、アメリカは違うなーと単純に思ってたんですが、EvanはすでにGoogleにブログサービス(私も使っているこのBlogger)を売却した実績があって、スタートアップ界隈ではすでに有名人だったようだ。
別に有象無象の人間に投資を続けていたわけではなく、投資されるだけの理由がちゃんとあったわけです。あと、はじめの方はEvan本人がBloggerの売却益を回したりしていたようだ。

2. テレビ業界などのメディアが積極的にTwitterを利用していた。
最近は若干ましになった気もするが、日本のメディアのIT嫌いは異常なほど。それに比べると積極的にテレビの番組などでTwitterを利用している様子は印象的だった。