BattleField 1のStatsを得る

gametools.networkを利用する API JSON Excelとの連携 C++ Builder CE登場

の続編です。アプリを作ります。結果はExcelのファイルに追記するので、まずExcelのファイルのハンドリングをC++ Builder CEから行うやり方を紹介します。まずヘッダーは、

#include <System.Win.ComObj.hpp>  // EXCEL処理用

が必要です。初期化は、ヘッダを見ればお分かりのようにComでやりとりをしますから。

	Variant ExcelApp = CreateOleObject("Excel.Application");

Variantという変数の型は見慣れないと思いますが、宣言時には具体的な型が決められず、実行時に決まるという変化する型でVariantです。この型の変数がふんだんに出てきます。結果の1行を既存のExcelのファイルに追加する関数は、

void __fastcall TForm1::Excel_addRow(WideString srcFile, WideString dstFile)
{
    bool closeExcel = false;
	Variant range2;
	Variant ExcelApp = CreateOleObject("Excel.Application");

			ExcelApp.OlePropertySet("Visible", true); // Excel not shown
			ExcelApp.OlePropertySet("DisplayAlerts", false); // No dialog for overwrite

			xls_books = ExcelApp.OlePropertyGet("Workbooks");
			xls_abook = xls_books.OleFunction("Open", srcFile);
            // 最初のシート選択
            xls_abook.OlePropertyGet("Sheets", 1).OleProcedure("Select");
            xls_asheet = xls_abook.OlePropertyGet("ActiveSheet");
			//

			range2 = xls_asheet.OlePropertyGet("UsedRange");
			linecount = range2.OlePropertyGet("Rows").OlePropertyGet("Count");
			columncount = range2.OlePropertyGet("Columns").OlePropertyGet("Count");
               // セルA5のデータをAnsiString Data1に読み込む
    // C++Builder 2007では、range = worksheet.OlePropertyGet("Cells","A5"); だったところ。
	//range = worksheet.OlePropertyGet("Cells").OlePropertyGet("Item", 5,1);
	//Data1 = range.OlePropertyGet("Value");

		// dump item names from A3->G3
		String items;
		String res;


		int i = 1;
		String temp;

		for(;;){
			temp = GetItemNames(i);
			if( temp == "" )
				break;
			targets->Add(temp);
			i++;

		}


		GetPersonalStats();
		BasicStats->Checked = true;
		GetWeaponStats();
		WeaponStats->Checked = true;

        prepsearch();

		for( int i = 0 ; i < targets->Count ; i++ ){
			items += targets->Strings[i];
			items += " ";
			results->Add(searchit(targets->Strings[i]));
		}
		Memo1->Lines->Add(items);

		for( int i = 0 ; i < results->Count ; i++ ){
			res += results->Strings[i];
			res += " ";

		}
		Memo1->Lines->Add(res);

			//ShowMessage("huh?");
		// put cells

		for( int i = 0 ; i < results->Count ; i++ ){
			putcell(i);
		}

			//ShowMessage("huh?");
				//xls_auxbook.OleFunction("Close");
			xls_abook.OleFunction("Save");
			closeExcel = true;
            ExcelApp.Exec(Procedure("Quit"));


}

この関数で呼び出しているGetItemNames(i)の実体は、

String GetItemNames(int k )
{
	Variant range;

	range = xls_asheet.OlePropertyGet("Cells").OlePropertyGet("Item", 3,k);

	//AnsiString retvalue = range.OlePropertyGet("Value");
	//if( retvalue == "" )
	//	ShowMessage("empty string");
	return range.OlePropertyGet("Value");

}

追記するExcelファイルの見出し行(3行目)の左からk番目の項目名をまずcell位置を得て、そのセルの中身(Value)を得て、それを関数の戻り値としてStringで返しています。具体的には、

の”date”,”timePlayed”,”kills”,”deaths”,”killDeath”までが基本statsで、以下”Gewehr 98 Sniper”,”Tripwire Bomb –HE”が着目する武器の指定です。これをglobal領域に宣言した変数

	TStringList* targets = new TStringList;
	TStringList* results = new TStringList;

	bool there = false;

			int linecount,columncount;


        Variant xls_books;
        Variant xls_abook;
        Variant xls_sheets;
        Variant xls_asheet;

に拾っておきます。GetPersonalStats()は、

void    GetPersonalStats()
{
		TMemoryStream* ms = new TMemoryStream(); 
		TStringList* atlist = new TStringList();


	TIdHTTP* kick = Form1->IdHTTP1;
 UnicodeString aurl = TIdURI::URLEncode(
  "https://api.gametools.network/bf1/stats/?name=docnao");

	int ret = 0;

		Form1->Memo1->Lines->Clear();

		Form1->Label1->Caption = aurl;

		Form1->IdHTTP1->Request->CustomHeaders->Clear();  // for re-use

		Form1->IdHTTP1->Request->CustomHeaders->FoldLines = false;
		Form1->IdHTTP1->Request->CustomHeaders->Add("accept:application/json");

		jsonorig = "";

			try {
				kick->Get(url,ms);
			}
			catch(const Exception& e)
			{
				ShowMessage(e.ClassName());
				return;
				//printf("Caught C++ Exception: %s :\n", e.msg());
			}

			ms->Position = 0;
			atlist->LoadFromStream(ms, TEncoding::UTF8);

			jsonorig="";
			//for( int i = 0 ; i < Memo1->Lines->Count ; i++ )
			for( int i = 0 ; i < atlist->Count ; i++ ){
				jsonorig += atlist->Strings[i];
				//Memo1->Lines->Add(atlist->Strings[i]);
			}




}

GetWeaponStats()は、

void    GetWeaponStats()
{
 		TMemoryStream* ms = new TMemoryStream(); 
		TStringList* atlist = new TStringList();


	TIdHTTP* kick = Form1->IdHTTP1;
		//Form1->Memo1->Lines->Clear();

		Form1->Label1->Caption = wurl;

		Form1->IdHTTP1->Request->CustomHeaders->Clear();  // for re-use

		Form1->IdHTTP1->Request->CustomHeaders->FoldLines = false;
		Form1->IdHTTP1->Request->CustomHeaders->Add("accept:application/json");

		jsonorig2 = "";

			try {
				kick->Get(wurl,ms);
			}
			catch(const Exception& e)
			{
				ShowMessage(e.ClassName());
				return;
				//printf("Caught C++ Exception: %s :\n", e.msg());
			}

			ms->Position = 0;
			atlist->LoadFromStream(ms, TEncoding::UTF8);

			jsonorig2="";
			//for( int i = 0 ; i < Memo1->Lines->Count ; i++ )
			for( int i = 0 ; i < atlist->Count ; i++ ){
				jsonorig2 += atlist->Strings[i];
				//Memo1->Lines->Add(atlist->Strings[i]);
			}



	Form1->IdHTTP1->Disconnect();



}

です。前の記事のcurlで得たのと同様ですね。具体的な検索は、

void prepsearch()
{

		LStringReader = new TStringReader(jsonorig);
	LJsonTextReader = new TJsonTextReader(LStringReader);
	LIterator = new TJSONIterator(LJsonTextReader);

				LStringReaderw = new TStringReader(jsonorig2);
	LJsonTextReaderw = new TJsonTextReader(LStringReaderw);
	LIteratorw = new TJSONIterator(LJsonTextReaderw);


}

String searchit(String target)
{

	if( target == "date" )
		return Date();

	if(LIterator->Find(target)){
		if( target == "timePlayed" )
			return LIterator->AsString;
		else if( target == "killDeath" )
			return FloatToStr(LIterator->AsDouble);
		else
			return IntToStr(LIterator->AsInteger);

	}

	for( int i = 0 ; i < 247 ; i++ ){
		String wepname;

		if( LIteratorw->Find("weapons[" + IntToStr(i) + "]" + ".weaponName"))
			wepname =  LIteratorw->AsString;
		if( wepname == target ){
			int kills;

			if(LIteratorw->Find("weapons[" + IntToStr(i) + "]" + ".kills")){
				//ShowMessage("found "+ IntToStr(i) + " " + wepname + LIteratorw->AsString);
				kills = LIteratorw->AsInteger;

			}
			else
				ShowMessage("can't pick up "+ wepname + "kills");
			return IntToStr(kills);

		} // found

	}

	return "";

}

見ればお分かりかもしれませんが、いわゆる”バカサーチ”で探しています。相手がソートされていないので、しょうが無いですね。武器種類は300以下ですから、それほど時間はかかりません。今取ってきた結果は、

こんな風に得られます。TlistViewでなくExcelファイルに追記すると、簡易DBとして使えるので便利です。とはいえgametools.networkがいつまで稼働してるかは分かりませんね。

アプリの画面の終了時は上図のようになってます。7秒程度でデータを取り込めています。武器名を変更すればそれに対応したstatsが得られますが、途中結果は更新されませんね。あくまで最終行だけですね。ま、時々stats得ています。C++ BuilderからExcelのファイルの読み書きはそれほどめんどくさくはないので、やってみるのが吉です。慣れるまではコツが要りますがね。

コメント