잠깐 딴짓하기 좋은 게임 모음 — 오목 · 장기 · 리버시 · 윷놀이 · 스도쿠 · 지뢰찾기 · 노노그램 · 솔리테어 · 테트리스 · 벽돌깨기 · 2048 · 꼬들
https://yjworks.github.io/games/
서버도 계정도 없습니다. 진행 상황과 기록은 브라우저(localStorage)에만 저장되고, 한 번 열어 본 뒤에는 네트워크 없이도 실행됩니다(PWA).
/ 게임 네비게이터(허브)
/games/omok/ 오목 /games/minesweeper/ 지뢰찾기 /games/tetris/ 테트리스
/games/janggi/ 장기 /games/nonogram/ 노노그램 /games/breakout/ 벽돌깨기
/games/reversi/ 리버시 /games/solitaire/ 솔리테어 /games/2048/ 2048
/games/yut/ 윷놀이 /games/sudoku/ 스도쿠 /games/kkodle/ 꼬들
허브에서 분류(보드 · 퍼즐 · 액션 · 데일리)로 걸러 볼 수 있고, 키보드가 있으면 숫자키로 바로 들어갑니다
(숫자키·방향키 안내는 마우스·키보드가 있는 기기에서만 보이고, 터치 화면에서는 숨깁니다 — base.css의 .kbd-only/.touch-only).
| 게임 | 분류 | 특징 |
|---|---|---|
| 오목 | 보드 | 15×15, 좌표 표시, 착수 미리보기, 승리 라인 강조. AI는 즉승/즉방 + 열린형태 평가로 최대 10수를 읽습니다(1.6초). |
| 장기 | 보드 | 차림 4종·진영 선택, 점수제(한 덤 1.5), 기보. AI는 MVV-LVA 정렬 + 정지탐색으로 최대 5수(2.5초). |
| 리버시 | 보드 | 착수 가능 위치 표시, 모서리·기동력·확정석 평가, 종반 16칸 완전탐색(2.5초). |
| 스도쿠 | 퍼즐 | 유일해 보장 생성, 메모·자동메모·되돌리기·힌트, 실수 제한. |
| 지뢰찾기 | 퍼즐 | 추측이 필요 없는 판만 출제 — 생성한 배치를 솔버로 끝까지 풀어 보고 통과한 것만 내보냅니다. |
| 노노그램 | 퍼즐 | 줄 논리만으로 풀리는 문제만 출제 — 유일해가 보장됩니다. 밀어서 칠하기, 되돌리기. |
| 솔리테어 | 퍼즐 | 클론다이크. 한 장/세 장 뽑기, 두 번 눌러 자동 이동, 되돌리기, 자동 완성. |
| 테트리스 | 액션 | SRS 회전 + 월킥, 7-bag, 홀드, 고스트, 락 딜레이, T-스핀/백투백/콤보. |
| 윷놀이 | 보드 | 도개걸윷모와 뒤도, 지름길·업기·잡기까지. 모서리에 정확히 멈춰야 지름길을 탑니다. AI 3단계와 2인 대국. |
| 벽돌깨기 | 액션 | 패들 어디에 맞느냐로 각이 갈리고, 빠른 공도 벽돌을 뚫지 않습니다. 아이템 3종과 판마다 다른 배치. |
| 2048 | 액션 | 타일이 미끄러지는 연출, 되돌리기, 최고 타일 기록, 2048 이후 계속하기. |
| 꼬들 | 데일리 | 두 글자 한글 낱말을 자모 단위로 채점. 하루 한 문제 + 무한 모드, 연속 일수 기록. IME 없이도 되는 한글 조합 자판 내장. |
오늘의 문제는 꼬들·스도쿠·노노그램·지뢰찾기 네 곳에 있습니다. 서버가 없으므로 기기 날짜를 시드로 삼아 만들며, 같은 날이면 누가 열어도 같은 문제가 나옵니다.
모든 게임에 튜토리얼(첫 방문 자동 · 이후 ? 키) 과 서사가 있고,
진행 중인 대국은 자동 저장되어 허브에서 “이어하기”로 돌아갈 수 있습니다.
모바일 퍼스트로 만들었습니다. 기본 레이아웃이 세로 화면 기준이고, 넓은 화면에서 사이드 패널이 열립니다.
터치 목표는 최소 44~52px, 하단 조작부는 env(safe-area-inset-bottom)을 반영합니다.
게임끼리 공유하는 코드는 src/shared/(공통 레이어)에 모여 있습니다.
| 모듈 | 역할 |
|---|---|
registry.ts |
게임 목록·메타데이터·서사. 허브와 각 게임이 같은 정의를 참조 |
storage.ts |
games:<gameId>:<key> 네임스페이스 localStorage 래퍼 (저장 실패해도 게임은 계속 동작) |
progress.ts |
“이어하기” 요약 — 허브 카드에 표시 |
records.ts |
최고 기록 (점수형/시간형 모두 지원) |
stats.ts |
AI 대전 전적 |
prefs.ts |
전역 설정(효과음) — 모든 게임이 공유 |
sound.ts |
WebAudio 합성 효과음. 오디오 파일 없음 |
tutorial.ts |
튜토리얼 자료형 + 최초 1회 자동 표시 여부 |
daily.ts |
날짜 시드로 정하는 오늘의 문제 + 연속 일수 기록 |
color.ts |
색 밝기·투명도 (캔버스 그러데이션용) |
react/GameShell.tsx |
공통 상단바(허브 링크·제목·도움말·효과음) + 튜토리얼 |
react/GameMark.tsx |
게임마다 하나씩 있는 표식(SVG). 허브 카드와 상단바가 같은 그림을 씁니다 |
react/useKeys.ts |
전역 단축키 |
dom/shell.ts, dom/tutorial.ts |
React를 쓰지 않는 게임(스도쿠)용 같은 기능 |
pwa.ts |
서비스 워커 등록 |
게임을 새로 추가하는 방법은 docs/ADDING_A_GAME.md, 외부 라이브러리(MediaPipe 등) 반입 규칙은 docs/THIRD_PARTY_ASSETS.md를 보세요.
npm install
npm run dev # 개발 서버 (허브: /, 게임: /games/<id>/)
npm test # 규칙 엔진 테스트 (vitest, 190여 개)
npm run typecheck
npm run build # dist/ 생성 (허브 + 게임 12개 + 서비스 워커)
npm run previewmain에 푸시하면 GitHub Actions가 빌드해 gh-pages 브랜치로 배포합니다.
base가 상대 경로(./)라서 계정 이름이나 저장소 이름이 바뀌어도 그대로 동작합니다.
서비스 워커의 경로도 전부 상대 경로입니다.
앱이 도는 데 필요한 주소는 매니페스트의 id(/games/) 하나뿐입니다(링크 미리보기용
canonical·og:url에는 https://dibrain.dev/games/...가 적혀 있어, 주소를 옮기면 같이 고칩니다). 앱 식별자는 설치된 앱을
가리키는 이름이라 고정돼 있어야 합니다 — 빼 두면 start_url에서 파생되는데, 그러면
start_url을 손대는 순간 다른 앱이 되어 홈 화면에 아이콘이 두 개 생깁니다.
/games/가 아닌 경로로 옮긴다면 이 값도 같이 옮겨야 합니다.
기록은 주소(오리진)에 매여 있습니다. 배포 주소를 옮기면 localStorage가 새로 시작하고 설치해 둔 PWA도 따로 잡힙니다 — 옮기기 전에 알아 둘 것.
- 서비스 워커가 빌드 산출물 전체를 미리 캐시합니다(
vite.config.ts의pwaPlugin). - 두 번째 방문부터는 네트워크 없이 실행되고, 홈 화면에 설치하면 앱처럼 열립니다.
- 외부 CDN을 쓰지 않습니다. 폰트·아이콘·효과음까지 전부 저장소 안에 있습니다.
- 글꼴 Pretendard는
public/vendor/fonts/에 있습니다.pretendard.css만 미리 캐시하고, 조각 파일(woff2 92개)은 화면에 필요한 것만 처음 쓸 때 받아games-fonts-v1캐시에 둡니다(다음부터 네트워크를 보지 않음). - 같은 주소(dibrain.dev)를 쓰는 다른 앱의 캐시는 건드리지 않습니다. 옛 캐시를 지울 때
games-로 시작하는 것만 지웁니다.