1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

バックエンドなしで「毎日更新パズルゲーム」を作った話(450問自動生成・PWA対応・テスト2,600件)

1
Posted at

バックエンドなしで「毎日更新パズルゲーム」を作った話(450問自動生成・PWA対応・テスト2,600件)

作ったもの

定時退社タンゴhttps://teiji-tango.com)

👔と🍺を6×6のグリッドに並べる、毎日更新の論理パズルゲームです。

ルールはシンプルです。

  • 各行・各列に👔と🍺がちょうど3個ずつ
  • 同じ記号が3連続してはいけない
  • セル間の「=」「×」制約を満たす

「定時退社できた!」という達成感をゲームにしたくて作りました。残業も飲み会も、3連続したらNG。

gameplay.png

なぜ作ったか

LinkedInが公開している「Tango」というパズルゲームがあります。毎日1問、論理パズルを解くゲームで、Wordleと同じ「今日の問題」フォーマットです。

これが面白くて毎日やっていたんですが、ある日ふと思いました。

「👔と🍺に置き換えたら、完全に日本のサラリーマンゲームになるのでは」

3連残業も3連飲み会もNG。バランスよく定時退社する。ゲームのルールがそのままメッセージになる。

作るしかないと思いました。

技術構成

フロントエンド: 素のHTML + CSS + JavaScript(フレームワークなし)
バックエンド: なし
データベース: なし(localStorage のみ)
ホスティング: GitHub Pages(無料)
ビルドツール: なし(静的ファイルそのままデプロイ)

意図的にバックエンドを作りませんでした。

毎日更新のパズルは、日付から決定論的に問題を選べます。(日付のオフセット) mod 30 でプールから選ぶだけ。サーバーは不要です。ユーザーの進捗はlocalStorageに保存します。

Wordleも最初はサーバーなしの静的サイトでした。

一番大変だったこと:パズル自動生成

450問以上のパズルを作る必要がありました(5モード × 3難易度 × 30問)。手動では無理なので自動生成スクリプトを作りました。

ただし、パズルには厳しい条件があります。

  1. 唯一解であること(答えが1つだけ)
  2. 演繹のみで解けること(仮置きが不要)
  3. 難易度が正確であること(初級は初級の手筋だけで解ける)

この3つを満たすパズルを自動生成するのが、思ったより難しかったです。

ソルバーの実装

パズルが「演繹のみで解けるか」を判定するために、9種類の解法パターンを実装しました。

// ① バランス完了:行/列に3個揃ったら残りは全て逆
// [👔 🍺 _ _ 👔 👔] → 👔が3個 → [👔 🍺 🍺 🍺 👔 👔]

// ② ダブルブロック:2連続の前後は逆
// [👔 👔 _ ...] → [👔 👔 🍺 ...]

// ③ サンドイッチ:同じ記号に挟まれた中間は逆
// [👔 _ 👔] → [👔 🍺 👔]

// ④ 制約直接:= や × の片側が確定したらもう片方も確定

// ⑤ 端点バランス:端の2連続から反対端を確定
// [👔 👔 _ _ _ _] → 反対端は必ず🍺(背理法で証明)

これらのパターンを繰り返し適用して、変化がなくなったときに全マスが埋まっていれば「演繹のみで解けるパズル」と判定します。

「端点バランス(⑤)」は特に面白いパターンです。

[👔 👔 _ _ _ _]

「もし反対端が👔なら:👔は3個(位置0,1,5)」
「バランスより残り3セル(位置2,3,4)は全て🍺」
「位置2はダブルブロックで既に🍺確定」
「→ 位置2,3,4が🍺🍺🍺 = 3連続違反」
「矛盾 → 反対端は🍺に確定」

単純なパターンマッチングではなく、2段階の背理法推論です。これを知らないと「論理的に解けない」と感じてしまうパズルが、実は演繹で解けます。

生成アルゴリズム

1. ランダムな完成盤面を生成(バックトラッキング)
2. マスを1つずつ消していく
3. 消したあとにソルバーを走らせる
4. まだ唯一解かつ演繹で解けるなら、次のマスを消す
5. 解けなくなったら1つ前の状態が最終パズル
6. 使った手筋のセットで難易度を自動判定

このプロセスを1パズルあたり数十〜数百回試行して、条件を満たすものだけを採用しています。

5種類のゲームモード

通常モード以外に4種類追加しました。

壁ありモード 🧱

グリッドに壁を設置し、壁をまたいで3連続は無効になります。空間の分断がパズルの複雑さを増します。

エリアモード 🗂️

6×6グリッドを6つのエリアに分割。各エリアにも3個ずつのルールが加わります。

Xタンゴモード ✕

両対角線にも同じルールが適用されます。9方向(行×6、列×6、対角線×2)全てを同時に満たす必要があります。

キラータンゴモード 🔪

Killer Sudokuのように、セルがケージ(グループ)に分割されます。各ケージ内の🍺の数が表示され、それを満たす必要があります。初期配置(ヒント)がゼロで、全マスが空白からスタートします。

シェア機能:Wordleと同じ仕組み

クリア後に絵文字グリッドをシェアできます。

定時退社タンゴ Day 4(通常・初級)
⏱ 1:47 | ヒント0回

👔🍺👔🍺👔🍺
🍺👔🍺👔🍺👔
👔🍺👔🍺👔🍺
🍺👔🍺👔🍺👔
👔🍺👔🍺👔🍺
🍺👔🍺👔🍺👔

#定時退社タンゴ
https://teiji-tango.com

Wordleが爆発的に広がったのは、この絵文字グリッドがタイムラインに流れて「なんだこれ?」と気になって踏むからです。同じ仕組みを最初から実装しました。

PWA対応

manifest.json と Service Worker を実装し、ホーム画面に追加するとネイティブアプリのように動きます。オフラインでもプレイ可能です。

// sw.js: キャッシュ戦略
const CACHE_NAME = 'teiji-tango-v19';
const ASSETS = ['/', '/index.html', '/style.css', '/js/app.js', ...];

self.addEventListener('install', e => {
  e.waitUntil(caches.open(CACHE_NAME).then(c => c.addAll(ASSETS)));
});

テストについて

ゲームロジックが複雑なため、テストを重視しました。

tests/
├── unit/
│   ├── game.test.js      # TangoGame ユニットテスト
│   ├── solver.test.js    # TangoSolver ユニットテスト
│   └── puzzles.test.js   # パズルデータ整合性テスト(全450問)
└── integration/
    └── puzzle-flow.test.js  # 実績・ゲームフロー統合テスト

パズルデータの整合性テスト(唯一解チェック)が全450問に対して走るので、テスト数は2,600件を超えています。

$ npm test
PASS tests/unit/game.test.js
PASS tests/unit/solver.test.js  
PASS tests/unit/puzzles.test.js
PASS tests/integration/puzzle-flow.test.js

Test Suites: 4 passed
Tests:       2603 passed

パズルデータを変更するたびにCIで整合性が保証されます。

やってみてわかったこと

「バックエンドなし」は制約ではなく武器でした。

  • デプロイが静的ファイルのアップロードだけ
  • サーバー費用ゼロ
  • レスポンスが速い(CDNから直配信)
  • メンテナンスコストがほぼゼロ

毎日更新のコンテンツを作るなら、日付ベースの決定論的な問題選択で十分です。ユーザーごとの状態はlocalStorageで管理できます。「サーバーが必要かどうか」を最初に疑うことで、シンプルな構成になりました。

遊んでみてください

https://teiji-tango.com

インストール不要、無料、毎日新しい問題が更新されます。

上級のキラータンゴモードはかなり手強いです。ノーヒントでクリアできたらぜひXで教えてください。

#定時退社タンゴ


もし実装について気になることがあればコメントください。ソルバーのロジックや自動生成アルゴリズムの詳細も書けます。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?