iSpring 前端面试题「Пятнашки」:React Hooks + TypeScript 实现可解 15 拼图实战指南 教程【免费下载链接】ru-test-assignmentsТестовые задания для самостоятельного выполнения от разных it компаний项目地址https://gitcode.com/gh_mirrors/ru/ru-test-assignments点击查看免费下载导读本文围绕 iSpringispringtech/coding-interview前端面试题中的「Пятнашки」15 Puzzle / 数字华容道任务完整解读原题的四项功能要求与五项技术约束并结合经典 15 拼图的算法原理给出可复现的 React Hooks TypeScript 实现方案包括可解性判定、洗牌算法、点击移动与胜利检测。读完本文你将能独立完成这道前端测试题并理解它背后考察的 React 状态管理、不可变更新与组合数学知识点。原题出自 frontend.md 所列测试任务清单完整题目位于 15.md。一、原题要求逐条解读1.1 功能需求Задание原文档要求开发经典版本的“Пятнашки”游戏具体功能点如下「Начать заново」重新开始按钮将数字 115 以随机顺序打乱排列生成的组合必须是可解的решаема不能随便生成任意乱序否则可能无解方块通过点击移动Квадратики должны перемещаться по клику点击某个方块若其与空格相邻则滑动到位正确排列时向用户祝贺胜利поздравлен с победой当 115 按序排好且空格归位时触发胜利提示。1.2 技术约束Требования原题对实现方式有明确限制这是面试官考察的重点约束项具体内容语言TypeScript或 Vanilla JS 组件类型检查样式/结构纯手写 CSS 与 HTML禁止预处理器Sass/Less与模板引擎UI 框架必须使用 React且采用函数式风格 Hooks第三方依赖除 React 外不得引入任何第三方库加分项使用 state manager状态管理器自选如 Zustand/Redux Toolkit这些约束意味着打乱算法、可解性判断、移动逻辑、胜利判定都必须由你自己实现不能借助 lodash 之类工具库也不能用现成的拼图组件。二、核心算法为什么随机打乱可能导致无解2.1 可解性判定的数学原理15 拼图4×4 棋盘含一个空格并不是所有排列都可还原。一个排列可解的充要条件如下空格从初始位置右下角到目标位置的曼哈顿距离为偶数在 4×4 棋盘中空格的目标位置为第 4 行第 4 列初始与目标相同距离恒为 0为偶数逆序对inversions数量为偶数。逆序对的定义从左到右、从上到下读取棋盘跳过空格得到长度为 15 的序列若其中某一对数字i j但位置上前者大于后者则计一个逆序对。当逆序对数量为偶数时排列可解为奇数时不可解。由于本题中空格最终仍回到右下角奇偶性一致判定规则可简化为逆序对数为偶数即可解。2.2 为什么必须显式保证可解性如果只是简单地对1..15做Math.random()洗牌约有 50% 的概率生成不可解排列。用户在不可解的棋盘上无论怎么移动都无法完成游戏就“卡死”了。因此原题特别强调“生成的组合必须可解”这是本题最重要的算法考察点。2.3 两种可靠的洗牌策略策略一对合法移动序列洗牌正向构造从已还原的目标棋盘出发随机执行 N 次合法移动只能移动与空格相邻的方块。由于每一步都从可解状态出发最终状态必然可解for (let i 0; i 200; i) { const moves getLegalMoves(board, emptyIndex); // 空格上下左右可交换的方块 const pick moves[Math.floor(Math.random() * moves.length)]; swap(board, emptyIndex, pick); }策略二随机排列 逆序对校验逆向修正先生成随机排列计算逆序对数若为奇数交换任意两个非空格方块如前两个数字将逆序对奇偶性翻转从而保证可解function isSolvable(flat: number[]): boolean { const tiles flat.filter((v) v ! EMPTY); let inversions 0; for (let i 0; i tiles.length; i) { for (let j i 1; j tiles.length; j) { if (tiles[i] tiles[j]) inversions; } } return inversions % 2 0; }两种策略任选其一即可建议优先采用策略一因为它的正确性不依赖对奇偶规则的推导且更容易用测试验证。三、React Hooks 架构设计3.1 状态模型在函数式 React 中用 Hooks 管理游戏状态的核心思路是把棋盘建模为一维数组空格用0表示索引位置即棋盘格子编号type Board number[]; // 长度 16值为 0..150 表示空格const [board, setBoard] useStateBoard(SOLVED_BOARD); const [moves, setMoves] useState(0); const [won, setWon] useState(false);其中SOLVED_BOARD [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0]目标状态空格在末尾。3.2 移动逻辑点击与相邻性判定“方块按点击移动”的本质是点击的方块索引必须与空格索引相邻上下左右二者才交换。用useCallback封装const moveTile useCallback( (index: number) { if (won) return; const emptyIndex board.indexOf(0); const row Math.floor(index / 4); const col index % 4; const eRow Math.floor(emptyIndex / 4); const eCol emptyIndex % 4; const isAdjacent Math.abs(row - eRow) Math.abs(col - eCol) 1; if (!isAdjacent) return; // 不相邻忽略点击 const next [...board]; [next[index], next[emptyIndex]] [next[emptyIndex], next[index]]; setBoard(next); setMoves((m) m 1); }, [board, won] );这里用Math.abs(dRow) Math.abs(dCol) 1判断曼哈顿距离为 1 即相邻简洁且无边界问题。注意setBoard(next)传入的是新数组满足 React 不可变更新的要求保证重渲染正确。3.3 胜利检测每次setBoard后用useEffect监听棋盘是否达到目标状态useEffect(() { if (board.every((v, i) v SOLVED_BOARD[i])) { setWon(true); } }, [board]);也可以把判断内联进moveTile在交换后立即比对目标数组命中则setWon(true)并渲染祝贺消息如「Поздравляем! Вы собрали головоломку за N ходов」。3.4 重新开始按钮「Начать заново」按钮将棋盘重置为洗牌后的新随机可解状态并复位步数与胜利标记const shuffle useCallback(() { const shuffled randomSolvableBoard(); // 见 2.3 节两种策略之一 setBoard(shuffled); setMoves(0); setWon(false); }, []);建议在首次挂载useEffect[]依赖时也执行一次洗牌让用户进入页面即可开始游戏。四、渲染与样式实现要点4.1 纯 CSS 网格布局原题要求纯手写 CSS无预处理器、无 UI 库4×4 棋盘用 CSS Grid 即可优雅实现.board { display: grid; grid-template-columns: repeat(4, 80px); grid-template-rows: repeat(4, 80px); gap: 4px; } .tile { display: flex; align-items: center; justify-content: center; font-size: 24px; border-radius: 6px; background: #4a90d9; color: #fff; cursor: pointer; user-select: none; } .tile--empty { background: transparent; cursor: default; }渲染时div classNameboard {board.map((value, index) ( button key{index} className{tile ${value 0 ? tile--empty : }} onClick{() moveTile(index)} disabled{value 0} {value 0 ? : value} /button ))} /div用button而非div可以免费获得键盘可达性这是加分细节。空格格子渲染为空且禁用点击。4.2 组件拆分建议App持有游戏状态与事件处理洗牌、移动、胜利标志Board纯展示组件接收board与onTileClickTile单个方块负责自身样式与点击回调。这样的拆分让状态集中在顶层符合函数式 Hooks 的推荐写法也便于后续引入 state manager。五、加分项接入状态管理器State Manager原题明确写出“使用 state manager 是加分项”。在仅允许 React 一个依赖的前提下可以选择轻量的 Zustandzustand包或 React 内置的useReducer Context。以useReducer为例零额外依赖最符合“不引入第三方库”约束type Action | { type: MOVE; index: number } | { type: SHUFFLE } | { type: RESET }; function reducer(state: GameState, action: Action): GameState { switch (action.type) { case MOVE: { // 相邻判定 交换 胜利检测返回新 state return movedState(state, action.index); } case SHUFFLE: { return { board: randomSolvableBoard(), moves: 0, won: false }; } default: return state; } }Reducer 是纯函数把“移动、洗牌、重置”三类状态转换集中在一处天然可测试。若面试官偏好外部状态库也可用 Zustand 的create定义 store效果等价。六、可测试性与代码组织建议纯函数优先把isSolvable、randomSolvableBoard、isAdjacent、move都写成不依赖 React 的纯函数便于单测用例覆盖至少验证三类用例——可解判定对错例的区分、从任意可解状态移动一步后仍可解、胜利检测在目标状态触发运行方式项目可用 Create React App 或 Vite 初始化二者均以 TypeScript 模板开箱支持 JSX 与类型检查执行npm install与npm start或npm run dev即可本地运行npm test运行测试。注意实现游戏逻辑本身无需任何额外库第三方依赖仅限 React 及脚手架工具。七、面试考察点总结这道题在 iSpring 的 frontend.md 中与「Пианино (Junior)」、「Пианино (Middle)」、「Drag to area」、「Видеоплеер」并列属于面向 Frontend 开发者的经典测试任务主要考察算法功底15 拼图可解性判定逆序对奇偶性与洗牌策略React 函数式写法HooksuseState/useEffect/useCallback与不可变状态更新代码组织纯函数与组件拆分、state manager 的使用工程约束意识在“零第三方库”限制下完成全部功能这对测试候选人区分度较高。完整题目原文见 15.md同系列其他任务可对照 frontend.md 任务清单查阅。赞分享教程【免费下载链接】ru-test-assignmentsТестовые задания для самостоятельного выполнения от разных it компаний项目地址https://gitcode.com/gh_mirrors/ru/ru-test-assignments点击查看免费下载相关推荐Sendsay 前端面试题实战用 React TypeScript 从零实现拖拽式计算器构造器Calculator ConstructorSendsay 前端面试题实战用 React TypeScript 从零实现拖拽式计算器构造器Calculator Constructor 本篇技术指教程KODE 前端实习生测试任务实战指南React TypeScript 实现员工目录应用KODE 前端实习生测试任务实战指南React TypeScript 实现员工目录应用 导读 本文以开源仓库 ru test assignments ht教程Highcharts React高级技巧自定义组件集成与性能优化实战Highcharts React高级技巧自定义组件集成与性能优化实战 Highcharts React是官方支持的React图表组件库它允许开发者轻松地在R上一篇中文AI绘画终极指南5分钟掌握ComfyUI工作流配置秘籍下一篇开发者必看Llama-3.2-1B-Instruct API接口调用与参数配置详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考