OhMyGame
OhMyGame은 AI로 게임을 만들 수 있는 오픈소스 스튜디오로, 디자인·구축·플레이테스트·출판을 하나의 앱에서 모델 키와 함께 수행합니다
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A small collection of browser games, hosted on GitHub Pages. Pick a game from the home page and play — no install needed.
?seed=1234 plays that deal again (&level=hard picks the level). The rules are in solitaire/klondike.js, tested in Node (npm run test:solitaire).house/README.md..
├── index.html # home page — links to each game
├── manifest.json # the home page on a phone's Home Screen
├── icons/ # its icons (each game has its own icons/ too)
├── tools/ # make-icons.mjs draws the Home Screen icons
├── dice/
│ ├── index.html
│ ├── script.js
│ ├── style.css
│ ├── manifest.json
│ └── sw.js
├── snake/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── tictactoe/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── hangman/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── wordle/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── wordsearch/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── connect4/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── shared/ # online play on two phones for Connect 4 and Tic-Tac-Toe
│ ├── firebase.js # the database: plain REST and streaming, anonymous sign-in
│ └── duel.js # rooms, codes, turns and the start/join panel
├── solitaire/
│ ├── index.html
│ ├── klondike.js # the rules, no page (tested in Node)
│ ├── manifest.json
│ └── sw.js
├── uno/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── soccer/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── soccer5v5/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── battleships/ # multi-file: js/ (rules, game, ai, scene/), css/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
├── house/ # multi-file: js/ (world, characters, jobs, systems), css/
│ ├── index.html
│ ├── manifest.json
│ └── sw.js
└── tests/
├── e2e/
│ ├── dice.spec.js
│ ├── snake.spec.js
│ ├── tictactoe.spec.js
│ ├── hangman.spec.js
│ ├── wordle.spec.js
│ ├── wordsearch.spec.js
│ ├── connect4.spec.js
│ ├── battleships.spec.js
│ ├── battleships-online.spec.js
│ ├── solitaire.spec.js
│ ├── uno.spec.js
│ ├── soccer.spec.js
│ ├── soccer5v5.spec.js
│ └── house.spec.js
├── battleships/ # Node tests of the Battleships rules, computer players and two-device games
├── duels/ # rules-live.mjs: the two-phone rules checked against the real database (by hand)
├── solitaire/ # Node test of the Klondike rules
└── house/ # Node play-through test for Ford House (no browser)
Each game lives in its own folder and is fully self-contained — no shared dependencies between games (Ford House and Battleships load three.js from a CDN, cached for offline play by its service worker). Every game has a manifest.json + sw.js for offline support (installable, and playable after the first visit with no connection).
wordle/)index.html as the entry pointmanifest.json and sw.js for offline support (copy an existing game's as a template)index.html at the root, linking to <folder-name>/Backed by a Firebase Realtime Database, accessed via plain REST fetch() calls rather than the Firebase SDK (the SDK's realtime sync layer proved unreliable in testing). Scores are capped at 50 stored entries — the leaderboard prunes down to the top 50 automatically after each save — and only the top 5 are displayed. There's no polling; the display refreshes on page load, after a save, or via the manual refresh button.
All three let anyone add a word to a shared pool, stored in the same Firebase database as the leaderboard (different top-level nodes: customWords for hangman, wordleWords/easy and wordleWords/hard for wordle, wordSearchWords for word search). Each word is stored using the word itself as the key rather than a generated ID — this makes Firebase's own rules reject duplicate/simultaneous additions automatically, with no risk of a race condition even if two devices add the same word at once. Custom words load in the background on page load and are available starting with the next new word/game/puzzle — no need to wait on a network call before playing. There's no profanity filtering; since the site is public, anyone who can reach the page could technically add an inappropriate word.
Classic rules: a 10×10 grid, a fleet of five (Carrier 5, Battleship 4, Cruiser 3, Submarine 3, Destroyer 2), ships may not touch (not even at the corners), and you're told when you've sunk a ship (and which). Turns alternate.
battleships/js/
rules.js the grid, the fleet, placing, firing: no three.js, runs in Node
game.js one game as plain JSON: players, fleets, shots, whose turn (saved under battleships.save.v1)
ai.js the computer: Easy (random), Normal (hunts, then follows a hit along the line),
Hard (counts every way the ships still afloat could lie and fires where that's highest)
main.js the menu, placing, turns, the hand-off screen, the end of the game
scene/ the 3D world: stage (camera and lights), sea, boards, ships, effects
The computer only ever looks at its own shots and what they found, never at your fleet, and it keeps nothing
between turns, so a saved game carries on with the same computer. On average it sinks a fleet in about 75 shots
on Easy, 50 on Normal and 39 on Hard (npm run test:battleships plays 200 games at each level and checks they
stay in that order).
Add ?seed=42 to the address to make the fleets and the computer play the same way each time, and ?speed=4 to
run the animations faster.
Games between two phones go through their own Firebase project (ford-battleships, Realtime Database in
europe-west1), separate from the database the other games share. As with the others there's no Firebase SDK:
js/net/firebase.js uses plain REST calls, and the database's streaming (EventSource) to hear about changes as
they happen. Each device signs in anonymously once (Firebase Authentication over REST) and keeps that sign-in, so
after a reload it's the same player and takes its seat again.
A game is a room under rooms/<code> (js/net/online.js describes it): the two players, n shots fired so
far (even numbers are the starter's), the shots, and the answers. The fleets never go into the database. Each
device keeps its own and puts up only a fingerprint (SHA-256 of the fleet and a random salt). The player who is
fired at works out hit, miss or sunk on their own device and writes the answer; a shot plays out on both screens
once it's answered. At the end both fleets are revealed and each device checks the other one against its
fingerprint and every answer it gave, so a wrong answer shows up on the game over card.
The security rules (battleships/database.rules.json, pasted into the Firebase console by hand) make everyone
take turns: only signed-in players, a seat can't be taken twice, only the player whose turn it is can fire,
only the player fired at can answer, nothing can be changed once written, and every field is checked. Rooms
over a day old may be deleted (by hand, in the console). After changing the rules, run
node tests/battleships/rules-live.mjs: three throwaway players try 47 things against the real database, allowed
and not. It isn't part of CI, since it needs the real database.
The same database holds two-phone games of Connect 4 and Tic-Tac-Toe, under duels/<code>, using
shared/firebase.js (a copy of Battleships' database code) and shared/duel.js. A game holds the two players,
the round, and the moves (a column or a square each); each phone works out the board from the moves, so a reload
or a dropped connection just picks up again. The player who starts is Red (or X), the one who joins Yellow (or
O), and the rounds take turns going first. The pages load shared/duel.js only when 📱 Two phones is picked, so
they still work offline; without the internet that option is greyed out and the game is played on one device.
The rules for duels/ are in the same rules file: only the two players, one move at a time on their own turn,
no changing a move. After changing them, run node tests/duels/rules-live.mjs (41 checks against the real
database).
A proper 6-team single round-robin: you plus 5 original opponent teams (own names/colors, no real clubs — Sunset Rovers, Meadowlark Athletic, River Town, Northfield United, Ironclad FC), 15 fixtures across 5 rounds, every team playing every other exactly once.
You play your own 5 matches for real. The other 10 (AI vs AI) are simulated with a weighted random model: each team's expected goals scale with the strength gap between the two sides, sampled from a Poisson distribution so scorelines look like real football (mostly 0–3 goals, blowouts rare) and upsets genuinely happen — a weak team beats a strong one roughly 8% of the time rather than never. The two AI matches from a round are simulated when you complete your own fixture from that round, so the whole table progresses together and every team always shows the same number played.
AI difficulty is entirely data — the same decision logic runs for every opponent, just tuned differently per skill tier (easy/medium/hard: AI speed, shooting range, chase radius, reaction time, and formation tightness all scale together). Standings are a real league table (P/W/D/L/GF/GA/GD/Pts), sorted by points, then goal difference, then goals scored — so you can finish mid-table on goal difference exactly as in reality.
Saved to localStorage under ford5v5LeagueSeason as plain JSON (per-fixture scorelines plus which rounds are complete), deliberately simple so a future move to a persistent, shared backend (e.g. Firebase, following the same pattern as the leaderboard/word lists) — with a transfer market, budget, and squad-building career mode — could swap out how it's read and written without redesigning what it stores.
End-to-end tests live in tests/e2e/, run with Playwright:
npm install
npm run test:e2e
playwright.config.js starts a local static server automatically before running. Snake's tests mock the Firebase REST calls, so they never depend on network access or write to the live database. CI runs these on every push/PR via .github/workflows/ci.yml.
Battleships has Node tests of its rules, computer players and two-device games (npm run test:battleships); its
two-device browser tests play two browser contexts against each other through a stand-in for the database.
Ford House also has a play-through test in tests/house/ that runs in plain Node against a stand-in for three.js (npm run test:house) — see house/README.md.
No build step — just open index.html in a browser, or serve the folder with any static file server (npm run serve uses the same serve package the tests use).
Every game and the home page has an icons/ folder for saving it to a phone's Home Screen:
apple-touch-icon.png for iOS (linked from index.html) and icon-192.png and icon-512.png for Android
(listed in manifest.json). They're drawn by tools/make-icons.mjs: change a symbol there, run
npm run icons, commit the PNGs and bump that game's CACHE_NAME in sw.js. iOS keeps the old icon until the
tile is deleted and saved again.
Served via GitHub Pages from the main branch, root folder.
The current version is the version in package.json; the Game Zone page shows it at the bottom.
CHANGELOG.md lists what changed in each version, newest first.
## [Unreleased] in CHANGELOG.md in the same pull request.develop:
CHANGELOG.md, rename ## [Unreleased] to ## [x.y.z] - YYYY-MM-DD, add a fresh empty
## [Unreleased] above it, and update the compare links at the bottom."version" in package.json (and the two matching lines at the top of package-lock.json).develop into main titled with the version; merge it once CI passes.vx.y.z on main, title vx.y.z, and paste that version's
section from CHANGELOG.md as the notes.A test (tests/e2e/version.spec.js) checks that package.json, the changelog and the Game Zone page
agree on the version.