2023年12月30日土曜日

シミュレーションゲームを使った行動分析

Twineというツールがある.もともとこのツール,「対話的非線形物語(interactive, nonlinear stories)」あるいは「あなた自身の冒険を作成(create-your-own-adventure)」と呼ばれる,オンライン版のテキストアドベンチャーゲームを作成するツールである.

このツールを用いて行動分析できないかと今年の卒研生が考えた.彼らは「避難行動のシミュレーションゲーム」を作成し,いま,その分析をしながら卒論を書いている.

もとより簡単な行動ログを記録する機能が用意されていたが,より簡便かつ確実に,詳細な行動記録を取れるように拡張してみた.本稿ではその概要を説明する.

Snowmanの利用

まず,Twine上で動作させるstory formatとしてSnowmanと呼ばれるものを利用する.Twineでは,いくつかのスクリプティングシステムを切り替えて活用できる.そのスクリプト言語のことをstory formatと呼んでいる.

デフォルトではHarloweと呼ばれるものが組み込まれているが,これを,Snowmanと呼ばれるstory formatに切り替える.Snowmanは,CSSやJavaScriptをある程度理解している人向けに用意されているもので,PHPやERB(Embedded Ruby)のように,<% …… %>や<%= …… %>といったタグを用いてJavaScriptをそのまま埋め込められる.

なお,Snowmanへの切り替えは,Twine編集画面のStoryタブにあるDefaultsメニューから行う.

Story JavaScriptの設定

Story formatをSnowmanに切り替えたら,まず,StoryタブのJavaScriptメニューを開き,Story JavaScriptに以下のコードを記載する.

window.story.state.hash = {};


// create a unique ID

window.story.state.uid = Date.now().toString(16) +

            Math.floor(1000 * Math.random()).toString(16);


$(window).on('sm.passage.shown', 

             function (eventObject, passageObject) {

  window.story.state.

    hash[passageObject.passage.name] = eventObject.timeStamp


  // disable the browser's back button

  history.pushState(null, null, null);

  return;

});


// to show the alert to quit the game

$(window).on('beforeunload', function (event) {

  event.preventDefault();

  return;

});

このコードは,ストーリーが起動されたときに最初に走るJavaScriptのプログラムである.なお,Snowmanでは,JavaScriptの動作スコープは各passage(ページのこと)内に閉じられており,グローバル変数をwindow.story以外に持たない.したがって,ストーリー全体でデータをやりとりしたい場合には,基本的にはwindow.story.state変数にいろいろと付け加えていくしかない.

そのような理由により,まずは,window.story.state.hashという連想配列を用意し,動作状況はそこに記録していくことにする.

なお,もうひとつ,window.story.state.uidというユーザIDを記録する変数も用意し,そこに一意な文字列として作成するIDを格納する.

Snowmanでは,passage(ページ)遷移時にsm.passage.shownというイベントを発火する.各passageのアクセス記録を取りたければ,そのイベントを受けて,先の連想配列に,passage名をキー,その時点でのタイムスタンプ(Unixエポック)をバリューとして,記録するようにしておけばよい.passage名はpassageObject.passage.nameとして,タイムスタンプはeventObject.timestampとして与えられるので,それを連想配列に記録する.

残りのコードは,ブラウザの「戻る」ボタンを無効にしたり,途中でやめようとしたときにアラートを発したりという設定を行うものである.

記録のテスト

簡単なストーリーを用意した.次の図は,四つのpassageから成るストーリーである.Story JavaScriptには先のコードが入れられており,最後のページには,記録されたユーザIDとJSON形式で提示されるアクセス一覧を表示するコードが埋め込まれている.

このストーリーを実行して最後のページ(The last page)まで遷移すると,次の画面が現れる.

一意なユーザIDと,JSON形式で表現されたアクセスログが表示されていることを確認できる.

ところで,ここまで注意深く読んできたひとは,"The last page"が記録されていないじゃないか?ということに気付いただろう.そのとおり.残念ながら最後のページの記録が残らない.これは,どうも,sm.passage.shownというイベントが,ページのレンダリングの「後で」発火するから,という仕様によるものらしい.

なお,ドキュメントにはsm.passage.hideやsm.passage.showというイベントもあると書いてあり,shownではなくshowを使えば最後のページも記録できそうなものだが,現時点で利用できるTwine 2.8.0とSnowman 2.0.2の組合せではsm.passage.shownしか使えなかった.残念だが,致し方ないところである.最後まで記録するためには,最後にもうひとつ,ダミーのページを用意して対処する必要がある.この点には注意しなければならない.

サーバへ送信

さて,ここまで確認できれば,あとは,最後のpassageにサーバへ記録を送信するコードを埋め込み,サーバ側ではその送信を受け取るコードを用意すればよいだけである.

<%

  $.ajax({

    url: 'https://your.server.domain/php/tlog.php',

    type: 'POST',

    data: { 'uid': window.story.state.uid,

            'log': JSON.stringify(window.story.state.hash) },

    dataType: 'json'

  });

%>

このコードはyour.server.domainというサーバに結果を送信するAJAXのコードである.ユーザIDとJSON形式で表現したアクセスログを送っている.

なお,アクセスログのデータは ... 'log': JSON.stringify(window.story.state.hash) }ではなく... 'log': window.story.state.hash }としてもそのままJSONデータとして送信できるが,サーバ側での処理を簡易化するため,この段階で文字列データ化して送信している点に注意.

サーバ側の処理

サーバ側のプログラムは,/var/www/html/php/tlog.phpというファイルにおいて,次のように記述する(サーバ側はUbuntu 20.04, PHP 7.4.3 および SQLite3 3.31.1を使用).

<?php

  $uid = $_POST['uid'];

  $log = $_POST['log'];


  $dbfile = './tlog_data.sqlite3';

  $conn = new PDO('sqlite:' . $dbfile);


  // create table

  $ddl = 'CREATE TABLE IF NOT EXISTS userdata(

             id integer primary key autoincrement, 

             uid string, log text)';

  $conn->query($ddl);


  // insert posted data

  $sql = 'INSERT INTO userdata(uid, log) 

          VALUES (:uid, :log)';

  $stmt = $conn->prepare($sql);

  $stmt->execute(array(':uid' => $uid, ':log' => $log));

?>

以上の比較的単純なコードで,ユーザ行動を記録できる.次のスクリーンショットは,記録の例である.左からID,ユーザID,アクセスログが記録されている状況を確認できよう.

今回は単純な線形のストーリーであるため,全て同じ行動でタイムスタンプが違うだけだが,複雑な非線形ストーリーを用意すれば,全ての選択行動をデータベースに記録でき,その結果を分析すればユーザの判断行動もわかるという仕組みである.

行動分析の例

最後に,行動分析の事例を紹介しておこう.いま卒論に取り組んでいる学生たちが用意したストーリーで得られた,ユーザの選択行動データである.テーマは「避難行動」,大地震に遭遇したときの避難状況において,ユーザはどういった判断をするのかという分析である.

次の図は,アクセスログから解析した行動状況を示すものである.ノードは各passageを示し,ノードに付記されている数値は,そのpassageを通過したユーザの数を表す.また,ノードの色は滞在時間の平均値から計算されたものであり,寒色系は短い時間,暖色系は長い時間を表している.白は誰も通過しなかったノード,および,最後のノードである(最後のノードはそもそも滞在時間を計測できないため).

この図は,アクセスログと,Twineから出力される.tweeファイルに基づき,PythonとGraphvizを用いて作成した.アクセスログだけでなく.tweeファイルも必要な理由は,誰も通過しなかったノードはアクセスログから掘り起こせないからである(なお,その作業を行ったことにより,どこからも参照され得ない不適切なノードの存在も発覚した.そのような不適切なノードは分析作業の対象外として,先の図には表していない).

この結果から,いくつか意義のある分析結果が得られている.ここでは,ひとつの興味深い事例を紹介しよう.


「道が分かれている」というpassageでは,ハザードマップを見たことがあるか否かの質問を投げかけている.「はい」を選んだグループと「いいえ」を選んだグループでは,明らかに,その後に投げかけられた「どの場所を通っていきますか?」という設問の回答に差が出ている.

見ただけで,ハザードマップを見たことがあるグループは大通りを選ぶ傾向がある一方で,見たことがないグループはそうでもないことがわかる.実は,ハザードマップには大通りを選ぶべきであるヒントが記されていた.したがって,素直に解釈すればハザードマップを見たグループは大通りを選ぶ一択になるはずなのである.この結果に対してカイ二乗検定を実施したところ,1%水準で有意差が示された.

0 件のコメント:

コメントを投稿