key_nameメモ

  • Foo(key_name=...)でキーの名前を指定できる
  • foo.key_nameになってるかと思いきやfoo.key().name()だった
  • Foo.get_by_key_name(...)でその「キーの名前」でアクセスできる
  • key_nameがコンフリクトしたら例外とか飛ぶのかなーと思ったけど飛ばなかった。
  • 試しにDateTimeProperty(auto_add_now=True)をつけて何度か同じkey_nameでアクセスしてみたら、その日時の情報は追加のたびに変わって行ったがDataStore Viewerに現れた「日時情報のついているエントリー」は1個だけだった。つまりkay_nameがかぶった場合は警告無しに上書きされる、ということだな。

なお上記の調査は全部ローカルの開発サーバで行ったので本番環境では違うかもしれない

こんな感じで使おうと思っている:

def generate_key(prefix="", salt=DEFAULT_SALT):
    from datetime import datetime
    import hashlib
    time = str(datetime.now()) + salt
    return prefix + hashlib.md5(time).hexdigest()

ちなみに数字で始まるkey_nameは禁止されているので「game_」とかプレフィックスつけて使おうと思っている。わかりやすいし。

APIドリブン

いつもウェブアプリを作るときには画面の遷移の流れを追って作るんだけど、今回はAPIの仕様策定からスタートした。なるべく多くの人が手軽にボットを作れるような遊び場を作ることが目的なのでなるべくシンプルなフォーマットでなるべく少ない情報をやり取りしてゲームを進めることを考えている。これはこれで違う視点から見れて切り分けがはっきりしていいなぁ。
人間が操作するブラウザ上の画面遷移を考えているとそれに引きずられて「ここではこんな情報も出てくれるとうれしいな」とか「このボタン一つで何々ができたらいいのに」ってのに引きずられて本来複数に分割すべき処理が一つに固まってしまったりする。そしてそれがくっついていることが問題であることに気づけない。
具体的に言うと最中限で最初に/start_new_gameで「自分とランダムAIが2人着席したゲームを作る」というようにしてしまったために、対人戦やAIの選択をするための自然なデザインが思いつかなかった。新しいゲームを作成する時点で、そのプレイヤー3人を指定しないと行けないような思い込みをしていた。実際には/start_gameで誰も着席していない新しいゲームを作成してパーマリンクを作成し、そこからjoin(自分の参加)やadd_player(AIの追加)すればいい。対人戦ならそのパーマリンクを相手に送って相手がjoinすればいい。気づいてみれば当たり前なんだが、古いデザインにとらわれるとなかなか気づけないもんなんだなぁ。

LinCityがHomelessだらけになっている


快適な環境を作って放置していたら住む場所もないのに産めよ増やせよしたらしい。人口の98%くらいホームレス。

人口が多すぎてうっとうしいのでちょっと死んでもらおうかと思ったが(物騒)
農場を配置しすぎて飢え死にしてくれない。2.0に入っていた「井戸」の概念を最初は「無駄にすること増やしてよくないな」と思っていたけど、このシチュエーションで井戸をつぶしてしまえば人がどんどん死んで行く!

そしてこの破滅的な状況の中でなぜか高まって行く技術力。

しかし、人が死なないように病院を作ったり、ゴミが溜まって公害が発生すると慌ててリサイクルセンターを作ったりしてたんだけど、そうやって快適な環境を整えたのが間違いだったのだろうか。もっと劣悪な環境にして一方的に増え続けないようにするべきだったのだろうか。ほどほどに汚染したりして。土地の量は有限なのだから、増殖率が1を超えたままではいつか溢れ出す。出生率が下がらないのならば死亡率を上げてバランスを取らないといけない。

現実をモデルにしたゲームって時々すごく深刻な問題を提起するよな。



20万人ほど虐殺した(なお家に住んでいる人口が5万人)
まだまだ全然足りない。しかしこの方法での虐殺はコストがかかる。もっとサステイナブルな虐殺が必要だ。

というわけで一番汚染のひどい地域に高密度住宅街を作ってみた。

うーん。いまいち効果が見えない。いまいちだったのでリサイクルセンターにつながっている電線を切断してみた。


放置してたら公害で人類が滅びそうになっていた。

やばいな、これは。人口が減りすぎて、リサイクルセンターを稼働させられる労働力がない。コスモクリーナーが必要だ。

消防を維持できる労働力がなくなったため2ヶ所で甚大な火災が起きている。

人類は復活し始めた!

しかし新たな火災が発生した!

っていうかもう飽きたからこのまま燃えちゃってもいいかもなー(ぇ

記念撮影

でも人類意外としぶとくて3万人まで人口が戻って、上流社会風上の土地では消防も復活している。経済的にも黒字に戻ったなぁ。ふうむ。浄化装置と食料の供給の十分ある風上の土地と、汚染物質が流れてきて時々食料の供給が途絶えるような下流の土地を作るか。そうすると人口が増えすぎたときに下流の環境が悪化して人口が調節されつつ、技術レベルの維持のために必要な機能は上流がになう、と。なんていうディストピアSFだ。しかし人口が増えて困る以上、誰かが死ななきゃいけない。

もう18時半ですよ

な、なんだってー

urls.pyだけ書いた。URLとビューのマッピングが取れているんだからドキュメントはビューのdocstringに置いて自動でAPIリファレンスを出力とかできるよなぁ、なんて思ったけど中途半端なフレームワーク作り始めるといつまでたっても完成品ができないので我慢する。

# -*- coding: utf-8 -*-
from django.conf.urls.defaults import *
import views
urlpatterns = patterns(
    'saichugen.views',
    (r'^$', 'index'),
    (r'^rule_ja.html$', 'rule_ja'),

### API

    (r'start_game/', 'start_game'),
# POST ゲームを開始する
# return game_id

    (r'join/(?P<game_id>.*)/', 'join'),
# POST ゲームに着席する
# return seat_id

    (r'add_player/(?P<game_id>.*)/(?P<player_id>.*)/', 'add_player'),
# POST Passiveなプレイヤーを追加する
# return seat_id(? FIXME)

    (r'seat/(?P<seat_id>.*)/', 'seat'),
# GET 盤面状況の取得
# return what? FIXME

    (r'play/(?P<seat_id>.*)/', 'play'),
# POST 手を出す
# @arg hand=0..52 
# @return 0: 問題なく終了, 1: 無効な手だったのでランダムに選択した, 2: 手を出せる状態ではない

    (r'game/(?P<game_id>.*)/', 'game'),
# GET 非プレイヤー用観戦画面。ゲームが終わるまでは手札も見えない。
# ゲーム終了後は観戦に適した表示にする(htmlの場合)

)



出かけてお酒を飲んで帰ってきた。

さっき書いたこれ、こうすればいいと思った。
# urls.py (前略)

api_urlpatterns = (
    (r'start_game/', 'start_game'),
    (r'join/(?P<game_id>.*)/', 'join'),
    (r'add_player/(?P<game_id>.*)/(?P<player_id>.*)/', 'add_player'),
    (r'seat/(?P<seat_id>.*)/', 'seat'),
    (r'play/(?P<seat_id>.*)/', 'play'),
    (r'game/(?P<game_id>.*)/', 'game'),
)

urlpatterns += patterns(
    'saichugen.api_views',
    *api_urlpatterns
)

# views.py 一部抜粋

def api_doc(request):
    import urls, api_views
    api_list = []
    for (url, view_name) in urls.api_urlpatterns:
        view = getattr(api_views, view_name)
        api_list.append(
            (url, view.__doc__))
                
    return render_to_response(
        request, 
        'saichugen/api_doc.html',
        dict(api_list=api_list))

レンダリング結果

単にリファレンスに入れる対象だけ後で再利用できるようにリストをわけておいて、docstringを読んで表示するビューを追加しただけ。

def start_game(request):
    """
    POST ゲームを開始する
    @return game_id
    """
(以下略)