2013年11月26日火曜日

在中日本人108人のそれでも私たちが中国に住む理由

第1章 あのデモがあった日
第2章 それでも「日本」が要る!
第3章 暮らしのなかの「島」問題
第4章 日中の誤解を生むもの
第5章 「反日」下のビジネス
第6章 国境を超え伝える
第7章 隣人との未来へ




悪化する日中関係のさなかにおいて、中国に住む日本人は日々友人や家族から安否を気遣われている。日本のメディアを通してみる反日としての中国は、攻撃的で破壊的であるようにしか映らない。

しかしながら現地に住む日本人によれば、日本のメディアで報じられているほど危険な状態ではなく、またすべての中国人が日本嫌いなわけではなく、親日的な人たちも少なからずいるとのことだ。

本書ではそのような現地の人達の声をまとめた本である。

本を読むについて、日本人と接触したことがなく、反日教育の影響でただ当然のように日本を嫌っている中国の現状が窺えてくる。そのため、わずか数分の日本人との交流で、急に友好的な態度に改める者も少なくないようであり、そのような記述が随所に描かれている。実態を知らないからこそ、盲目的に嫌うことが出来るのであろう。

確かに、私自身を思い返して中国に大してマイナスなイメージを持っていないのは、日本で知り合った多くの中国人留学生のおかげである。彼ら彼女らが非常に友好的で、なんら日本人と変わらず付き合えることを知っているし、今でも同じ職場で仕事をしているおかげで、日本のメディアの報道を客観的に受け止められているといえる。

そう考えると、国レベルではなく、個人レベルでの日中交流は、決して絶やしては行けないのかもしれない。ここまで関係が悪化すると実現自体のハードルも高くなるが、だからこそ、最低限のできることとして人と人との交友は絶えないことを願いたい。

一方でそうは言うものの、日本人というだけでタクシーの乗車拒否をしたり、安全確保の名目のためとはいえ、有事には公共の場で日本語を使うのをやめるように警察から指示されるなど、冷静に考えれば異様な事態である。本書の中の人物はどなたも淡々と受け止めている様子だが、部外者の私には侮辱にも程があるように感じる。もはや反日は中国にとって文化や条件反射にさえ感じられ、関係改善は相当路が険しいと感じた。

2013年11月19日火曜日

Google App Engineのデータをエクスポートする方法

iOSのランキングを日々取得しているiOSランキングトラッカーは、無課金のGoole App Engineで動かしていますが、そろそろ容量の上限値1Gに達しそうな勢いです。

収益があるわけでもない、ただの趣味Webサービスに金をかけることも出来ないので、古いデータを外出しする方法を調べてみました。

今回はそのうち、Google App EngineのBigQuery上にあるデータを手元のデスクトップPCにコピーする手順を紹介します。

1. app.yamlを編集する

下記の二行を追加する。

builtins:
- remote_api: on

2. コマンドを実行してアプリケーションを更新する。

<GAEのインストールパス>/appcfg.py update <app-directory>
注: <app-directory>は直下にapp.yamlがおいてあるディレクトリを指定する必要がある。


3. コピーの実行

%><gae-install-dir>/appcfg.py download_data --application=<app-id> --url=http://<appname>.appspot.com/[remote_api_path] --filename=<data-filename>
<app-id>はapp.yamlに定義した"application"の値。
ただし、high-replication datastoreを使ってる場合、appidの先頭にs~をつける必要がある。
例) applicationの値がexampleで、high-replication datastoreを使ってる場合は下記のようになる。
%><gae-install-dir>/appcfg.py download_data --application=s~example --url=http://example.appspot.com/_ah/remote_api --filename=/tmp

補足

どうやら、このコマンド実行もDataStoreのリクエスト制限の対象のようで、OverQutaErrorが出てしまいました。
OverQuotaError: The API call datastore_v3.RunQuery() required more quota than is available.
 このような場合は下記のようにkindを指定したほうがよさそうです。
%><gae-install-dir>/appcfg.py download_data --application=example --kind=<kind> --url=http://example.appspot.com/_ah/remote_api --filename=/tmp

参考文献

データのアップロードとダウンロード

2013年11月10日日曜日

第一回AI麻雀大会開催しました

先日公開したAI麻雀サーバを利用して第一回AI麻雀大会を開きました。

第一回ということでとりあえず都合のついた二人だけで対戦しました。

まずユーザのプロフィール紹介。

ユーザ名 戦略の概要
developing 喰いタンのみ、役牌のみなどを狙い誰よりも早く上がることを目指す
shizimily7 シャンテン数が一番小さくなるものを計算して捨て牌を選択。役があるかは考慮せず リーチを掛けることによって最低限の役を確保する戦略

そして結果の概要。

ユーザ名 勝利回数
developing 5
shizimily7 93
*引き分け 2回


100回行った対戦の平均点数は次のようになりました。
ユーザ名 平均点数
developing 22942
shizimily7 30638
sampleAI1 22368
sampleAI2 22056


結果としてはshizimily7の圧勝。結果を解析すると、上がり回数、平均上がり得点ともにdevelopingより優秀。特に普通に 七対子や大三元を上がるなどソツのない優秀なAIに仕上がる。developingは鳴きを駆使して早上がりを目指す戦略にであったが上がり回数でも shizimily7を下回っていることを考えると、もう少しプログラムの作り込みに期待したいところ。      

2013年11月1日金曜日

麻雀のAIサーバ作りました

麻雀のAI(Artificial intelligence)サーバを作りました。

https://github.com/kentan/SmartBird

最近では電王戦と称した将棋のAIが話題ですが、
麻雀のAIを作っていろいろ遊んでみたくしてみたくなり、このたびAIサーバを作成してみました。

将棋・チェス・碁などはいわゆる「二人ゼロ和有限確定完全情報ゲーム」と呼ばれるもので、

簡単にいうと、下記のようなゲームのことを意味します。


  • 一方の負け点数が他方の勝ち点数になる(二人ゼロ和)
  • 戦略の数が有限(有限)
  • サイコロをふるなどの運要素がない(確定)
  • 戦略を判断するための情報を全部知ることが出来る(完全情報)



有限で完全情報であるゲームは基本的にコンピュータのリソースをふんだんに使うと強くなります。
10年ほど前にはプロの相手にならなかった将棋のAIがちらほら勝てるようになったのもそのためです。

それに対し、麻雀は


  • 4人ゲーム(Not 二人ゼロ和)
  • 次に選択できる手が(ツモる牌)が確定的でない(Not 確定)
  • 戦略を決定するための情報のほとんどを知ることが出来ない(Not 完全情報)


といった特徴があり、将棋や碁よりもAIの難易度が高いといえます。






2013年10月15日火曜日

App Ranking HIstoryにチャート機能を実装した

AppleのRSSからiPhoneアプリのランキングを取得するApp Ranking Historyにチャートを導入してみました。

気になるアプリのランキング推移をチャートで確認できます。



google chart apiを使って作りました。
定番のchartはひと通り揃ってていい感じです。

一応スマフォでも見れるようには作りましたが、レイアウトとか見栄えが若干いまいち。

iPhoneのサファリで株価とかgoogle検索すると綺麗に表示されるんだけど、これはどうやってるんだろうか。これもgoogle chart apiつかってるのだろうか。




ところで、このサービス、暇な時にちょこちょこいじってましたが、ここまで来ると次はアプリ名で検索とかしたくなりますね。まあそれは気が向いた時にでもやってみます。

2013年8月4日日曜日

iOSアプリトラッカー アップデートしました

前にブログで紹介したiTunesストアで1位をとったアプリを時系列で表示させるiOSアプリトラッカーをちょっとだけ機能拡張しました。

今回の修正では1位だけじゃなくて2位以降も表示出来るようにしました。






日付のところをクリックすると、今まではiTunesストアのアプリページに飛んでいたのですが、今回からは次のような画面に遷移して2位以降のアプリも見ることができます。

2013年7月16日火曜日

Kotlinはじめました。

IntelliJ IDEAで有名なjetbrains社が推進しているスクリプト言語Kotlin始めました。

Kotlinは静的型付けのJVM上で動作するスクリプト( statically-typed JVM-targeted programming language)です。


公式サイトによると、以下の項目を開発のゴールとしている。
  • Java互換の言語であること
  • Javaと同等の実行速度であること
  • NullPointerError Exceptionの静的にチェックによりJavaより安全であること
  • 動的型推論、高階関数、extension functionなど様々な機能による豊か、かつ簡潔な表現力をもつこと

ふーむ。ということでとりあえず書いてみる。Hello World。


package demo 
 
fun main(args : Array<string>) { 
  println("Hello, world!") 
}

しかし、さすがにHello Worldしただけじゃ面白くないので、15 puzzle書いてみた。
ただの反復深化です。枝狩りなし。githubはこっち




package demo
 
fun main(args : Array<string>) {
    val data = array(1,2,3,0,5,6,7,4,9,10,11,8,13,14,15,12);
    var limit:Int = 0;
    var LIMIT_MAX:Int = 20;
    while(limit <= LIMIT_MAX){
        print(limit);
        if(run(data,0,-1,limit)){
            break;
        }
        limit += 1;
    }
}
 
fun search_white_space_pos(data : Array<int>):Int{
    for(i in data.indices){
        if(data[i] == 0){
            return i;
        }
    }
    return -1;
}
 
fun finish(data : Array<int>):Boolean{
    for(i:Int in data.indices){
    if(i == 15){
        if(data[i] == 0){
            return true;
        }else{
            return false;
        }
    }else{
        if(data[i] == i + 1){
            continue;
        }
        return false;
    }
}
return true;
}
 
fun is_move_down(white_space_pos:Int):Boolean{
    if(white_space_pos >= 12 && white_space_pos <= 15){
        return false;
    }
    return true;
}
fun is_move_up(white_space_pos:Int):Boolean{
    if(white_space_pos >= 0 && white_space_pos <= 3){
        return false;
    }
    return true;
}
fun is_move_left(white_space_pos:Int):Boolean{
    if(white_space_pos == 0 || white_space_pos == 4 || white_space_pos == 8 || white_space_pos == 12){
        return false;
    }
    return true;
}
fun is_move_right(white_space_pos:Int):Boolean{
    if(white_space_pos == 3 || white_space_pos == 7 || white_space_pos == 9 || white_space_pos == 15){
        return false;
    }
    return true;
}
fun swap(data:Array<int>,pos1:Int,pos2:Int){
    var temp:Int = data[pos2];
    data[pos2] = data[pos1];
    data[pos1] = temp;
}
 
fun show(data:Array<int>){
    for(d in data){
        print(d);
        print(" ");
    }
    println();
}
 
fun run(data : Array<int>,depth:Int,previous:Int,limit:Int):Boolean{
    var white_space_pos:Int = search_white_space_pos(data);
 
    if(depth > limit){
 
        return false;
    }
    if(finish(data)){
        println("found");
        show(data);
        return true;
    }
    if(previous != 1 && is_move_down(white_space_pos)){
        swap(data,white_space_pos,white_space_pos + 4);
        if(run(data,depth + 1,0,limit)){
            return true;
        }
        swap(data,white_space_pos + 4,white_space_pos);
    }
    if(previous != 0 && is_move_up(white_space_pos)){
        swap(data,white_space_pos,white_space_pos - 4);
        if(run(data,depth + 1,1,limit)){
            return false;
        }
        swap(data,white_space_pos - 4,white_space_pos);
    }
    if(previous != 3 && is_move_left(white_space_pos)){
        swap(data,white_space_pos,white_space_pos - 1);
        if(run(data,depth + 1,2,limit)){
            return false;
        }
        swap(data,white_space_pos - 1,white_space_pos);
    }
    if(previous != 2 && is_move_right(white_space_pos)){
        swap(data,white_space_pos,white_space_pos + 1);
        if(run(data,depth + 1,3,limit)){
            return false;
        }
        swap(data,white_space_pos + 1,white_space_pos);
    }
    return false;
}

Kotlinっぽい機能全く使ってないけど、普通のjavascriptでも書いてる気分でかけました。とりあえずとっつきやすそう。

参考文献