20
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

モンティ・ホール問題の扉を増やしてクソゲーにする

Posted at

この記事は クソアプリAdvent Calendar 2025 の23日目の記事です。

はじめに

モンティ・ホール問題という有名な確率論の問題があります。

プレーヤーの前に閉じた3つのドアがあって、1つのドアの後ろには景品の新車が、2つのドアの後ろには、はずれを意味するヤギがいる。プレーヤーは新車のドアを当てると新車がもらえる。プレーヤーが1つのドアを選択した後、司会のモンティが残りのドアのうちヤギがいるドアを開けてヤギを見せる。

ここでプレーヤーは、最初に選んだドアを、残っている開けられていないドアに変更してもよいと言われる。
ここでプレーヤーはドアを変更すべきだろうか?

一見、ドアを変更してもあたりの確率は変わらないように感じますが、実はドアを変更することであたる確率が1/3から2/3に変わる、という問題です。

正しい確率を計算するための方法はいくつかありますが、直感的な理解のための説明として 「扉を増やして考えてみる」 という解説をよく聞きます。

つまり、問題を以下のように考え直します。

プレーヤーの前に閉じた 100個 のドアがあって、1つのドアの後ろには景品の新車が、 99個 のドアの後ろには、はずれを意味するヤギがいる。プレーヤーは新車のドアを当てると新車がもらえる。プレーヤーが1つのドアを選択した後、司会のモンティが残りのドアのうちヤギがいるドアを 98個 開けてヤギを見せる。

ここでプレーヤーは、最初に選んだドアを、残っている開けられていないドアに変更してもよいと言われる。
ここでプレーヤーはドアを変更すべきだろうか?

最初の段階で100個のドアの中からあたりを選んでいない限り、ドアを変更することであたりのドアを選べます。
つまり、ドアを変更することで99%の確率であたりのドアを選ぶことができます。

クソゲーであります。
というわけで今回は扉を100個に増やしたモンティ・ホール問題実装してみました。

作ったもの

【たくさん】モンティ・ホール問題

keiyakeiya.github.io_kusoapp2025_.png

扉が100個あります。
選び放題です。

実装について

普通に実装しても面白くないので、AltJSを使って実装してみました。
AltJSとはJavaScriptの代替となるプログラミング言語群の総称で、JavaScriptにコンパイルされることで実行されます。JavaScriptの弱点を補ったり、簡潔な構文を採用したりと様々な言語が存在しますが、わざわざ1つのプロジェクトに複数の言語を用いて無駄に複雑化させるためのものではありません。

というわけで今回はJavaScriptに加えて以下の言語を使いました。

  • TypeScript
  • CoffeeScript
  • PureScript
  • Dart

これらの言語でクラスや関数を実装してコンパイルし、JavaScript側でimportして使用しました。今回のプログラム全体を説明すると長くなってしまうので、Hello Worldのプログラムで、どんな感じで組み合わせたかご紹介します。

※ 筆者はこれら4つの言語に関しては全くの初心者であり、今回ご紹介する方法が正攻法なのかすらわかりません。その点ご留意ください。

AltJSをてんこ盛りにするための構成

ディレクトリ構成は以下の通りです。

.
├── build
│   ├── coffeescript
│   ├── dart
│   ├── purescript
│   └── typescript
├── index.html
├── src
│   ├── coffeescript
│   │   └── script_cs.coffee
│   ├── dart
│   │   └── script_dart.dart
│   ├── javascript
│   │   └── script_js.js
│   ├── purescript
│   │   └── script_ps.purs
│   └── typescript
│       └── script_ts.ts
└── tsconfig.json

環境構築

  • TypeScript
    • npm install -g typescript
  • CoffeeScript
    • npm install -g coffeescript
  • npmプロジェクトの初期化
    • npm init
  • PureScript
    • npm install -D esbuild purescript spago@next
    • npx spago init
  • Dart
    • Flutterをインストールしたときに入っていたものを使いました。

実装

基本的にはES Modulesを使い、AltJS側でexportしてJavaScriptでimportします。
Dartだけはやり方がわからなかったため、Dartで定義した関数をJavaScriptからグローバルスコープで呼び出しました。

TypeScript

src/typescript/script_ts.ts
export function hello_ts(arg: string): void {
  console.log(`Hello from TypeScript! ${arg}!`);
}

CoffeeScript

src/coffeescript/script_cs.coffee
export hello_coffee = (arg='anonymous') ->
  console.log "Hello from CoffeeScript! #{arg}!"

PureScript

src/purescript/script_ps.purs
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)

hello_ps :: String -> Effect Unit
hello_ps arg = do
  log ("Hello from PureScript! " <> arg <> "!")

Dart

src/dart/script_dart.dart
import 'dart:js';

void hello_dart(String arg) {
  print('Hello from Dart! $arg!');
}

void main() {
  context['hello_dart'] = allowInterop(hello_dart);
}

JavaScript

src/javascript/script_js.js
import { hello_ts } from '../../build/typescript/script_ts.js';
import { hello_coffee } from "../../build/coffeescript/script_cs.js";
import { hello_ps } from '../../build/purescript/Main/index.js';

console.log('Hello from JavaScript! Call by js');
hello_ts('Call by js');
hello_coffee('Call by js');
hello_ps('Call by js')();
hello_dart('Call by js');

HTML

index.html
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <script src="./build/dart/script_dart.js"></script>
    <script src="./src/javascript/script_js.js" type="module"></script>
  </body>
</html>

ビルド

TypeScript用のtsconfig.jsonを作っておきます

tsconfig.json
{
  "compilerOptions": {
    "target": "ES6",
    "module": "ESNext",
    "outDir": "./build/typescript",
    "rootDir": "./src/typescript",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  },
  "include": ["src/typescript"]
}

PureScript用にspago.yamlを編集します

spago.yaml
package:
# 省略
  bundle:
    minify: true
    module: Main
    outfile: "build/purescript/script_ps.js"
    platform: browser
    type: "module"
workspace:
# 省略
  buildOpts:
    output: "build/purescript"

ビルドのコマンド

  • TypeScript
    • tsc
  • CoffeeScript
    • coffee -c -o ./build/coffeescript ./src/coffeescript
  • PureScript
    • npx spago build
  • Dart
    • dart compile js ./src/dart/script_dart.dart -o build/dart/script_dart.js

実行

プロジェクトのルートディレクトリでローカルサーバーを立ててアクセスし、開発者ツールを開くとconsoleに以下の出力が表示されます!
それぞれの言語で実装した関数を呼び出すことができました。

Hello from JavaScript! Call by js!
Hello from TypeScript! Call by js!
Hello from CoffeeScript! Call by js!
Hello from PureScript! Call by js!
Hello from Dart! Call by js!

※ 単にindex.htmlをブラウザで開くだけではCORSエラーが発生してうまくいかないので注意してください。
pythonを使うと簡単にローカルサーバーを立てられます。
参考: pythonでローカルwebサーバを立ち上げる #Python - Qiita

まとめ

今回は複数のAltJSを強引に使って扉100個のモンティ・ホール問題を実装しました。
ゲームに関しては開発途中のテストプレイで何度かプレイしましたが1度目であたりを選べた回数は0でした。なかなかクソゲーかと思います。
実装については不安しかない構成でしたが、生成AIサービスのおかげでなんとか環境構築やコードを書き進めることができました。便利な時代です。
関数型言語の経験がなくPureScriptを活かしきれなかったことは少々心残りです。
これを機に関数型言語も勉強してみようかと思います。

20
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?