슬롯머신 로그라이크. 릴을 굴려 나온 심볼(elements)의 시너지로 산소를 만들고, 구역이 끝날 때 생명유지장치가 산소를 가져간다. 못 대면 표류 — 얼마나 멀리 갔나가 기록.
의존성 없음. index.html 하나 열면 끝.
open index.html| 종류 | 로그라이크 · 싱글 |
| 인원 | 1인 |
| 플레이 | https://41ways.github.io/oxy-voyage/ |
| 로컬 실행 | open index.html |
| 한 줄 규칙 | 릴을 굴려 심볼 시너지로 산소를 만들고, 구역마다 생명유지장치에 산소를 바친다 |
| 허브 | https://41ways.github.io/norara/ |
목차 — 파일 · 심볼 추가하는 법 · 정족수 — 고점 덱의 진입장벽 · 사람과 봇을 나눠서 잰다 · 밸런스 (v0.7 · 900판) · 현재 상태 (v0.5)
| 파일 | 하는 일 |
|---|---|
index.html |
화면, 연출, 진행. 규칙은 안 들고 있음 |
engine.js |
심볼 테이블 + 점수 판정 + 소모량 곡선. DOM 안 씀 |
sim/balance.js |
engine.js를 그대로 불러다 수천 판 돌리는 밸런스 시뮬레이터 |
게임과 시뮬레이터가 같은 engine.js를 쓴다. "돌려본 수치"와 "실제로 도는 게임"이 어긋날 수 없게 하려고 이렇게 나눴다.
node sim/balance.js 250engine.js의 SYMBOLS에 항목 하나 붙이면 뽑기 목록·툴팁·화물칸에 자동으로 붙는다.
solar:{ e:'☀️', n:'태양광 패널', base:0, r:'common', d:'인접한 심볼이 서로 전부 다르면 +5',
effect:function(c){ var a=c.adj(); if(!a.length) return;
var ids={}, n=0;
for(var i=0;i<a.length;i++){ var id=a[i].entry.id; if(!ids[id]){ ids[id]=1; n++; } }
if(n===a.length) c.addSelf(5); } },필드
e이모지 /n이름 /base기본값 /r등급(commonuncommonrarelegend) /d설명baseOf(entry)기본값이 인스턴스마다 다를 때 (수경재배처럼 자라는 것)badge(entry)칸 좌상단에 띄울 짧은 글자 (남은 턴, 누적치)costCut화물칸에 들고만 있어도 구역 소모량이 깎임sticky정비로 버릴 때 이 확률로 안 떨어짐noOffer뽑기 목록에 안 뜸 (부화·변신으로만 얻는 것)
effect(c)에서 쓸 수 있는 것
| 호출 | 설명 |
|---|---|
c.self c.cells c.deck |
지금 칸 / 판 전체 / 화물칸 |
c.adj(...ids) |
인접 8칸 (id 주면 필터) |
c.all/row/col(...ids) |
판 전체 / 같은 행 / 같은 열 |
c.emptyAdj() |
인접한 빈칸 수 |
c.addSelf(n) c.add(t,n) c.mul(t,m) |
값 조작. mul은 한 칸당 스핀 1회만 먹음 |
c.kill(t) |
파괴 (화물칸에서도 영구 제거) |
c.morph(t,id) |
다른 심볼로 변신 |
c.gain(n) |
칸과 무관하게 산소 획득 |
c.spinMul(m) |
이번 스핀 총합에 배수 (전체 천장 3배) |
c.debt({mul,add}) |
다음 구역 소모량에 빚을 남김 |
c.deckAdd(id) |
화물칸에 심볼 추가 |
c.link(t) |
연출용으로 칸을 묶음 (조회 계열은 자동) |
c.rand() c.note(msg) |
난수, 항해일지 한 줄 |
조회 계열이 돌려준 칸과 add/mul/kill의 대상은 자동으로 연출 링크에 묶인다.
단 값이 실제로 움직였을 때만 확정된다 — 조회만 하고 조건이 안 맞아 아무 일도
안 일어난 칸까지 빛나면 왜 빛나는지 알 수가 없어서.
효과는 왼쪽 위 칸부터 순서대로 실행된다. 그래서 먼저 죽으면 효과를 못 쓴다.
고점을 노리는 심볼은 화물칸에 일정 장수가 모여야 제 값이 나온다. 그 전엔 있으나 마나여서, "센 거 서너 개만 얹어놓고 안정형으로 가기"가 손해가 된다. 안정형은 안정형대로, 고점 덱은 한 축에 몰빵해야 성립한다.
| 심볼 | 등급 | 정족수 (안정 축 수준) | 완성 (고점이 뚫림) |
|---|---|---|---|
| 🌌 웜홀 | 희귀 | 6장 → 2.2% | 11장 → 6% |
| ☢️ 반응로 | 희귀 | 5장 → 인접 2배 | 9장 → 2배 +9씩 |
| 🧠 AI 코어 | 전설 | 3장 → 인접당 +5 | 5장 → +14 |
| 🩸 비상 배급 | 전설 | 3장 → +400 | 5장 → +1100 |
| ⚛️ 임계 반응 | 전설 | 2장 → 2배 | 4장 → 3배 |
| 🦑 우주 크라켄 | 전설 | 2장 → 기본값 3배 | 4장 → 6배 |
| 🗝️ 마스터 코드 | 전설 | 2장 → 전부 개방 | 4장 → 개당 140 |
| ✨ 초공간 정렬 | 전설 | 2장 → 순도 +4 | 4장 → +7 |
| 🌈 스펙트럼 | 전설 | 2장 → 12종 | 4장 → 9종 |
정족수는 등급별로 한 판에 실제 몇 장이 손에 들어오는지에 맞췄다. 처음엔 전설에 611장을
걸었는데, 전설은 아무리 모아도 35장이라 도달 자체가 불가능한 벽이었다.
시뮬레이터가 셋이다. 셋이 재는 게 다르다.
| 파일 | 두는 방식 | 재는 것 |
|---|---|---|
sim/balance.js |
고정 선호표 — 축 하나만 기계처럼 집음 | 축별 상한과 통과율 |
sim/strategist.js |
전방탐색 — 후보를 실제로 6스핀 굴려보고 고름 | 최적에 가까운 플레이의 천장 |
sim/human.js |
전방탐색 없음 — 카드를 읽고 어림으로 고름 | 실제로 사람이 얻는 점수 |
봇과 사람의 차이가 핵심이다. 봇은 당장의 기댓값을 정확히 재지만, 그래서
정족수 심볼을 영영 못 고른다 — 문턱을 넘기 전엔 눈앞의 값이 나쁘니까(국소 최적 함정).
사람은 정확히 못 재는 대신 카드에 적힌 문턱을 읽고 계획을 밀고 나간다.
sim/human.js는 그 비대칭을 그대로 넣었다.
- 전방탐색 없음. 기본값 · 짝이 덱에 있나 · 이미 몇 장 모았나로 어림
- 설명에 세 자리 수가 보이면 과대평가한다 (성향에 따라 1.0 ~ 2.2배)
- 마이너스 기본값을 꺼린다
- 문턱이 적힌 카드는 한 장이라도 있으면 목표로 삼는다
- 한 번 정한 축을 웬만해선 안 바꾼다 (구역마다 12%만 재고)
- 같은 후보도 판단이 ±30% 흔들린다
| 성향 | 중앙 구역 | 중앙 점수 | 상위 25% | 상위 5% | 최고 | 정족수 | 완성 |
|---|---|---|---|---|---|---|---|
| 🙂 안정 지향 | 10구역 | 3,308 | 4,180 | 5,043 | 9,240 | 5% | 0% |
| 🔥 고점 지향 | 10구역 | 3,513 | 4,831 | 8,441 | 216,168 | 2% | 1% |
평균은 사실상 동률인데 천장이 23배 갈린다. 안정 지향은 9,240에서 멈추고, 고점 지향은 정족수를 채운 판에서 216,168까지 간다.
봇(strategist.js)으로 재면 신중형 중앙 2,919 / 최고 161,198,
대깨형 중앙 2,234 / 상위 5% 124,087 — 중앙값은 신중이 높고 천장은 대깨가 5.7배다.
판 하나가 40~50픽인데 특정 희귀는 그중 두세 번 뜨는 게 고작이다. 그대로 두면 정족수는 영영 못 넘는 벽이라, 화물칸에 쌓아둔 문턱 심볼은 훨씬 자주 나오게 했다.
가중치 = 등급 가중치 × (1 + 편향 × min(보유 장수, 7.5))
일반 심볼 편향 0.4
문턱 심볼 편향 5.0
1.2 / 2.5 / 4 / 5 / 7 을 재봤고 5.0을 골랐다 — 정족수 도달 11%, 완성 5%로 "드물지만 노려볼 만한" 선이 나온다. 더 낮추면 문턱이 장식이 되고, 더 올리면 누구나 완성해서 고점의 의미가 없어진다.
"구역별 탈락률을 일정하게"는 소모량을 그 구역 정산 직전 산소 분포의 같은 백분위에 놓는 문제다. 소모량 = p15면 정확히 15%가 떨어진다. 남은 산소가 다음 구역으로 이월돼 자기참조라, 다섯 번 수렴시킨다.
node sim/fit-costs.js 15 220 5| 구역 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 28 |
|---|---|---|---|---|---|---|---|---|---|
| 탈락률 | 16% | 15% | 14% | 13% | 16% | 15% | 14% | 17% | 21% |
심볼을 늘리거나 선택지 수를 바꾸면 수입 분포가 통째로 바뀌므로 반드시 다시 맞춘다.
- 비상 배급의 되먹임 — "이번 구역 소모량의 N%를 획득"으로 뒀더니 수입이 소모량과 같이 지수로 커져서 게임이 끝나질 않았다(41구역 상한 도달). 자기참조를 끊고 고정값으로.
- 배수 겹침 — 반응로 ×2가 여러 장 겹치면 한 칸이 2ⁿ이 된다. 어떤 소모량 곡선으로도 못 막아서, 한 칸은 스핀당 한 번만 배수를 먹게 캡을 걸었다.
- 희귀도 램프 상한 — 구역이 깊어질수록 희귀·전설이 잘 나오게 했는데, 상한이 없으면 후반 덱이 전부 희귀가 되어 복리가 안 멈춘다.
- 조합이 성립할 확률 — 소각로·만능공구가 "인접"만 볼 땐 채택률이 0.4장이었다. 사거리를 넓히고 나서야 원형으로 굴러갔다.
- 공짜로 커지는 심볼 — 미세 블랙홀이 인접 1개당 무조건 3을 빼고 6을 얻었다. 옆칸이 뭐든 +3이라 인접 수만 채우면 되는 구조. 기준을 대상의 기본값으로 바꿨다.
- 판 밀도를 고려 안 한 페널티 — 곰팡이 기본값 8에 인접 −3을 걸었더니, 판 평균 인접이 3.4칸이라 실질 −2.2였다. 숫자만 보면 이득처럼 보이는데 실제론 손해.
판에서 가장 많이 깔린 한 종류의 비율로 스핀 총합에 배수가 붙는다 (60% ×1.15 → 100% ×3.0). 통일 덱이 최고 고점이고, 동시에 곁가지 픽 하나하나에 눈에 보이는 대가가 붙는다. 통일을 실제로 도달 가능하게 만드는 두 심볼이 🧫 배양조(가장 많은 종류를 복제)와 🗑️ 폐기 슈트(소수파를 영구 폐기)다.
정반대 축도 있다 — 🔭 관측 망원경은 판의 종류 수로 값을 내고, 🌈 스펙트럼은 12가지 이상이면 총합을 2배로 만든다. 순도를 포기해야 성립한다.
- 심볼 59종 (흔함 17 / 보통 23 / 희귀 12 / 전설 7, 변신 전용 포함)
- 스핀 5회 고정 · 선택지 4장 · 구역 30개 표 + 이후 1.25배씩
- 계열: 순도 / 다양성 / 우주 동물(🐁🐈⬛🐕🐢) / 외계(🥚👾👽🦑) / 통신(📡📻📶🖥️) / 오염(🦠🔥🧰🧯) / 밀수(📦🛠️🪝🗝️) / 온실(🌱🌷🌸) / 광맥(💎⛏️🕳️) / 증폭(☢️🧊) / 저당(⚛️🩸🎫)
- 아이템·유물 없음, 기록은 localStorage