ゲームサーバ作り日記

create_roomみたいな粒度の細かい命令も将来的にBOT対戦とかやるには必要だけどとりあえず今週末のうちに人間がプレイを出来るようにすることを考えて最短パスを通りたい。今日は東急ハンズに行くし(ぉ

  • start_single_game(/player_id/password)
    • create_roomとAI二人のjoin
    • カードを配って、プレイ待ちにはいる
    • API的にはgame_idを返す必要がある
  • get_status/game_id(/player_id/password)
    • game_idのゲームに関する情報を取得
    • 人間的にはプレイ中に見ている画面
    • AI的にはちょっと粒度の高い情報取得API
  • play/card_id(/player_id/password)

BOT用のAPIと人間が見る画面とを対応づける必要があるだろうか。

  • 対応づける場合
    • 各カードは/api/play/card_id/htmlにリンクされていてクリックするとHTTPでAPIを叩いてから帰ってきた情報で/api/game_status/htmlにリダイレクト
  • 対応づけない場合
    • 各カードは/games/game_id/?play=card_idとかにリンクされていてユーザ視点で「ふつう」なURL

後者かなぁ。前者も「人間のアクセスかBOTのアクセスか区別しません」とかちょっと面白いけど。

        • -

13:36
モデルの定義をする。モデルの定義と小さな遷移のデモを2時間くらいで作ったら東急ハンズに行くとしよう。

Twitterもどきを作りながらGoogle App Engineの肝データストアを理解する - page2 - builder by ZDNet Japan経由でTypes and Property Classes - Google App Engine - Google Codeを読む。

IMとかRatingとかあるのがちょっと面白いな。ForeignKeyはdb.ReferenceProperty(Author)みたいに書くのか。

予告通り、とりあえずBlobを1個持っているだけのモデルを作ってみるつもりだったけど、ついでにゲーム開始時間も持たせてみた。

class Game(db.Model): 
    opened = db.DateTimeProperty(auto_now_add=True)
    info = db.BlobProperty()

views.pyではとりあえずそのオブジェクトを作ってputする。

def main(request):
    game = Game(info="")
    game.put()
    ...

NameError at /
global name 'Game' is not defined

インポートし忘れ。from saichugen.main.models import Game

おおー、できたー。簡単すぎる。DjangoだからオートインクリメントでIDを振ってくれる。Djangoを初めて使ったときにCGIよりだいぶ簡単だ(特にデータの永続化まわりが。って言っても比較対象はKENT CGIを改造して遊んでいた大学1年の僕だけど)と思ったのだけど、Django on GAEはそれよりもさらに簡単だ。

次は情報を取る方だ。info = request.get("info")と書いてエラーになった。'WSGIRequest' object has no attribute 'get'だって。そりゃそうか、Djangoのrequestオブジェクトだもんな。request.GET.get("info")したら動いた。

printデバッグは出来ないのか。Djangoだとprintしたものはコンソールに出るからHTTPヘッダとか気にせずにprintデバッグできたのだけど

    info = request.GET.get("info")
    print info

ってやったら

hello
Status: 200 OK
Content-Type: text/html; charset=utf-8

Hello, world!

になった。

        • -

BOT用のAPIと人間が見る画面とを対応づける必要があるだろうか。

  • 対応づける場合
    • 各カードは/api/play/card_id/htmlにリンクされていてクリックするとHTTPでAPIを叩いてから帰ってきた情報で/api/game_status/htmlにリダイレクト
  • 対応づけない場合
    • 各カードは/games/game_id/?play=card_idとかにリンクされていてユーザ視点で「ふつう」なURL

後者かなぁ。

って書いたけど、たとえば新規ゲームを始める際には/start_new_gameとかにアクセスして、そこでユニークなgame_idが割り当てられて/game/game_idとかにリダイレクトされるのが自然な挙動のような気がしてきた。

リダイレクトはどうやるのだろう。

ついつい「Google App Engineでリダイレクトってどうやるんだろう」とか思ってしまうけどDjangoだよな。忘れる。

最新のドキュメントってこれ?1年以上Djangoを触っていないから忘れた。

    import cPickle
    deal = logic.deal_cards()
    game = Game(info=cPickle.dumps(deal), memo="new single game")
    game.put()
    return HttpResponseRedirect("/game/%d" % game.id)

オブジェクトのIDを取得するのどうやるんだろう。これじゃエラーになる。

Keys and Entity Groups - Google App Engine - Google Code
obj.key().id()だって。

んー、idからオブジェクトを取得するのはどうするのかな。
Creating, Getting and Deleting Data - Google App Engine - Google Codeをみるとkeyからの取得しか書いていないけど、keyはuglyだからユーザに見せたくない。
あ、Model.get_by_idってのがある。
The Model Class - Google App Engine - Google Code

        • -

/start_new_gameにアクセスすると新しいゲームオブジェクトを作って、それをDataStoreで永続化し、/game/game_idにリダイレクトをして、/game/game_idへのリクエストでgame_idを見てデータから取り出して表示、ってところまでできた。

まだまだだけどdiffが大きくなると読みづらいからコミットしておいた。
http://coderepos.org/share/browser/lang/python/saichugen/gae

        • -

予定通り2時間だ。今から10分でお風呂にお湯を張って、のんびり入ってから東急ハンズに行く。散髪もしたいんだよなぁ。

帰ってきてからの作業リスト

  • 認証は必須だと気がついた。プレイ中の画面を他人に見せたい以上、カードを出せるのはプレイヤーだけにしないといけない。とりあえず人間だけ認証するってことでGoogleの認証APIを使うけど、BOTのためにそこをバイパスできるような設計にしておく必要があるな。
  • cPickleの結果を突っ込んであるBlobがあるけど、そんなことをしないでもExpandoってのを使うと目的は果たせそうな気配がする。まだちゃんと読んでない。
  • あとDataStoreって動的にフィールド追加したりしてもぜんぜん平気なんだな。素直にモデルをいじればよかったんだ。doukaku.orgの時に「当初作ったモデルから形を変更したくなって移行に苦労」ってのを経験したから臆病になっていたけど、それってRelational Database脳なのかも。