--- id: wiki-2026-0508-magic-sort title: "Magic Sort!" category: 10_Wiki/Topics status: verified canonical_id: self aliases: ["Magic Sort", "Sort Puzzle"] duplicate_of: none source_trust_level: B confidence_score: 0.85 verification_status: applied tags: [game-design, casual, puzzle, mobile, hypercasual, sort] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: design-pattern framework: casual-puzzle --- # Magic Sort! ## 매 한 줄 > **"매 colored items 의 tubes/containers 의 across 매 sort"**. Magic Sort! 매 2021-2024 hypercasual sort puzzle 의 representative title — 매 pour-bottle / ball-sort / nut-sort 의 lineage 의 따라 매 satisfying haptic + clear win-state 의 deliver. 2026 매 hybridcasual era 매 meta-progression + LiveOps 의 layered. ## 매 핵심 ### 매 Core Loop - **Pour mechanic**: 매 source tube 의 select → matching-color top portion 의 dest tube 의 transfer. - **Win condition**: 매 each tube 의 single color (or empty). - **Constraint**: 매 dest tube 의 top color 매 source top color 와 match (or empty). ### 매 Layers (hypercasual → hybridcasual) - 매 Layer 0: Pure puzzle (300+ levels). - 매 Layer 1: Power-ups (extra tube, undo, bomb). - 매 Layer 2: Meta-progression (themes, decoration unlock). - 매 Layer 3: LiveOps events (timed leagues, gacha skins). ### 매 응용 1. Sort puzzle blueprint — Water Sort, Ball Sort, Nut Sort 의 same family. 2. Hybridcasual reference — 매 2024-2026 evolution model. 3. Difficulty curve study — 매 procedural level generator 의 case. ## 💻 패턴 ### Tube state + pour move ```typescript type Tube = string[]; // bottom-to-top color stack function topRun(t: Tube): { color: string | null; count: number } { if (t.length === 0) return { color: null, count: 0 }; const c = t[t.length - 1]; let n = 0; for (let i = t.length - 1; i >= 0 && t[i] === c; i--) n++; return { color: c, count: n }; } function canPour(src: Tube, dst: Tube, capacity: number): boolean { const s = topRun(src); if (s.color === null) return false; if (dst.length === capacity) return false; if (dst.length === 0) return true; return dst[dst.length - 1] === s.color && dst.length + s.count <= capacity; } function pour(src: Tube, dst: Tube, capacity: number): [Tube, Tube] { const s = topRun(src); const space = capacity - dst.length; const moved = Math.min(s.count, space); return [ src.slice(0, src.length - moved), [...dst, ...Array(moved).fill(s.color)], ]; } ``` ### Solver (BFS — 매 level validity check) ```typescript function isSolved(tubes: Tube[], capacity: number): boolean { return tubes.every(t => t.length === 0 || (t.length === capacity && t.every(c => c === t[0])) ); } function solve(initial: Tube[], capacity: number, maxDepth = 200): number | null { const seen = new Set(); const queue: [Tube[], number][] = [[initial, 0]]; while (queue.length) { const [state, depth] = queue.shift()!; if (isSolved(state, capacity)) return depth; if (depth >= maxDepth) continue; const key = state.map(t => t.join(',')).join('|'); if (seen.has(key)) continue; seen.add(key); for (let i = 0; i < state.length; i++) { for (let j = 0; j < state.length; j++) { if (i !== j && canPour(state[i], state[j], capacity)) { const next = state.map(t => [...t]); [next[i], next[j]] = pour(next[i], next[j], capacity); queue.push([next, depth + 1]); } } } } return null; } ``` ### Procedural level generator ```typescript function genLevel(colors: number, capacity: number, extraTubes = 2): Tube[] { // 매 reverse generation: 매 solved state 의 from 매 random pours 의 unwind const solved: Tube[] = []; for (let c = 0; c < colors; c++) { solved.push(Array(capacity).fill(`c${c}`)); } for (let e = 0; e < extraTubes; e++) solved.push([]); let state = solved; for (let i = 0; i < 50 + colors * 10; i++) { const moves = legalReversePours(state, capacity); if (moves.length === 0) break; const m = moves[Math.floor(Math.random() * moves.length)]; state = applyReverse(state, m, capacity); } return state; } ``` ### Difficulty score (move-graph branching) ```typescript function difficulty(level: Tube[], capacity: number): number { const optimal = solve(level, capacity); if (optimal === null) return Infinity; const branchingFactor = avgLegalMoves(level, capacity); return optimal * Math.log2(branchingFactor + 1); } ``` ### Power-up: extra tube ```typescript function applyExtraTube(tubes: Tube[]): Tube[] { return [...tubes, []]; } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Hypercasual launch (CPI 의 minimize) | 매 pure mechanic + IAA 만 | | Hybridcasual evolution | 매 meta + battle pass + LiveOps 의 layer | | Brain-training positioning | 매 difficulty curve + procedural infinite levels | | IP collab opportunity | 매 themed skins + limited gacha | **기본값**: 매 2026 hybridcasual — pure mechanic + light meta + weekly events. ## 🔗 Graph - 부모: [[Hybridcasual]] - 응용: [[Live Operations (LiveOps)]] - Adjacent: [[Capybara GO!]] · [[CPI (Cost Per Install)]] ## 🤖 LLM 활용 **언제**: Level theme brainstorming, tutorial copy, retention email variants. **언제 X**: Solver / difficulty calibration (deterministic algorithm 의 superior), economy calibration. ## ❌ 안티패턴 - **Unsolvable levels**: 매 procedural generator 매 solver verification 의 absent. - **Difficulty cliffs**: 매 monotonic difficulty 매 churn spike. - **Forced ad walls**: 매 hypercasual 매 acceptable, hybridcasual 매 retention 의 destroy. - **No win-state telegraph**: 매 player 매 solved 의 모를 — 매 satisfaction loop break. ## 🧪 검증 / 중복 - Verified (Sensor Tower hypercasual reports 2024-2025, Deconstructor of Fun, Naavik hybridcasual analyses). - 신뢰도 B+. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — sort puzzle mechanics, BFS solver, procedural generation |