milk_spoonのブログ

さじの情報科学的活動、考えたこと、その他を雑記するためのブログです。

wxPythonで画像が切れたり余分に表示されてしまうときの対処

PythonGUI書きたいよね、と思ってwxPythonやってます。
QtはC++で書いたことあるし、PyQtのほうが良いんじゃ…と思いつつ。
正直書きやすく分かりやすくはないので人にオススメするかと思うとうーん
定数でレイアウト指定とかして、Pythonっぽくない…

でもまあ頑張ればそれなりに書けるので簡単なツールを手元で作ってます。
画像ビューア+αみたいな感じだけど、一つ詰まったことが…

下のようなサイズ違いの2つの画像を切り替えてGUIに表示すると
f:id:milk_spoon:20180108221240p:plain
f:id:milk_spoon:20180108221244p:plain

こんな風にめちゃくちゃな表示に(´;ω;`)
f:id:milk_spoon:20180108224707p:plain
f:id:milk_spoon:20180108224713p:plain

原因は、画像データを更新した後に画面のRefreshメソッドを呼んでいなかったことでした。
実際使うものを作ったときには画像が欠けてたりもして結構解決に時間をとりましたが、分かってみるとそりゃそうか、という感じです…
抜き出しでわかりづらいですがGUI上の画像変更のコードを以下のようにするとうまくいきます。

    #wx.Frameを継承したメイン画面クラスのメンバ
    def UpdateDisplay(self):
        #画像ファイルパスを指定して読み込み
        img = wx.Image(self.imagePaths[self.index])
        #StaticBitmapコントロールにセットする
        self.image.SetBitmap(img.ConvertToBitmap())
        #画面を更新する←これが抜けていた
        self.Refresh()

実際作ってるものはメインのwx.Frame継承クラスの子にさらにwx.Panelが何重かネストしてその中に画像があったりするんですが、一番上のself.Refreshだけでちゃんとなるので
あまりコントロール間の事は考えずself.Refreshが楽そうです
(wx.Window継承クラスならRefresh呼べるので一部Panelだけ更新ももちろんできるはず)

wxPythonで画像が欠けたり変な表示になってたらself.Refresh()!ということで
同じところで詰まる人がいませんよーにと祈ります。

OpenCV+Siv3Dで超簡単な画像補正ソフトを作った

spoonblog.hatenablog.com

これでOpenCVとSiv3D一緒に使えるよ!といったものの実際の使用例がなくては話にならないので
ちょうど自分で試したかったことも合わせて、デモ的ではあるけど2つほど作りました

ソース↓
https://github.com/saji-spoon/OpenCV-Siv3DSamplegithub.com

動くやつ↓
https://github.com/saji-spoon/OpenCV-Siv3DSample/releases/tag/1.00

以下、問題設定とかSiv3Dと合わせた表示結果とかについて説明するのでOpenCVの画像処理そのものの深いところの説明はあまりしません。
輪郭検出とか個別のことは気が向いたら書くかもしれないけど詳しくはソースで(

1. アンケート用紙の枠検出

(Form-Siv3D(August2016v2)プロジェクト)

こんな感じ↓のアンケート用紙からOpenCVで入力枠を検出したい
f:id:milk_spoon:20171208012038j:plain

個人的にはこの記事↓めっちゃ参考になった…
画像から四角いものを検出する、ってときにだいたい必要なものが解説されてる気がする
dev.classmethod.jp

画像から四角いものを検出するには結構色々手順がいるけども、とりあえず「輪郭検出」→「ゴミ取り」(一定以下の面積の輪郭を取り除く)というところを考えると、「一定以下」ってどれくらいの数値ならいいのかがよくわからない。

ということで、スライダーで値を調整しながら、指定された値より面積が大きい輪郭しか表示しないようにSiv3Dで表示させてみた。
f:id:milk_spoon:20171208012536p:plain
デフォルトだとこの状態で、アンケート用紙に書かれた文字まで輪郭検出してしまっている。

f:id:milk_spoon:20171208012608p:plain
スライダーを少し右に動かすと、文字から検出した輪郭は表示されなくなる。5000を超えたあたりで一番小さい入力欄も表示されなくなってしまうこともわかる。

ゴミ取っただけだとタイトルとかも入ってしまっているけど、ここから切り出したい画像だけクリックで指定…とかも割と簡単に発展できそう。

2. アンケート用紙のスキュー補正

(SkewCorrect-Siv3D(August2016v2)プロジェクト)

またアンケート用紙。
白紙のと、記入済みだけど曲がってしまっているのがある。
スキャナで取るとき下手くそだと曲がりますね。

f:id:milk_spoon:20171208013037j:plainf:id:milk_spoon:20171208013040j:plain

ただ、アンケート用紙には一番外側にふっとい枠線がある。これを検出して曲がっているのを補正できる…はず。
スキャンしたときとか曲がってしまったり歪んでしまったりして結果画像がズレてるのをスキューと言います。これを直すのでスキュー補正。
f:id:milk_spoon:20171208013449p:plain
これが白紙画像。これの補正枠を基準として、これに合わせて直す。

f:id:milk_spoon:20171208013458p:plain
曲がってしまってる記入済みの画像。でも補正枠は検出できてるので、これを白紙画像の補正枠と合わせるように変形すればいい。

f:id:milk_spoon:20171208013455p:plain
できました。

輪郭検出と直線近似で補正枠の四角形はとれた上で、
cv::getPerspectiveTransform
cv::warpPerspective
の2つの関数を使うと、一方を基準として、もう一方をそこにあわせる、みたいな画像の変形ができる(ふんわり)。
正確にはcv::getPerspectiveTransformで、4点から4点への変換行列を作り、cv::warpPerspectiveで作った行列を用いて実際に画像変形を行う。
まあでも、今回は単純に歪んでる方の補正枠を正しい補正枠に合わせるように変形しているだけといえばそれだけ。

Siv3D単体でも結構な加工とか画像処理(輪郭検出すら)できるので
OpenCVだけでできるのはなんじゃろなーと考えてたけど、自分の知ってる範囲ではcv::warpPerspectiveかな…と。
予想以上にきれいに補正できて(画像がもともときれいなのはあるけど)びっくり。
真っ直ぐにした画像を予め用意してるとかそういうわけではないぞ!

まとめ

やっぱり値を調整しつつリアルタイムで見れるのとか、座標クリックとかで操作しつつ次の処理を決めたりとかするにはSiv3Dの機能が強力。
簡単な操作で大量の画像の切り出しとか加工を自動化できると色々役に立つかも。
特に最近機械学習がブームで大真面目に大手がソースコードの画像を機械学習にぶっこんだりしているので、やっぱり画像処理できると強いと思います(雑)

OpenCV←→Siv3Dで画像のデータ変換をする

これはSiv3D Advent Calendar 2017 8日目の記事です。

最近OpenCVC++)で色々と画像処理をしてるけども、デフォルトのウィンドウとかキー入力受付が結構使うのが辛く、せっかくなので表示とか操作インターフェースを使い慣れたSiv3Dでやりたい。
Siv3Dの画像クラスであるs3d::Imageと、OpenCVの画像クラスであるcv::Matの変換ができれば、OpenCVで加工した画像をSiv3Dで表示する、ってことが普通にできそう。
やっぱり先駆者さんもいるもので、このコードを参考にちょいちょいしてみたらできた。

Siv3D+OpenCV環境

ちょろっと環境構築の話もしておく。
まず当たり前だけどVisual Studio 2015とSiv3D とOpenCV(3.0.0)のインストールは済ませておく。
作るプロジェクトはSiv3Dプロジェクト。
そのプロジェクトに、OpenCV3.0.0のincludeディレクトリを「追加のインクルードディレクトリ」として設定。終わり。
f:id:milk_spoon:20171207205940p:plain
追加のライブラリディレクトリなどの設定はいらず、Siv3D側で導入済みのOpenCVのライブラリを参照して動く。
簡単だけどcv::imreadやcv::imshowが通常通り動かない点など、通常の導入とちょっと違う点も出てくる。
例えば、cv::imreadはbmpしか読み込まないので、基本的に画像読み込みはs3d::Imageから行ったほうがいいと思う。
他、cv::imshow等ウィンドウ系関数が使えない。そもそもインターフェースをSiv3Dにするという話なので使わないのだけど…

コンパイルした時、未解決の外部シンボル "public: void __cdecl cv::Mat::copyTo~とかって怒られる場合は、OpenCV3.0.0以外のバージョンのincludeディレクトリが指定されているかも。
2017年12月現在の最新はOpenCV3.3.1なので、インストールもディレクトリの指定も間違わないように注意。

画像の変換

ということでcv::Mat←→s3d::Image。
以下のコードは
1. s3d::Imageでファイル読込→cv::Matに変換して加工→s3d::Imageを出力
2. cv::imreadでファイル読込→s3d::Imageに変換して加工→s3d::Imageを出力
をする。最後のループで両方の結果を表示。

下記コードを動かす前に、Siv3DデフォルトでExampleディレクトリに入ってる「Windmill.png」をbmpに変換した「Windmill.bmp」を同じディレクトリに置いておいてください。
ペイントとかで開いたのを保存しなおすとかでOKです。

#define NO_S3D_USING

# include <opencv2/opencv.hpp>// OpenCV3.0.0
# include <Siv3D.hpp>//Siv3D 2016Augustv2

void Main()
{
        //1. s3d::Image to cv::Mat
        s3d::Image image1(L"Example/Windmill.png");

        //s3d::Image内の画素データを参照するcv::Matを作成
        cv::Mat_<cv::Vec4b> mat1(image1.height, image1.width, static_cast<cv::Vec4b*>(image1.data()), image1.stride);

        //OpenCVの関数で加工 今回はネガポジ反転
        mat1 = ~mat1;

        for (auto it = mat1.begin(); it != mat1.end(); ++it)
        {
                //透明度も反転で0になってしまっているので255に
                (*it)[3] = 255;
        }

        //表示用        
        s3d::Texture texture1(image1);

        //2. cv::Mat to s3d::Image

        //cv::imreadはbmpしか読み込めない
        cv::Mat mat2 = cv::imread("Example/Windmill.bmp", cv::IMREAD_UNCHANGED);

        //channelが4かつ画素はRGBAの並びでないといけないので変換
        //(imreadのデフォルトはBGR)
        cv::cvtColor(mat2, mat2, cv::COLOR_BGR2RGBA);

        s3d::Image image2(mat2.cols, mat2.rows);

        //cv::Mat内の画素データをs3d::Image内へコピー
        std::memcpy(image2.data(), mat2.data, 4 * mat2.cols * mat2.rows);

        //加工
        image2.scale(0.3);

        //表示用
        s3d::Texture texture2(image2);

        while (s3d::System::Update())
        {
                texture1.draw();
                texture2.draw(texture1.width, 0);

        }
}

f:id:milk_spoon:20171112144050p:plain

1.でcv::Matを加工した後、s3d::Imageへの変換処理をしなくていいの?ってなるけど、今回はなくて大丈夫。
s3d::Image→cv::Matは「変換」と書いてるけど、s3d::Imageが持ってるデータをcv::Matが指すようになるだけなので。
当然、cv::Matを更新すると、s3d::Imageも更新されている。

初めに読み込んだイメージから別のcv::Matをcloneなどで作った場合は、cv::Mat→s3d::Imageの処理を挟む必要がある。

cv::Mat→s3d::Imageする際は、cv::Mat側がチャンネル数4で、画素がRGBAの並びでないといけない。
上記のようにmemcpyを使うと、cv::Matがメモリ上で連続データでないといけない。(ROIなど、連続データでないこともある。)
例では適当に変換しているが、本来変換元のcv::Matにどのようなものが来るかは色々あると思っていて、

  • s3d::Imageから変換したもの(チャンネル数4, RGBA)
  • cv::imreadでファイル読込したもの(チャンネル数2 or 3 or 4, グレイスケール or BGR)
  • 他のcv::Matを加工したもの(チャンネル数等不明、不連続データの可能性あり)

これらを適宜RGBAに変換などする必要がある。

変換関数

ここまで来て結構めんどくさいなーと思いました。cv::Mat→s3d::Imageするのに↑のような条件気にしたりとかはやってられない感。
なのでわりと思考停止で変換できるんじゃないかなーという変換関数たちを作った↓

github.com

↑の条件を気にしないでcv::Mat→s3d::Imageに変換したいって場合はcvsiv::MatToImageForceを呼んで渡せばだいたい変換できる。
(cv::MatがRGBかBGRかは判定できないので指定する必要アリ)
↑の条件に合っていることがわかっていればcvsiv::MatToImageを呼んだほうがコピーが少ないので良い。
cvsiv::IsConvertibleByMatToImage/IsConvertibleByMatToImageForceでそれぞれ適用可能かも判定できる。

上の例のようにs3d::Imageを参照するcv::Matを作りたいときはcvsiv::GetMatLinkedToImage。
むしろデータ共有してるとか嫌だからコピーしてcv::Mat作りたいときはcvsiv::ImageToMat。
ただ画像データは基本コピーしないほうがパフォーマンスいいと思うので…

まとめ

cv::Matの表現できる画像データはs3d::Imageより多様なので、任意のcv::Matをs3d::Imageへ変換しようとするのは意外と色々あって大変だけどもう変換関数があるので解決(?)
次はこれを使ってOpenCVで処理した画像をSiv3Dで見てみるようなものを作ってみたい。
→作りました
spoonblog.hatenablog.com


Siv3D Advent Calendar 2017 8日目でした。
次はhota1024さんです。

jupyter-themesでダーク系のテーマでもgrade3みたいな感じにする

Windows上でJupyter Notebookを使い始めた。
早速Vim-bindingにしてみたり、jupyter-themesで画面をダーク系にしてみたりと楽しんでいる(?)

ところが、このjupyter-themesのテーマ、不満な点が一つだけあった。
以下のコマンドでダーク系のmonokaiテーマへ切り替える。
ちなみに-vimVim-bindingを入れてる人用のオプション、-N -Tでファイル名やツールバー表示。

> jt -f inconsolata -t monokai -vim -N -T

結果がこの通り。

f:id:milk_spoon:20170820182559p:plain

うーむ。

デフォルトのgrade3(↓)みたいに、コードセルだけ違う色にしてあとは背景に沈ませてほしい…

f:id:milk_spoon:20170820183038p:plain

今のままだとセルが全部光っていてどこを見ればいいかわかり辛い。

コレを解決するには、テーマ切り替え時のオプションに-altmdを追加してやればよい。

> jt -f inconsolata -t monokai -vim -N -T -altmd

適用するとこんな感じ。

f:id:milk_spoon:20170820183517p:plain

まだ出力セルが少し明るいが、これはスタイルファイルの編集でなんとかなる。
スタイルファイル(.less)が
(Anacondaインストールディレクトリ)\Anaconda3\Lib\site-packages\jupyterthemes\styles\
にあるので編集する。
出力セルの背景色は@cc-output-bgの値を変更すればOK。
スタイルファイルを増やせばちゃんとjtコマンドで設定できるので自作テーマももちろん作れる。

以上。
grade3がデフォルトで背景にテキストやmdが沈むようになっているのはstylefx.pyでgrade3だけ特別処理されてるとか、-altmdオプションは公式readmeでも説明がないとか色々悩んだけどなんとかなった…

作業メモ:msys2インストール、エクスプローラーから起動

動機:git (for Windows)を使うときに、cmdからはいいかげん操作し辛いと感じた。Git bash でOK、と気づいたときにはすでにmsys2を入れていたので、将来的に色々使うことを考えて自然に使えるようになるまでセットアップ。

基本は以下を参考に、64bit版をインストール、起動、パッケージシステムpacmanのupdateなどを行った。

MSYS2で快適なターミナル生活 - Qiita

ただし2017年3月現在上記の記述と変わっているところも少しある

mingw64_shell.bat, mingw32_shell.batは廃止

minGWへのPATHが通った状態でmsys2を起動する用の個別のバッチファイル廃止、代わりにmsys2_shell.cmdにオプション-mingw32または-mingw64を使うことになった。
これらのbat, cmdの起動ファイルはパッケージfilesystemで提供されている(いた)。

> msys2_shell.cmd -mingw32
> msys2_shell.cmd -mingw64
pacmanの更新はupdate-core不要

MSYS2における正しいパッケージの更新方法 - Qiita
更新がなくなるまで以下のコマンドでupdate

$ pacman -Syuu

エクスプローラーのアドレスバーからmsys2を起動

普段はエクスプローラーでファイルを見ながら必要なときにアドレスバーからcmdを起動して使う。cdが面倒くさいのかもしれない…
そんなノリでmsys2を素早く起動したい。
要件を以下の3つとして、実現する方法を見つけた。

やり方

1. msys2のインストールフォルダにPATHを通す
2.インストールフォルダのmsys2.iniを開くと以下のようになっているので

#MSYS=winsymlinks:nativestrict
#MSYS=error_start:mingw64/bin/qtcreator.exe|-debug|<process-id>
#CHERE_INVOKING=1
#MSYS2_PATH_TYPE=inherit
MSYSTEM=MSYS

CHERE_INVOKING=1, MSYS2_PATH_TYPE=inheritの行のコメントアウトを外す

#MSYS=winsymlinks:nativestrict
#MSYS=error_start:mingw64/bin/qtcreator.exe|-debug|<process-id>
CHERE_INVOKING=1
MSYS2_PATH_TYPE=inherit
MSYSTEM=MSYS

3.エクスプローラーでアドレスバーに"msys2"と入力すると、要件通りの動作でmsys2が起動する

説明

msys2のランチャーはmsys2_shell.cmd以外にも複数の形式で提供されている。
Launchers · msys2/msys2 Wiki · GitHub
右クリックから開くためのレジストリ設定等もある。
この中で、msys2-launcherパッケージで提供されているexe形式を使用する。
ちなみに特にpacmanで改めてインストールしなくてもmsys2インストール完了時点で既に入っていた。

このmsys2-launcherパッケージのランチャーexeは起動するときに設定として.iniファイルを読み込む。
msys2.exeが読み込むmsys2.ini内のオプションの中で、カレントディレクトリを引き継ぐCHERE_INVOKING=1、PATHを引き継ぐMSYS2_PATH_TYPE=inheritを有効にすると、msys2が要件を満たすように起動する。
エクスプローラーのアドレスバーへの入力については、正確な仕様は知らないが概ねcmd上でのコマンド実行と同様なので、msys2.exe(インストールディレクトリ)へのPATHを通せばmsys2(.exe)の入力だけで起動できる。
今回はデフォルトのmsys2.exeを使用したが、minGWへのPATHを通すランチャーのmingw32.exe, mingw64.exeも同様。
iniファイルもそれぞれ分かれていてmingw32.ini, mingw64.iniとなっているので注意。

その他

最終的には簡単なことだったけど、msys2_shell.cmdだけしかランチャーを知らない時はショートカットを作ってPATH通して起動しようとしてたり(なんでそんな遠回りをしたのか…)
でもmsys2_shell.cmdをrenameするのはパッケージのupdateとかで整合性がとれなくなるのイヤなのでこれが一番簡単かも…?

追記(2017/08/20更新)

結局msys2からWindows版Gitを触った時にコミットメッセージを入力時にWindowsgvimがうまく起動しないとか、細かいところで整合性がとれなさそうなのでMSYS2_PATH_TYPE=inheritはやめてしまった。
それ以外は概ね快適。

Cookie Clickerの今を知るためにプレイしてみた&ブックマークレットでGolden Cookie通知

現状報告

f:id:milk_spoon:20151017130858p:plain

この通り。

少し前、すごいブームが巻き起こったブラウザゲー"Cookie Clicker"。
今では「とっくに日本ではブームが終わったことを作者はつゆ知らず、今でも『日本の人喜んでくれるかな~』とアップデートが続けられている…」というウワサがまことしやかに伝えられるのみとなっています。
しかし、何故か最近また再燃してきた人がチラチラいるなーという様子でしたので、そのウワサのアップグレードを体感するためにも再度プレイしてみました。
ちなみに以前のブーム時もプレイしていて、Antimatter Condenser買ったぐらいでなんとなく満足してやめてたような気がします。

で、一通りやって気付いたんですが色々ダンジョンとか追加されてるのってbeta版みたいですね…
ずっと正式版ver.1.0466をやってました!なので以下はその内容となります。
それでもブーム時より季節イベント(通常プレイでも切り替え化)、新施設とかの追加もあるので結構変わってるところがあります!
で、そのあたりを紹介しようかなーと思ったんですが、自分でプレイした時の楽しみにしたい方がいると思うので最後にしておきます。

それより私のような久しぶりプレイヤー向けにちょっと問題が。
Wikiにも掲載されているプレイ補助ブックマークレットであるCookie Monsterですが、現行バージョンでは動かない…みたいです。
Stats内で表示されるLuckyで最大数を獲得するためのクッキー数やHeavenly Chips数といった統計量は見れますが、GUIに干渉するものやGolden Cookieに関係するものは非常に不調です。新バージョンで既存機能のナカミも色々変わったみたいですね。
特にGolden Cookieが出現したら音が鳴る機能を使いたかったのでとても困りました。なので、自分でその機能だけささっとブックマークレットで作ったり、↑のような以前のバージョンのブックマークレットがなんで動かなくなったのか調べてみました。

Game.GoldenCookie.delayはもういない!

Golden Cookie関連のチートを調べるとよく出てくるのがGame.GoldenCookie.spawn()です。
これを呼ぶとすぐさまGolden Cookieが現れます…が、チートをしたことを示す隠し実績がついてしまいます。
次によく見るのがGame.GoldenCookie.delay。これを0にすると、Golden Cookieが出た直後に次のGolden Cookieがすぐさま出現するそう…です。
なんか煮え切らない書き方なのは、実はこの変数もうないみたいだからです。

f:id:milk_spoon:20151017132816p:plain

あら。

多分昔はこのdelayで出現頻度を管理していて、Cookie Monsterでもこれを利用して次のGolden Cookieまでの時間とか出してたんでしょうが、色々変わっちゃったみたいですね。
変わりに色々探してて見つけたのがGame.GoldenCookie.time/minTime/maxTimeです。

f:id:milk_spoon:20151017133321p:plain

観察していたところ、timeは毎フレームごとに増加し、minTime<time<maxTimeの時に確率でGolden Cookieが出現するようになってるみたいです。Golden Cookieが出現するとtimeは0になります。
delayを書き換えられて即時スポーンするチートの対策ですかね…?
ちなみにminTime・maxTimeは固定値で、最初minTime=9000, maxTime=27000です。
Golden Cookieの出現間隔が半分・出現時間が倍になるUpgradeがあるんですが、これを買うと上記の値が両方半分になります。
↑はそれを2回買った状態ですね。

Golden Cookieお知らせブックマークレット GoldenCookieBeacon

このように色々変わっていますが、別に私はチートをしたいわけではなく、ただちょっとGolden Cookie出るのがいつかなーって待ってる時間を進捗に当てたい、そういう考えなわけです。
「Golden Cookieが出たら音で通知」機能が理想なわけですが、当該機能を実装したCookie Monsterが不調…
ということで自分で適当にブックマークレットを書きました。

javascript:(function (){  var soundURI = "音楽ファイルのURL";var audio = new Audio(soundURI); audio.volume = 0.5; console.log("GoldenCookieBeacon was staeted."); teiki = function(){if(Game.goldenCookie.life != 0){audio.play();console.log("Golden Cookie!");}}; setInterval("teiki()",3000);})()

Golden Cookie Beacon

これをブックマークレットの内容として保存すればオッケー…と思ったんですが
なんか音楽ファイルをどうやって用意しようかなあってところが壁になったんですよね…
素材サイトやいろんな借りてるところからの直リンは多分めっちゃ怒られると思うんですが
このためにサーバー用意するの…????ってなったので
音のファイルは各自ご用意ください()
音楽ファイルのURLってとこを置き換えていただければと思います。

ちなみに自分はDropboxのpublicフォルダにアップロードして共有リンクを作成すると直リンクになる、っていうのを使ってます

f:id:milk_spoon:20151017135618p:plain

デフォルトであるPublicってフォルダに音ファイルを投げて…

f:id:milk_spoon:20151216095554p:plain

右クリック→「公開リンクをコピー」で出てくるURLを使います。
公式で提供されてるものだし自分用なら大丈夫だと思う!(

ローカルからファイルを読み込む方法、あるのかもしれないけどわかりません!
誰か教えて…_(:3」∠)_


・使い方
上のブックマークレットを、CookieClickerをプレイしているページで起動すると開発コンソールで"GoldenCookieBeacon was staeted."というメッセージが表示されます。
これが確認できれば正常に起動できています。あとはGolden Cookie出現時に音と"Golden Cookie!"のメッセージでお知らせしてくれます。
音が出てないけどGolden Cookie!のメッセージだけ出ている場合は音楽ファイルを指定していないかもしれません。
まあ使い方を書くほどのものでもないですね…

一応、使い方が全然わからない人向けに記事書こうかなと思います。

Golden Cookieが出ない時

ちょっとだけ↑の内容に関連するんですが
私は久しぶりにプレイして、「待てど暮らせどGolden Cookieが出ない」というヤバめの現象に悩まされました。
端的に言いますと、これの対処は「再読み込みしてください」です。
至って普通なんですが、久しぶりにプレイするということで最初にアクセスしてぶっ続けでやる人もいると思うので、結構引っかかるんじゃないかなあと思います。

これの原因は前述したminTime, maxTimeがゲーム開始時にはともに0に設定されているから、のようです。
以下はテストとして新規に始めたゲームの様子です。

f:id:milk_spoon:20151017141229p:plain

で、これらの値がゲーム進行でちゃんと変わるならいいんですが、どうも再読込するまで変わっている気配がない…
ファームを少し買い始めたあたりで再読み込みするとminTime, maxTimeが初期値になりました。

f:id:milk_spoon:20151017141437p:plain

その後、ちゃんとGolden Cookieも出現しました。

f:id:milk_spoon:20151017141535p:plain

Golden Cookie間隔短縮のUpgradeを購入した時も少しminTime・maxTimeの値を見てみましたが、少なくともコンソールで確認した限りでは再読み込みしないと変わらないみたいでした。
Golden Cookieの登場でおかしいことがあったらMenuからsaveの上で再読み込みが安定と思われます…。

最後に:ブーム時から何が変わったのか

ver.1.0466で何が前から変わっているのか、さっくり紹介します
普通にプレイしてて大きく変わったなーと思ったのは、まず新規Buildingとして"Prism"が追加されたことですね。

f:id:milk_spoon:20151017144435p:plain

光をクッキーに変換するそうです。まあ既プレイ者ならもう何があっても驚きませんよね。時間遡行よりは常識的な気さえします。
ちなみに新施設といえば新おばあちゃん。レインボー。

f:id:milk_spoon:20151017144459p:plain

あと、Grammapocalyps、通称ババアポカリプスはブーム時でも話題になっていたかと思いますが、この時に虫(Wrinkler)が出てくるようになりました。
虫がクッキーに取り付くと数に応じてCpSが下がりますが、実は虫がクッキーを体内に貯めこんでおり、クリック連打で潰した時に貯めこんだ分の1.1倍を還元してくれます!
なので実質CpSは上がっており、さらに虫がクッキーを貯めこむ速度は同時にいる虫の数で増えるみたいです。10匹が最大ですが、10匹いる状態での実質CpSはもとの6倍くらいだというので驚きです…!

f:id:milk_spoon:20151017145402p:plain

クッキーをクリックするでもなく、Golden Cookieを待つだけでなく、虫を飼うというクッキーの新たな増やし方が提供されなかなか楽しいです♪

あとはミルクの色が実績数で変わったり…
そういえば前述したGolden Cookieの出現間隔を短くするUpgradeって、ブーム時にありましたっけ…?
あんまりブーム時に深くやっていたわけではないので覚えていないんですが、なかなかお得感があっていいUpgradeだと思います。
出現条件がGolden Cookieクリック枚数なので、最初は自分で頑張って見つけてクリックする必要がありますが頑張って取得する価値があるとおもいます。

f:id:milk_spoon:20151017145855p:plain

あとは季節イベントがあるんですが…すいませんまだ出せてません;
できるようになったらレポートしたいと思います!
こんな感じで色々変わったCookie Clicker、是非もう一度プレイしてみてはいかがでしょうか…!

cannot import name …

pythonでdpktというパケット解析ライブラリを利用しようとして、pipでinstallもした、とりあえずテスト用ソースをコピペった、さあ動かすぞという段になってこのザマでした。
ちなみにバージョンはpython2.7.6 + dpkt1.8.6.2です。

$ python test.py 
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    import dpkt
  File "/usr/local/lib/python2.7/dist-packages/dpkt/__init__.py", line 12, in <module>
    import aoe
  File "/usr/local/lib/python2.7/dist-packages/dpkt/aoe.py", line 6, in <module>
    from decorators import deprecated
  File "/usr/local/lib/python2.7/dist-packages/dpkt/decorators.py", line 4, in <module>
    from test import pystone
ImportError: cannot import name pystone

うわ。

pythonのバージョンが悪いのか、パッケージが実はうまくインストールできてないのか、とか色々考えましたが、testパッケージおよびpystoneはpython2.7ではインストール時にデフォルトであるという話もあり、しばらく原因が特定できず困っていました。

結論を言うと、以下のようにソース名を"test.py"から変更したところ動作しました。

$ mv test.py ttt.py
$ rm test.pyc 
$ python ttt.py 
(1, u'. time: ', 1303496629.238845, u'Length:', 60)
(2, u'. time: ', 1303496629.609845, u'Length:', 58)
...

一度実行してしまっている場合は.pycファイルも削除しなければいけないようです。

どうしてファイル名なんかで実行の成否が変わったのか…
これはpythonのimportの記法と優先関係にあるみたいです。kannokanno.hatenablog.com
最初のエラーメッセージでは結局何がエラーとなったかというと、

    from test import pystone
ImportError: cannot import name pystone

testパッケージのpystoneモジュールを読み込もうとしたけどそんなものはない!という点ですね。
そしてインポート対象ファイルの優先順位が

  1. 実行中のファイルと同じフォルダ
  2. カレントフォルダ
  3. 環境変数「PYTHONPATH」に列挙したフォルダ
  4. sys.pathに登録してあるフォルダ

…ありますね、カレントフォルダに"test"という名前のソースが…

つまり、本来4番目の「sys.pathに登録してあるフォルダ」の中の"test"(ディレクトリ)を探索してほしいところなのに、優先順位2番目のカレントディレクトリの"test"(.py)に先に引っかかってしまい、当然その"test"(.py)下にpystoneが存在するはずもなくエラーです!、という…

相当時間かかったので、原因がわかった時はかなり脱力しちゃいました。
こんなので動かなくなっちゃうのは正直やめてほしい。と思ったり思わなかったり。
しかも名前の衝突なので、pystoneやtestパッケージなど、限定的なキーワードで原因を調べても同様の事例がなくて焦りました。
こうして書いておくことで未来の自分や後に続く人が同じ轍を踏んで時間を浪費することのないよう祈るばかりです。