様々な投票ルールを体験できる投票アプリを作った話
はじめに
ErabeRule(えらべるーる)というWebアプリを開発しました。
ErabeRuleは、様々な投票ルールを実験的に使うことができる投票アプリです。主催者は投票ルームを作成し、ルームのリンクを知っている人がその投票に参加できます。
このアプリの目的は、多くの人に「多数決だけが決め方ではない」ということを知ってもらい、様々な投票ルールを実際に使うことを通して、それらをより身近なものにすることです。
開発したきっかけ
昨年僕が高校3年生の時、経済の授業で投票システムについて学び、決め方によって投票の結果が変わってしまうということに衝撃を受けました。
それがきっかけで社会的選択理論という分野に興味を持ち、『多数決を疑う』という本を読みました。経済学的に多数決より優れた投票ルールがたくさんあるのに、多くの場面では多数決のような決め方が採用されてしまうことがショックでした。
そこで、社会的選択理論をもとにしたGoogleフォームのような投票アプリを作ろうと思い、ErabeRuleを開発しました。
使い方
「参加」と「作成」の2つのタブがあります。
投票の主催者は、以下の4つの投票ルールを使ってルームを作成できます。
- 多数決
- ボルダルール
- コンドルセ・ヤングの最尤法
- Majority Judgement
それぞれの投票ルールがどのようなものかについては、投票ルールの詳細をご覧ください。
参加者は、QRコードを読み取るか、ルームのタイトルを検索することで投票に参加できます。
いくつかの投票ルールでは、他の決め方を採用していたらどのような結果になっていたかシミュレーションできます。
↓デモルームを作ってみたので、ぜひ投票してどんな結果になるか見てみてください!
技術的側面
このアプリの初期バージョンは、実は1年前にリリースしていました。しかし、当時はWebアプリを作った経験がなく、非常に読みにくいコードを書いてしまっていたためアップデートが困難な状況でした。また、React・TypeScriptを使って作りたかったという理由もあり、今回プロジェクトを作り直すことにしました。
技術構成
ErabeRuleは、主に以下のようなフレームワークやライブラリを使って開発しました。
- Firebase Authentication
- Cloud Firestore
- Next.js (TypeScript)
- Recoil (グローバルな状態管理)
- Emotion (CSS)
- Framer Motion (アニメーション)
- Vercel (デプロイ先)
ユーザー認証には、Firebase Authenticationの匿名認証を使用しました。利用登録をなくし、QRコードを読み取ってすぐに投票に参加できるようにするためです。
また、Cloud Firestoreのスナップショットリスナーを活用し、投票の結果をリアルタイムで更新できるようにしました。
eraberule.com
にランディングページを、app.eraberule.com
にアプリ本体をデプロイしました。
ディレクトリ構成
root/
├─ pages/
├─ components/
│ ├─ ui/
│ ├─ common/
│ ├─ functional/
│ └─ templates/
├─ hooks/
├─ states/
├─ rules/
├─ types/
├─ styles/
│ ├─ global.css
│ └─ colors.ts
├─ lib/
├─ utils/
└─ public/
ここからは、ディレクトリ別に特筆すべき点を紹介していきます。(初心者なので不自然な部分があるかもしれません…)
pages/
UIに関係しないロジック部分の記述。
components/templates/
データを持たない、ページのUIに関する記述。
states/
グローバルに管理するRecoilのatomを配置。
rules/
このフォルダには、各投票ルールのアルゴリズムごとにファイルを作って配置しました。
firestoreには投票者が送信した生のデータが入っているので、各投票ルールに基づく結果の計算はフロントエンドで行われます。データはリアルタイムに更新されるため、票が入れられるたびに計算が実行されます。
投票ルールのアルゴリズムを作る部分が一番大変でした。人間なら直感的に結果がわかる場合であっても、数式化すると非常に手間がかかるものもあるとわかりました。同率順位が発生した場合に順位表示がずれないようにする点や、タイブレーキング(同点の候補者に対し、もうひと工夫加えて決着をつけること)の処理も難しかったです。
styles/colors.ts
アプリの中で何度も使う色は、まとめてこのファイルで管理しています。Emotionと組み合わせることで、動的な色の変更を簡単に実装できます。
//抜粋
const primarySelectedColor = "rgba(61, 100, 242, 0.12)"
//抜粋
const SingleSelectionCell: React.FC<Props> = (props) => {
return (
<button css={() => cellStyle(props.isSelected)}>
テキスト
</button>
)
}
const cellStyle = (isSelected: boolean) => css`
background-color: ${isSelected ? primarySelectedColor : "transparent"};
`
今後
今後は、新たな投票ルールを追加したり、投票作成の自由度を高めたりしていきたいです。
ぜひErabeRuleを使って様々な投票ルールを体験してみてください!
Discussion