kmjp's blog

競技プログラミング参加記です

Advent Calendar 2020 : プログラミングコンテスト参加中の心拍数を測定してみる

この記事は、Competitive Programming (1) Advent Calendar 2020 - Adventarの17日目の記事です。
読んでも競技プログラミングの実力は上がりませんので、ご注意ください。

過去の分はこちらです。

はじめに

コンテストに参加していると、予想外のWAにびっくりしたり、SRMでChallenge前に緊張したり、いろいろ心の動きがある。そこで本記事では、心拍数を測定することで、心の動きを外から観測できないか試してみることにした(おまけで一部コンテストでは部屋のCO2濃度も測ってみた)。
まずは結果を示し、最後に採取方法を示す。

実験結果

プレッシャーがないとき

Unratedで競争のない環境では、プレッシャーや緊張感がないので心拍数も大きく変動しないことが予想される。実際はどうだろうか?

◆ABC185とLeetCode219

以下は12/13に行われたAtCoder Beginner Contest 185と、その後自分で解いていたLeetCode Weekly Contest 219の時の記録である。グラフは時系列になっており、オレンジ線(右軸)が1分あたりの心拍数、青線(左軸)がCO2濃度(ppm)である。前半と後半で採取の間隔が異なるように見えるが、これは原因がわかっていないのでまずは無視してほしい。
ABC185の方はコンテスト開催中に解いているが、自分はrated対象外なのと、遅刻参加のため上位を狙うつもりもなく、その意味ではプレッシャーがない。その後のLeetCodeもコンテスト時間外での取り組みなので同様である。

f:id:kmjp:20201217022513p:plain
AtCoder Beginner Contest 185+LeetCode219

心拍数を見ると、全体的に70~80程度を遷移しつつ、ABCのA,B,CをSubmitしたあたり95前後になっているように見える。その後Dでは87、EでWAを出したときも90程度になっている気がするが、Fの時が全然心拍数に変化がない。FはBITを使えると即答できる問題だったので、「はいはいBITBIT」という感じで、緊張感がなかったからかもしれない。
その後行ったLeetCodeも、コンテスト時間外であるが、2~4問目のSubmit時は90程度まで心拍数が上がっているようにも見える。
こう見ると、いずれもUnratedとはいえ、多少はSubmit時に緊張しているのかもしれない。

この間、CO2濃度は500ppm→850ppm程度まで増加した。実は部屋を閉め切ると1時間で1500ppmぐらい、一晩で2500ppmまで行くのだが、それを知って以降軽く換気するようにしたので850ppmで収まっている。

◆Codeforces Round 688 (Div2)

以下はCodeforces Round 688(Div2)にオンタイム参加した例である。

f:id:kmjp:20201215015852p:plain
Codeforces Round 688(Div2)

こちらもUnratedなのでプレッシャーは小さいはずである。何となくSubmit→passed pretestの流れでは一時的に心拍数が90程度に上がっているような気がするが、もっと心拍数上がりそうなD failedの時に大きな変化がないのが謎である。やはりCO2濃度は上昇している。

まとめると、普段は70程度の心拍数だが、UnratedでもSubmit時は心拍数が90程度まで上昇しているように見える。とはいえ、落ちる気がしない問題など、特にプレッシャーがないときは上昇幅が小さいような気もする。

プレッシャーがあるとき

以下、もう少し心拍数への影響がありそうなRatedな回を見ていく。

鹿島建設プログラミングコンテスト2020

こちらはRatedかつ賞金付きということで、それなりにプレッシャーがあることが期待できる。

f:id:kmjp:20201216021208p:plain
鹿島建設プログラミングコンテスト2020(AtCoder Regular Contest 110)

なんか波形的に前半の測定がうまくいっているか自信がないが…。なんとなくB,C,DをSubmitしたときには心拍数が上がっているように見える。特にDでACを出したとき心拍数が100程度になっており、プレッシャーがない時より最大値が大きい。
ちなみにこの回はレート減と不本意な結果で終わっている。

AtCoder Grand Contest 049

こちらもRatedなので同じく心拍数の動きが期待できる。

f:id:kmjp:20201216021231p:plain
AtCoder Grand Contest 049

下に線が伸びてるときは測定エラーかな?無視してほしい。
結果を見ると、A,BでACを出したときやCでWAを出した後など、心拍数が100を超えている。RatedなのとAGCだと前半も油断できないということで、心拍数が上がりがちなのだろうか。DでACが出たときの心拍数が意外に低いが、これはもともとTLEになりそうなコードを出していたので、自信がなく期待もしてなかったからというのが原因かもしれない。
ちなみにこの回もレート減と不本意な結果で終わっている。

◆TopCoder Single Round Match 795

続けてCoding/Challenge/Systestとフェーズが段階的に変わり、心拍数の変動が多そうなSRMの結果も載せる。(むしろSRMはこの企画の本命である)

f:id:kmjp:20201216021311p:plain
TopCoder Single Round Match 795

ただ、不思議なことに意外に心拍数の変動が少ない。Coding phaseではローカルテスト以外のジャッジが動かないので、Submit時の緊張感がないのかもしれない。この回はChallengeで撃墜されたのだが、その際の変動が少ないのも謎。辛うじて、さすがにsystest後の結果発表では心拍数が90を超えている。
ちなみにこの回もレート減と不本意な結果で終わっている。

◆Codeforces Round 687

ここまでレート減の結果ばかりなので、レート増のケースも載せてみる。

f:id:kmjp:20201216021254p:plain
Codeforces Round 687

この回、ratedなはずなのに意外に心拍数の動きが小さい。A,BのACでも動きがなく、CのSubmitで86程度である。
もしかしたら時間帯が夕方ということもあるのかもしれない。

まとめ

心拍数は、Submit時の緊張感など、競技プログラミングの参加者の心の動きの一部を反映しているように思われる。Submitの瞬間など、普段70程度の心拍数が90~100程度に上がることがある。ただし、簡単でACを疑わないような問題や、SRMのように即座にジャッジされない問題ではさほど心拍数の動きは無いようだ。自分ではもう少し心拍数が大きく変動すると思っていたので、ちょっと物足りない結果となった。コンテスト中短期間心拍数が上がることはあっても、長時間持続することがないことも見栄えがしない一因かもしれない。

とはいえ、心拍数はコンテスト中の参加者の心の動きを外から測定するのに、一つの指標にはなるように感じた。今後、他の値を取ってみても面白いかもしれない。

あと、密閉した部屋のCO2濃度はすぐ1000ppm~2000ppmを超える。これは身体に影響を及ぼすレベルではないが、ビル衛生管理法や学校衛生基準では1000~1500ppm以下が推奨されているので、定期的に換気した方がよさそうだ。

収集方法

PCやスマートフォン、ネット環境はすでにあるものとして、その他は2万円強で揃う。構成図は以下の通り。

f:id:kmjp:20201217022449p:plain
構成図

Raspberry Pi 3でUbuntuを動かし、Prometheusで室温とCO2を取得し、心拍数は下記手順でInfluxDBに放りこんで、Grafanaでグラフ化した。

心拍数の収集法

Xiaomi Smart Band4は標準機能で1分毎に心拍数を取れるが、アプリ連携するともっと細かい間隔で心拍数が取れる*1。今回はアプリとしてNotify for mi Bandを利用した。こちらツールの設定エクスポート機能を使うとSQLite形式のデータを吐き出せるので、Raspberry Pi側ではSambaを動かしておき、このデータをSMB経由でRaspberry Piに送り付けた。

Grafanaでグラフ化できるよう、InfluxDBをRaspberry Pi上で動かし、下記コードでSQLite形式データから心拍数データをInfluxDBに取り込んだ。この手続きは現在は(Notify for mi Bandからのエクスポート、SMBでの送信含め)手動で行っている。

import datetime
import sqlite3
from influxdb import InfluxDBClient
from datetime import timedelta, timezone

JST = timezone(timedelta(hours=+9), 'JST')
influx = InfluxDBClient(database='heartbeat')
conn = sqlite3.connect('backup.db')
c = conn.cursor()
data = []

for row in c.execute("SELECT * FROM rush_com_mc_miband1_model2_HeartMonitorData"):
	data.append({
		"measurement": "monitor",
		"fields": {"heartbeat": row[5]},
		"time": datetime.datetime.fromtimestamp(row[9]/1000,JST),
		})

print("append %d entries" % len(data))
res = influx.write_points(data)

CO2濃度の採取法

co2-miniは室温・CO2濃度を測定できる安価な機器である。マニュアルには書いていないが、co2-miniではデータ通信可能なケーブルを使うとUSB経由でデータが取れる。Prometheus用のExporterがいくつか公開されているので、今回はこれを使用してPrometheusにデータを取り込んだ。
GitHub - gebi/co2exporter: Prometheus exporter for co2 sensors

参考文献

プログラミング中の脳の動きを外から観測する研究には下記がある。GIGAZINEはちょうど今日見かけたタイムリーな記事だった。
gigazine.net
id.nii.ac.jp
www.jstage.jst.go.jp

プレイヤーの心拍数表示をエンターテイメイントに使う例として、ゲームのRTA(Real Time Attack)動画がある。下記動画ではゲーム終盤でプレイヤーの心拍数が高止まりして、コメント欄も盛り上がっている。
www.nicovideo.jp

その他、人間が作業しているときに脳の動きやストレスを測る研究はいろいろあるので、競技プログラミングにも適用できるかもしれない。

あとがき

元々「競技プログラミングがスポーツや囲碁将棋、eSportsのように観戦が娯楽として成立しにくいのは、参加者が何やってるかわかりにくいからじゃないか?」という思いがありこんなネタをやってみました。最初そこらへんも前書きでいろいろ書いていたのですが、くどくなったのでばっさり削除。

心拍数ネタは2017年のAdvent Calendarのころから思いついていて、上記の通りSRMを対象に測定する予定でした。しかし当時まだ心拍計で安価なものがなく(fitbitで1万円強?)、他のネタを先に書いているうちに、SRMの参加者が減りモチベーションが低下していました。そんな中、上述のRTA動画を見てしまい、しまったと思ったのと、今年Xiaomi Smart BandをAmazon Prime Day契機で購入したので、今更ながらこんなネタをやってみました。思ったよりも心拍数の変動が小さく、ちょっと残念。

さて、毎年Advent Calendarでは普段の競技プログラミングでやらないことをやろうとしていたのですが、5年もやるととりあえず手持ちのネタが尽きてしまいました。もう一つ別ネタも思いついたのですが、何年かかるかわからないネタなのでまたそのうちに書くことにします。

明日18日はけんしんさんの記事です。タイトルが未公開ですが、何を書かれるんでしょうかね。

*1:スマホ側もバンド側も電池消費が激しいので、必要な時だけ間隔を細かくすることを推奨