結果だけでなく過程も見てください

たい焼きさんの日々の奮闘を綴る日記です。

AutoIt+OpenCVでデスクトップから任意の画像を曖昧検索(テンプレートマッチング)する

AutoItで画像の曖昧検索がしたい

UWSCにはchkimgという画像検索関数が標準で用意されていました。また有志の方がchkimgxという画像の曖昧検索を行う外部ライブラリを作成されていたようですが、アップローダーなどでのみ配布されていたようで現在では入手が難しくなっています。

一方、AutoItには画像検索をする標準関数が存在せず、searchImageという外部ライブラリを使う必要があるようです。
しかしこのsearchImage、2008年頃までしかメンテされていないようで、自分の環境でもいまいちちゃんと動いてくれず、単にdllの配置場所を間違えてたかもしれない件は置いといて、まぁ自分で画像マッチング出来た方が曖昧さ加減を調整出来たり、判定方法を自由にカスタマイズ出来たり、今後のことを考えると自作した方がいいかなぁなんて思いまして、OpenCVを触ってみることにしました。

こちらの環境です。

OS Windows 10
コンパイラ Visual Studio Community 2017
CMake 3.12.1(64-bit版)
OpenCV, OpenCV contrib 3.4.3
AutoIt v3.3.14.5

Visual Studio Community 2017のインストール

なにはともあれ、コンパイラをインストールします。

ダウンロード | IDE、Code、Team Foundation Server | Visual Studio

CMakeのインストール

OpenCVのビルドには、CMakeも必要です。インストールします。
ご自分の環境合わせてインストールしてください。自分の場合は「cmake-3.12.2-win64-x64.msi」でした。
https://cmake.org/download/

CMakeインストール中に「Add CMake to the system PATH for all users」を選択して、環境変数PathにCMakeのパスを追加してください。

↑を忘れてしまった場合は、手動で環境変数Pathに以下のCMakeのパスを追加してください。

C:\Program Files\CMake\bin

OpenCV, OpenCV contribのビルド

まずは以下からソースコードをダウンロードしましょう。

OpenCV

Releases · opencv/opencv · GitHub

opencv-3.4.3-vc14_vc15.exe」などのリンクをクリックしてダウンロードします。

OpenCV contrib

Releases · opencv/opencv_contrib · GitHub

「zip」と書かれたリンクをクリックしてダウンロードします。

上記ページのopencvのファイル名(例:opencv-3.4.3-vc14_vc15.exe)にあるvc14とかvc15とかが、
Visual Studioのバージョンに対応しているので、今後バージョンが上がった場合は注意して確認してみてください。

vc14 Visual Studio 2015
vc15 Visual Studio 2017

OpenCVのビルド

ここでは例として、「C:\temp」に上記ファイルをダウンロードします。

opencv-3.4.3-vc14_vc15.exe」をダブルクリックすると「opencv」というフォルダ名で展開されます。
opencv_contrib-3.4.3.zip」をダブルクリックすると「opencv_contrib-3.4.3」というフォルダ名で展開されます。

[スタート]メニューからcmake-guiを探して起動します。
[Where is the source code]に「C:/temp/opencv/sources」を指定します。
[Where to build the binaries]に「C:/temp/opencv/build」を指定します。
手動でパスを入力する場合、パス区切り文字は「/」にしてください
入力したら[Configure]ボタンを押下します。
f:id:taiyakisun:20180912000136p:plain

今回は、AutoItをデフォルト32-bitで動作させているので、OpenCVも32-bitでビルドします
そのため今回は「Win64が付いていない」プロジェクトを選択します。
f:id:taiyakisun:20180912000140p:plain

再びCMake-guiの画面に戻ります。
32-bit版の場合、cuda関連のビルドが通らないという噂がありますので、「WITH_CUDA」のチェックを外します(最近のは最初から外れてるかも)
また「OPENCV_EXTRA_MODULES_PATH」に「C:/temp/opencv_contrib-3.4.3/modules」を指定します。
何度も言いますが、パス区切り文字は「/」にしてください
f:id:taiyakisun:20180911221717p:plain
f:id:taiyakisun:20180911221720p:plain

コンソールに「Configuring done」が出力されたら成功です。
続いて「Generate」ボタンを押下します。
f:id:taiyakisun:20180911222335p:plain

C:\temp\opencv\build直下にOpenCV.slnが出来ているので、Visual Studioで開きます。
INSTALLプロジェクトをReleaseビルドしてください。

出力コンソールにこんな感じの表示が出れば成功です。

========== すべてリビルド: 134 正常終了、0 失敗、0 スキップ ==========

AutoItで使用するテンプレートマッチングを行うDLLの作成

Visual Studioで新規プロジェクトを作成します。
DLLのプロジェクトを作るか、プロジェクトの[構成プロパティ]→[全般]→[構成の種類]を「ダイナミック ライブラリ(.dll)」などにしてください。

同じプロジェクトのプロパティページの[C/C++]→[全般]→[追加のインクルードディレクトリ]に以下を入れます。

C:\temp\opencv\build\install\include

[リンカー]→[全般]→[追加のライブラリディレクトリ]に以下を入れます。

C:\temp\opencv\build\install\x86\vc15\lib
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <map>
using namespace std;

#include <Windows.h>

/**
* @brief ウィンドウハンドルhwndのイメージcv::Matを作成する。
* @param [in] hwnd	     ウィンドウハンドル。デスクトップを対象にしたい場合、GetDesktopWindow()の戻り値を渡すべし。
* @param [in] nPosLeft       検索対象となる左上のx座標
* @param [in] nPosTop        検索対象となる左上のy座標
* @param [in] nDestWidth     検索対象となる範囲のnPosLeftからの幅
* @param [in] nDestHeight    検索対象となる範囲のnPosTopからの高さ
*/
cv::Mat hwnd2mat(HWND hwnd, int nPosLeft, int nPosTop, int nDestWidth, int nDestHeight)
{
    HDC hwindowDC = GetDC(hwnd);
    HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);

    HBITMAP           hbwindow = NULL;
    cv::Mat           src;
    BITMAPINFOHEADER  bi;

    SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);

    RECT windowsize;    // get the height and width of the screen
    GetClientRect(hwnd, &windowsize);

    // ここはデスクトップの幅と高さ
    int srcheight = windowsize.bottom;
    int srcwidth  = windowsize.right;

    src.create(nDestHeight, nDestWidth, CV_8UC3);

    // create a bitmap
    hbwindow = CreateCompatibleBitmap(hwindowDC, nDestWidth, nDestHeight);
    if (hbwindow == NULL)
    {
        src.data = NULL;
        return src;
    }

    bi.biSize = sizeof(BITMAPINFOHEADER);    // http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
    bi.biWidth         = nDestWidth;
    bi.biHeight        = -nDestHeight;
    bi.biPlanes        = 1;
    bi.biBitCount      = 24;
    bi.biCompression   = BI_RGB;
    bi.biSizeImage     = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed       = 0;
    bi.biClrImportant  = 0;

    // use the previously created device context with the bitmap
    SelectObject(hwindowCompatibleDC, hbwindow);

    // copy from the window device context to the bitmap device context
    StretchBlt(hwindowCompatibleDC, 0, 0, nDestWidth, nDestHeight, hwindowDC, nPosLeft, nPosTop, nDestWidth, nDestHeight, SRCCOPY);	// change SRCCOPY to NOTSRCCOPY for wacky colors !
    GetDIBits(hwindowCompatibleDC, hbwindow, 0, nDestHeight, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS);		// copy from hwindowCompatibleDC to hbwindow

     // avoid memory leak
    if (hbwindow != NULL)
    {
        DeleteObject(hbwindow);
        hbwindow = NULL;
    }

    if (hwindowCompatibleDC != NULL)
    {
        DeleteDC(hwindowCompatibleDC);
        hwindowCompatibleDC = NULL;
    }

    if (hwindowDC != NULL)
    {
        ReleaseDC(hwnd, hwindowDC);
        hwindowDC = NULL;
    }

    return src;
}

extern "C"
{
    /**
     * @brief 教師画像が指定されたデスクトップ範囲に存在するかあいまい検索(テンプレートマッチング)する
     * @param [in]  pszFilePath    教師(テンプレート)画像のフルパス
     * @param [in]  nPosLeft       検索対象となるデスクトップの左上のx座標
     * @param [in]  nPosTop        検索対象となるデスクトップの左上のy座標
     * @param [in]  nDestWidth     検索対象となる範囲のnPosLeftからの幅
     *                             4の倍数でないと必ずマッチングしないため、4の倍数に増やす方向で調整をする。
     * @param [in]  nDestHeight    検索対象となる範囲のnPosTopからの高さ
     * @param [out] pDetectedPosX  検知した場合のX座標。検知してない場合なにも入らない。
     * @param [out] pDetectedPosY  検知した場合のY座標。検知してない場合なにも入らない。
     * @retval 0  正常終了(見つかった)
     * @retval 1  正常終了(見つからなかった)
     * @retval 10 教師画像ロード失敗
     * @retval 11 デスクトップ情報確保失敗
     * @retval 12 テンプレートマッチングでエラー
     */
    DllExport int searchImg(const char*  pszFilePath,
                            int          nPosLeft, 
                            int          nPosTop, 
                            int          nDestWidth, 
                            int          nDestHeight, 
                            int*         pDetectedPosX, 
                            int*         pDetectedPosY)
    {
        int nActualWidth = nDestWidth + (4 - (nDestWidth % 4));   // 4の倍数に増やす方向で合わせる

        cv::Mat template_image = cv::imread(pszFilePath);
        if (template_image.data == NULL)
        {
            return 10;
        }

        cv::Mat srcDesktop = hwnd2mat(GetDesktopWindow(), nPosLeft, nPosTop, nActualWidth, nDestHeight);
        if (srcDesktop.data == NULL)
        {
            return 11;
        }

        cv::Mat result;
        try
        {
            cv::matchTemplate(srcDesktop, template_image, result, cv::TM_CCORR_NORMED);
        }
        catch (cv::Exception ex)
        {
            return 12;
        }

        // Multiple results

        // 最もそれっぽい画像選ぶためのMap。同じ値の場合最初に検知したものを優先するため、insert自体しないこととする。
        // 比較をgreator<float>にすることにより降順に並ぶ。
        map<float, cv::Point, greater<float> > dectetedPointMap;

        const float threshold = 0.981f;     // TODO:ここも指定できるようにしたい

        for (int y = 0; y < result.rows; ++y)
        {
            for (int x = 0; x < result.cols; ++x)
            {
                if (result.at<float>(y, x) > threshold)
                {
                    if (dectetedPointMap.find(result.at<float>(y, x)) != dectetedPointMap.end())
                    {
                        // すでに同じ値がある場合、最初に見つかったものを採用するため、mapには追加しない
                        continue;
                    }

                    dectetedPointMap.insert(pair<float, cv::Point>(result.at<float>(y, x), cv::Point(x,y)));
                }
            }
        }

        
        if (dectetedPointMap.empty())
        {
            // 見つからなかった
            return 1;
        }
        else
        {
            // 最も似ている点(複数ある場合は最初に見つかった点)を採用する
            *pDetectedPosX = (dectetedPointMap.cbegin()->second.x + template_image.cols / 2);
            *pDetectedPosY = (dectetedPointMap.cbegin()->second.y + template_image.rows / 2);

            return 0;
        }
    }
}

とりあえず複数見つかった場合は以下のルールに従って、単一の結果を返すようにします。返すデータはマッチングした画像の中心座標です。

  • もっとも類似度の高いもの
  • 最初に見つかったもの

ビルドしてdllを作成します。
includeやlibの設定が間違っていなければビルドが通るはず(通らなかったらごめんなさい)。

AutoItからDLLを呼び出す

AutoItのインストール手順などは省略します。
適当にAutoItのファイルを作成し、以下のDLLをAutoItのファイルと同じ位置に配置します。

上記で作成した自作のDLL
opencv_ccalib342.dll
opencv_core342.dll
opencv_highgui342.dll
opencv_imgcodecs342.dll
opencv_imgproc342.dll

AutoItのコードは以下です。
まずはDLL関数のラッパー関数。

; @param [in] $hDll	   DllOpen()済のハンドル。ここファイル名を渡すこともできるが、そうすると毎回オープン/クローズ
;			           するせい(?)なのか、連続して呼び出すとアプリケーションエラーが発生するので、必ずハンドルを渡すこと。
; @param [in] $x       デスクトップ上のx座標
; @param [in] $y       デスクトップ上のy座標
; @param [in] $width   $xからの幅
; @param [in] $height  $yからの幅
; @retval -1 DllCallでエラー発生
; @retval 0  イメージが正常に判定し、見つかった。イメージの中心座標が$detectedXと$detectedYに格納される。
; @retval 1  イメージを正常に判定し、見つからなかった
; @retval 10 イメージのファイルが見つからなかった
; @retval 11 デスクトップ画面の取得で失敗
; @retval 12 マッチング処理でエラー発生
Func searchImageCore($hDll, $imgFileName, $x, $y, $width, $height, ByRef $detectedX, ByRef $detectedY)

	Dim $ret = DllCall($hDll, _
			"int:cdecl", _
			"searchImg", _
			"str", $imgFileName, _
			"int", $x, _
			"int", $y, _
			"int", $width, _
			"int", $height, _
			"int*", $detectedX, _
			"int*", $detectedY)
	If (@error <> 0) Then
		ConsoleWrite( "DllCall error!." & @LF )
		MsgBox(0, "error", "DllCall returned " & @error & "  imgFile=" & $imgFileName & @CRLF)
		Return -1
	EndIf

	; 0    -> 見つかった
	; 1    -> 見つからなかった
	; その他 -> エラー発生

	; 見つかった場合だけ、値を格納して、戻り値はコール元にそのまま返す
	If ($ret[0] = 0) Then

		$detectedX = $x
		$detectedY = $y

	EndIf

	Return $ret[0]

EndFunc   ;==>searchImageCore

次に、ラッパー関数を呼び出すAutoItコード。

Global $g_hDll = DllOpen( "<自作のDLLファイル名>.dll" )

Local detectedX = 0
Local detectedY = 0

Local $ret = searchImageCore($g_hDll, _
	                "abc.bmp", _
			0, _
			0, _
			1920, _
			1080, _
			$detectedX, _
			$detectedY)
If ($ret = 0) Then
	; 見つかった
	ConsoleWrite("searchImage detected!  dx=" & $detectedX & "  dy=" & $detectedY & @CRLF)
Else
	; 普通に見つからなかったか、なんらかのエラー発生。見つからなかったとして帰る。
	If ($ret <> 1) Then
		; 普通に見つからない場合以外はログを出しておく
		ConsoleWriteError("[Error] searchImage returned " & $ret & "  imgFile=" & $imgFile & @CRLF)
	EndIf
EndIf

思ったより長くなってしまった。あぁ疲れた…。

AutoItでDLLを呼び出す方法

つい先日からUWSCスマホゲーWindows上の作業を自動化していたのですが、
UWSCは最近公式サイトがリンク切れになったり、32-bitのバイナリしかなかったり、VMのゲストOSでの動作がうまくいかなかったりと、
将来的にいろいろ不安なので、AutoItに乗り換えられないか色々試してます。


AutoItについてはWikipediaでもみてください。
https://ja.wikipedia.org/wiki/AutoIt


こちらの環境です。

OS Windows 10
C++コンパイラ Visual Studio 2017 Community
AutoIt v3.3.14.5

AutoItは世界ではわりとメジャーだそうで、困ったことがあっても調べればすぐ解決方法が出てくるのも魅力ですね~。
今回は自作のDLL呼び出しでちょっと詰まったので、解決方法をご紹介します。

C++(DLL)のソースコード

サンプルで簡単な足し算の関数を定義します。

結果を戻り値で受け取るバージョンと、引数経由で受け取るバージョンの2つ。
これをVisualStudio等でビルド・dllを作成し、AutoItのソースコードと同じパスに配置しておいてください。

#define DllExport __declspec( dllexport )

extern "C"
{
    DllExport int Add(int a, int b)
    {
        return (a + b);
    }

    DllExport void AddRef(int a, int b, int* pResult)
    {
        *pResult = (a + b);
    }
}

AutoItのソースコード

Add関数の呼び方
Dim $result = DllCall("NinaImageSearch.dll", "int:cdecl", "Add", "int", 3, "int", 5)
If (@error <> 0) Then
	MsgBox(0, "error", "DllCall returned " & @error)
	Exit @error
Else
	MsgBox(0, "Result", "$result=" & $result[0])
EndIf
ポイント
  • 第一引数にはDLLのファイル名(またはDllOpenした結果(ハンドル))を渡す
  • 第二引数には関数の戻り値の型(※)を指定する
  • 呼び出す関数の呼び出し規約がcdeclの場合は「型:cdecl」と指定する
  • 第三引数に関数名を指定する
  • それ以降は型名(※)と値のペアを指定する
  • エラーが発生した場合はマクロ「@error」に0以外の数値が入る
  • 戻り値がintだろうと、結果は配列で返るので値を参照するときは$result[0]として参照する

ポイント多いですね!
この呼び出しを成功させるだけで、30分くらい格闘した覚えがあります…。

(※)型一覧はこのあたりをみてください。
https://open-shelf.appspot.com/AutoIt3.3.6.1j/html/functions/DllCall.htm

AddRef関数の呼び方

変数の参照(リファレンス)を渡して、そこに結果を格納する方法ですが、こいつがわかりづらい。

Dim $sum
Dim $ret = DllCall("NinaImageSearch.dll", "none:cdecl", "AddRef", "int", 3, "int", 20, "int*", $sum)
If (@error <> 0) Then
	MsgBox(0, "error", "DllCall returned " & @error)
	Exit @error
Else
	$sum = $ret[3]
	MsgBox(0, "Result", "$sum=" & $sum)
EndIf
ポイント
  • 戻り値voidの場合は「none」という識別子を使う。(第二引数)
  • 変数の参照(リファレンス)を渡すときは型の末尾に「*」を指定する(第八引数)
  • DllCallに成功しても、なんと$sumには値が入らない!
  • 結果は$retに格納されており、$retは以下のような配列構造になっている。
  • $ret[0] ←戻り値(noneの場合は空文字)
  • $ret[1] ←3 AddRef関数の第一引数に渡す値
  • $ret[2] ←20 AddRef関数の第二引数に渡す値
  • $ret[3] ←23 これがAddRef関数の戻り値!
  • つまり必要に応じて結果($ret[3])を変数に格納して使う。ってなんじゃこのわかりづらいのは~!

この呼び出しを成功させるだけで、1時間くらい格闘した覚えがあります…。

その他疑問

呼び出し規約をstdcallとかにすると、DllCallが3(関数が見つからない)が返ってくる。なぜだ。

Windowsで関数がどのlibファイルに含まれているかを確認する方法

Windowsで他人のライブラリをビルドして使用する場合など、
サンプルソースを動かしてみたら関数が未解決でリンクエラー…どのlibファイルをリンクすりゃいいんだ?
………なんて経験があると思います。今回はこの解消法をご紹介します。

ここではVisual Studioを使っていることを前提とします。

Visual Studioでは、使用するlibをプロジェクトのプロパティ→[リンカー]→[入力]→[追加の依存ファイル]に指定する必要がありますが、サンプルソースの関数がどのlibファイルに含まれているのかわからん!!といった場合は以下の方法を使います。

 

Visual Studioの開発者用コマンドプロンプトを開く

スタートメニューから開いてください。
例)Visual Studio 2017 (Community)の場合では「開発者コマンド プロンプト for VS 2017」という名前でした。

libファイルの内容をダンプする

libファイル群があるフォルダまで移動して、以下のコマンドを実行してダンプします。

link /dump /exports *.lib>liblist.txt

liblist.txtの内容を確認

あとは普通にテキストエディタでliblist.txtをオープンし、使いたい関数名で検索するだけ。liblist.txtの内容はこんな内容になっているはずです。

Dump of file maincore300.lib
File Type: LIBRARY
Exports
ordinal name
          ??abcfunc@@@QAE@ABV01@@Z (public: __thiscall abcfunc())
              :
          ??targetfunc@....

探したい関数がtargetfuncだったとすると、それが含まれるlibファイルはmaincore300.libということになります。

Summer Pockets(サマーポケッツ) 島モンファイト エサと場所で入手できる島モン組み合わせ一覧

発売日に予約していたものの時間が取れず、積んでおりました。昨日クリアしました…。

いや、大変よかったですね!各所でボロボロ泣きました。
特に後半は泣きすぎてマウスがクリックできなかったほど。

今回は個別ルート含め全体的にレベルが高い!
だーまえは原案だけということでちょっと心配でしたが、杞憂に終わりました。よかったよかった。

ただちょっと短かったかなぁ。
後半もうちょっとガッツリ作ってくれれば大満足でした。とりあえずファンディスク期待。

って、ちがう!!

この記事は島モンファイトの攻略記事です!
感想を書いてる場合じゃない!

島モンファイトはゲーム内のミニゲームで島モンを集めて、島の人たちとバトルをするもので、
まぁなんていうかポ○モンみたいなものです。いちおう専用のエンディングがあったりします。

ここでは島モンファイトについて、筆者が確認した範囲で情報をまとめたいと思います。
島モンは、同じ場所・同じエサの組み合わせでも複数候補の中からランダムに入手されるので、これで全部ということはありません。
そもそもまだエサもコンプリートしてないのです…。

あと色々間違ってたらごめんネ。見やすさも後日考えます。

エサ一覧と入手方法

エサ名 エサの入手方法
砂糖水(★1) 初回島モン開始時にうみちゃんがくれる
イカの皮(★1) 初回島モン開始時にうみちゃんがくれる
ミミズ(★1) 初回島モン開始時にうみちゃんがくれる
アイスの棒(★1) 初回島モン開始時にうみちゃんがくれる
昆虫ゼリー(★1) 不明(いつの間にかもってました…)
かき氷のシロップ(★2) 秘密基地に通い続ける。7/31にもらえる。
ナスビ(★2) 島モンファイトで主婦を全員撃破
ユムシ(★2) 島モンファイトで漁師、おっさんを全員撃破
鰹節(★2) 島モンファイトで亀、犬、猫を撃破
島産蜂蜜 (未入手)
ちょっとHな本 (未入手)
失敗したチャーハン(★3) 料理ができなくなっている8位のうみちゃんに勝利する。
シーウッキー(★3) 駄菓子屋のガチャガチャで取得
米氷飴(★4) 駄菓子屋で8/2までに1回はガチャをし、8/3の選択肢で「ガチャしない」を選ぶ。
魚肉ソーセージ(★4) 島モンファイトで少年、少女を全員撃破
すごくHな本(★4) 島モンファイトで蒼に勝利する。
究極のチャーハン(★4) チュートリアルでうみちゃんに勝利し、その後8位のパワーアップしたうみちゃんに勝利する。
賢者になれるHな本(★5) 秘密基地に通い続ける。8/7にもらえる
稀少焼酎・もりいいぞう(★5) 駄菓子屋に通い続ける。8/7にもらえる
うみのパンツ(★5) 神社に通い続ける。選択肢に100円があれば、必ず100円を選ぶ→願い事を聞かれたら「卓球うまくなりたい」→イナリが出たときは50円→1000円の選択肢があれば1000円を選ぶ→その後も100円をお賽銭し続ける。8/7にもらえる。(島モンチュートリアルのときのうみちゃんのセリフが変化する)

エサと場所で入手できる島モン組み合わせ一覧

なんとなく属性がわかりづらいな〜と思った島モンは後ろに属性を記載してます。
しかし、見にくいっすね…。すみません。

エサ\場所  山のなか(★1) 浜辺(★1) ため池(★1) 山の奥(★2) 海岸(★2) 神社(★3) 山の最奥(★3) 絶壁(★4) 海賊の洞窟(★4) 秘境(★5)
砂糖水(★1) クロオオアリ(★1) カメノテ(★1) シオマネキ(★1) ヤマトシジミ(★1) ヤマトシジミ(★1) マメハチドリ(★3) ハナカマキリ(★2) リュウグウノツカイ(★3) リュウグウノツカイ(★3) タヌキ(★2)
イカの皮(★1) トノサマバッタ(★1) マダコ(★1) イカナゴ(★1) アブラゼミ(★1) アカボシゴマダラ(★1) ユリカモメ(★2) ミヤマクワガタ(★2) エチゼンクラゲ(★3) ダイオウイカ(★3) エチゼンクラゲ(★3)
ミミズ(★1) オケラ(★1) アメリカザリガニ(★1) コカマキリ(★1) ニホンヤモリ(★1) アブラゼミ(★1) イワツバメ(★2) アオダイショウ(★2) ピラルク(★3) リュウグウノツカイ(★3) ピラルク(★3)
アイスの棒(★1) アオカナブン(★1) アサリ(★1) ヒグラシ(★1) ハエトリグモ(★1) クマンバチ(★1) 17年ゼミ(★3) タヌキ(★2) ダイオウイカ(★3) ダイオウイカ(★3) 17年ゼミ(★3)
昆虫ゼリー(★1) エンマコオロギ(★1) ミズクラゲ(★1) クマンバチ(★1) アゲハチョウ(★1) ミドリガメ(★1) モルフォチョウ(★2) オンブバッタ(★1) リュウグウノツカイ(★3) リュウグウノツカイ(★3) ヒッコリーホーンドデビル(★3)
かき氷のシロップ(★2) クマンバチ(★1) クマンバチ(★1) ツクシガモ(★2) ミヤマクワガタ(★2) カメノテ(★1)[海] ミヤマクワガタ(★2) アオダイショウ(★2) ヒッコリーホーンドデビル(★3) ヒッコリーホーンドデビル(★3) ケツァルコアトル(★4)
ナスビ(★2) アカボシゴマダラ(★1) カラバガニ(★2) カメムシ(★1) アライグマ(★2) ニホンミツバチ(★1) ケンランカマキリ(★2) ミヤマクワガタ(★2) アレキサンドラトリバネアゲハ(★3) エチゼンクラゲ(★3) ヤタガラス(★4)
ユムシ(★2) キジバト(★1) ヒヨドリ(★1) ライギョ(★2) ニホンカナヘビ(★2) マガモ(★2) マメハチドリ(★3) アライグマ(★2) リュウグウノツカイ(★3) ピラルク(★3) 朱雀(★5)[空]
鰹節(★2) ツクツクボウシ(★1) カラスアゲハ(★1) ツクツクボウシ(★1) ニホンカナヘビ(★2) ユリカモメ(★2) アライグマ(★2) オニヤンマ(★2) ピラルク(★3) ダイオウイカ(★3) ダイオウイカ(★3)
島産蜂蜜                    
ちょっとHな本                    
失敗したチャーハン(★3) オケラ(★1)、ヘラクレスオオカブト(★3) 国産ウナギ(★2) コウモリ(★2) イナリ(★4)、ミヤマクワガタ(★2)、アレキサンドラトリバネアゲハ(★3) コウモリ(★2)、エンゼルフィッシュ(★2) ケツァルコアトル(★4) ジュエルキャタピラー(★3)、ギラファノコギリクワガタ(希少種)(★3) リュウグウノツカイ(★3)、シロハヤブサ(希少種)(★3) リュウグウノツカイ(★3) 17年ゼミ(★3)、ヘラクレスオオカブト(★3)
シーウッキー(★3) ツクシガモ(★2) ハリセンボン(★2)、ダイオウイカ(★3) 国産ウナギ(★2) アライグマ(★2)、シロハヤブサ(★3) オオスズメバチ(★2)、ハリセンボン(★2)、ラブカ(★4) テンゼン(★4) 白虎(★5)、イナリ(★4) オオシャコガイ(★3)、17年ゼミ(★3)、ケツァルコアトル(★4) オオシャコガイ(★3)、エチゼンクラゲ(★3) オオシャコガイ(★3)
米氷飴(★4) ジュエルキャタピラー(★3) ラブカ(★4) オニヤンマ(★2) ジュエルキャタピラー(★3) オオシャコガイ(★3) ヤタガラス(★4) ギラファノコギリクワガタ(★3) リュウグウノツカイ(★3) リュウグウノツカイ(★3) テイオウムカシヤンマ(★3)
魚肉ソーセージ(★4) チュパカブラ(★4) クマゼミ(★2) オオシャコガイ(★3) ヒッコリーホーンドデビル(★3) アレキサンドラトリバネアゲハ(★3) テンゼン(★4) ヒッコリーホーンドデビル(★3) シロハヤブサ(★3) ケツァルコアトル(★4)[空] ヤタガラス(★4)
すごくHな本(★4) 17年ゼミ(★3) ピラルク(★3)、テイオウムカシヤンマ(★3) エチゼンクラゲ(★3) テイオウムカシヤンマ(★3) クマゼミ(★2)、タラバガニ(★2)、ヌシ(★4) 朱雀(★5)[空] アレキサンドラトリバネアゲハ(★3) ライギョ(★2)、ヤタガラス(★4) ダイオウイカ(★3) ニホンオオカミ(★4)、テンゼン(★4)、ケツァルコアトル(★4)[空]
究極のチャーハン(★4) ホンノウサギ(★2)、ケツァルコアトル(★4) リュウグウノツカイ(★3) クリオネ(★2)、ヤタガラス(★4) アレキサンドラトリバネアゲハ(★3)、ニホンオオカミ(★4) タラバガニ(★2)、エチゼンクラゲ(★3) ケツァルコアトル(★4) アルマジロトカゲ(★3)、シロハヤブサ(★3) イナリ(★4) ピラルク(★3) リュウグウノツカイ(★3)
賢者になれるHな本(★5) テンゼン(★4)[空] ユリカモメ(★2) ピラルク(★3) シロハヤブサ(★3) リョウイチ(★4) シロハヤブサ(★3) アレキサンドラトリバネアゲハ(★3) アルマジロトカゲ(★3) ヌシ(★4) ヤタガラス(★4)
稀少焼酎・もりいいぞう(★5) アルマジロトカゲ(★3) リョウイチ(★4) ヌシ(★4) マメハチドリ(★3) オオシャコガイ(★3) 朱雀(★5)[空] 白虎(★5)[山] 青龍(★5)[海] ラブカ(★4) オニヤンマ(★2)
うみのパンツ(★5) アルマジロトカゲ(★3)、17年ゼミ(★3) 人魚(★4) 玄武(★5)[海] テンゼン(★4)[空] シロハヤブサ(★3)、ダイオウイカ(★3) テンゼン(★4) チュパカブラ(★4)[山] ヤシガニ(★2)、ニホンオオカミ(★4) テイオウムカシヤンマ(★3)、人魚(★4) ニホンオオカミ(★4)

Kotlin文法まとめ

最近iPhoneアプリの移植で触り始めました。
メモとして適宜追加していきます。

when文 (Java/Cでいうswitch文)

変数fruitで分岐させる場合。Java/Cでいうbreakは不要です。

val FRUIT_APPLE    :Int = 0
val FRUIT_BANANA   :Int = 1
val FRUIT_MELON    :Int = 2
val FRUIT_LEMON    :Int = 3

 :

when ( fruit )
{
    FRUIT_APPLE              -> print("RINGO")
    FRUIT_BANANA             -> print("BANANA")
    FRUIT_MELON, FRUIT_LEMON -> print("MELON or LEMON")
    else                     -> print("OTHER")
}

ブロックも使えます。また複数の文を書きたい場合は、セミコロンで区切ります。

when ( fruit )
{
    FRUIT_APPLE              -> { print("RINGO"); fruit_name="RINGO" }
    FRUIT_BANANA             -> { print("BANANA"); fruit_name = "BANANA" }
    FRUIT_MELON, FRUIT_LEMON -> { print("MELON or LEMON"); fruit_name="MELON or LEMON" }
    else                     -> { print("OTHER"); fruit_name="OTHER" }
}

私はあまり好みではないですが、引数なしのwhen文という書き方もあるようです。

when
{
    fruit == FRUIT_APPLE                         -> { print("RINGO"); fruit_name="RINGO" }
    fruit == FRUIT_BANANA                        -> { print("BANANA"); fruit_name = "BANANA" }
    fruit == FRUIT_MELON || fruit == FRUIT_LEMON -> { print("MELON or LEMON"); fruit_name="MELON or LEMON" }
    else                                         -> { print("OTHER"); fruit_name="OTHER" }
}

Raspberry Pi 3で64GB以上のmicroSDカードを使う方法

Raspberry Pi始めました!
…んが、開始初日から躓いてしまいました。

64GBのmicro SDカードを購入したのですが、Raspberry Pi 3ではFAT32でフォーマットしたカードしか
認識できないようで、32GB以上のカードの場合は有名なSD card Formatter 5.0(現時点の最新版)を使っても、
自動でexFATフォーマットになってしまいRaspberry Piで正しく認識してくれないのでした。

どーするか。

解決法

SD card Formatter 4.0を使ってください。公式からは見つからず、ダウンロードはこのあたりから…。
https://sd-card-formatter.jp.uptodown.com/windows

で、起動したら[フォーマットオプション]から、[論理サイズ調整]をONにしてからフォーマットを
開始してください。これだけでRaspberry PimicroSDカードを正しく認識し、OSのインストールを進めることができます。

余談

この解決法に辿りつくまでBuffalo製のDisk Formatter Ver.2.08やHP USB Disk Storage Format Tool 2.2.3を
試したのですが、前者はmicroSDカードのライトプロテクトを外してくださいといった意味不明なこと
microSDカードにはWriteProtectはない)を言ってきたり、後者はFAT32でフォーマット出来たにも関わらず
Raspberry PiのOSインストール中にエラーが起こったりと、なかなかうまくいきませんでした。

ここは素直にSD Card Formatter 4.0を使用するのが良いようです。

ドラゴンクエストXI用 ドラクエ2最強ステータスのふっかつのじゅもん

明日(もう今日か)発売のドラクエ11では過去(ドラクエ2?)のふっかつのじゅもんが使えるようです!たぶん。

家の押入れを漁ったところ、小学生時代自分が書いたであろうドラクエ2の最強ステータスの
ふっかつのじゅもんがありましたので記念に投稿しておきます(使えなかったらごめんね)

最早ドラクエ少年の通過儀礼となっているふっかつのじゅもん写し間違えがよほど怖かったのか、
同じステータスのじゅもんを3つ残してます…やるな!小学生の頃のオレw

いちおう3つとも載せときます。ステータスは一緒です。

その1

やぼた ちけれ ろれきか
えせみ かさえ ぷねなず
わぽつ はげぼ ぴぴすて
ぼびす めてれ わたじぴ
かばる すもめ はむおぽ い

その2

いねわ わもう うあぴぜ
ざざう りわゆ ほばじち
くひわ じぷの ほぺまら
れぷふ かげり てずかぬ
ぱぽび ごうせ がるわほ ぼふ

その3

えひげ ごるこ さこらは
ねらぶ まやほ ねざぐせ
かはわ ずぽふ めえゆぎ
ざきよ ちぱぜ むうにり
せてし かのゆ おいかぶ ぬぶ

なつかしいですねぇ。
もょもとでおなじみの以下も使えるそうです。
いや〜楽しみだ。

ゆうて いみや おうきむ
こうほ りいゆ うじとり
やまあ きらぺ ぺぺぺぺ
ぺぺぺ ぺぺぺ ぺぺぺぺ
ぺぺぺ ぺぺぺ ぺぺぺぺ ぺぺ

小学校自体のメモはこれ。意外に綺麗ですね。