数独游戏的笔记功能是解决中高难度谜题的关键辅助工具。当玩家面对一个空单元格却无法确定具体数字时,可以在该单元格的笔记区域标记可能的候选数字。随着游戏的进行,玩家可以逐步排除不可能的选项,最终确定正确答案。
在传统纸质数独游戏中,玩家通常会用铅笔在单元格角落写下小数字作为笔记。而在电子版数独中,我们需要通过代码实现这一功能的数字化版本,同时还要利用电子设备的优势,提供比纸质版更强大的辅助功能。
笔记功能的核心是设计一个能高效存储和操作候选数字的数据结构。我们选择使用二维数组嵌套Set
dart复制List<List<Set<int>>> notes = [];
这个数据结构有以下优势:
初始化时,我们创建一个9x9的二维数组,每个元素都是一个空的Set:
dart复制notes = List.generate(9, (_) => List.generate(9, (_) => <int>{}));
为了让玩家明确知道当前是笔记模式还是填数模式,我们设计了模式切换机制:
dart复制bool notesMode = false;
void toggleNotesMode() {
notesMode = !notesMode;
update();
}
UI上会通过按钮样式变化直观显示当前模式:
笔记功能的核心交互是点击数字添加或移除笔记:
dart复制void addNote(int number) {
if (selectedRow < 0 || selectedCol < 0) return;
if (isFixed[selectedRow][selectedCol]) return;
if (board[selectedRow][selectedCol] != 0) return;
int row = selectedRow;
int col = selectedCol;
Set<int> currentNotes = notes[row][col];
Set<int> previousNotes = Set.from(currentNotes);
if (currentNotes.contains(number)) {
currentNotes.remove(number);
} else {
currentNotes.add(number);
}
moveHistory.add(GameMove(
row: row,
col: col,
previousNotes: previousNotes,
newNotes: Set.from(currentNotes),
));
update();
}
这段代码实现了以下功能:
笔记在UI上以3x3网格形式显示在单元格中:
dart复制Widget _buildNotes(Set<int> notes) {
if (notes.isEmpty) return const SizedBox();
return GridView.count(
crossAxisCount: 3,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(2.w),
children: List.generate(9, (index) {
int num = index + 1;
return Center(
child: Text(
notes.contains(num) ? num.toString() : '',
style: TextStyle(fontSize: 8.sp, color: Colors.grey),
),
);
}),
);
}
显示逻辑:
为提升效率,我们实现了自动填充可能数字的功能:
dart复制void autoFillNotes() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == 0 && !isFixed[i][j]) {
notes[i][j] = _getPossibleNumbers(i, j);
}
}
}
update();
}
Set<int> _getPossibleNumbers(int row, int col) {
Set<int> possible = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 排除同行的数字
for (int i = 0; i < 9; i++) {
possible.remove(board[row][i]);
}
// 排除同列的数字
for (int i = 0; i < 9; i++) {
possible.remove(board[i][col]);
}
// 排除同宫格的数字
int boxRow = (row ~/ 3) * 3;
int boxCol = (col ~/ 3) * 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
possible.remove(board[boxRow + i][boxCol + j]);
}
}
return possible;
}
算法说明:
当玩家填入数字后,相关笔记可能变得无效,我们自动检测并标记:
dart复制class NoteAnalyzer {
static Set<int> getConflictingNotes(
List<List<int>> board,
List<List<Set<int>>> notes,
int row,
int col,
) {
Set<int> conflicts = {};
Set<int> cellNotes = notes[row][col];
for (int note in cellNotes) {
// 检查同行冲突
for (int i = 0; i < 9; i++) {
if (board[row][i] == note) {
conflicts.add(note);
break;
}
}
// 检查同列冲突
for (int i = 0; i < 9; i++) {
if (board[i][col] == note) {
conflicts.add(note);
break;
}
}
// 检查同宫格冲突
int boxRow = (row ~/ 3) * 3;
int boxCol = (col ~/ 3) * 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[boxRow + i][boxCol + j] == note) {
conflicts.add(note);
break;
}
}
}
}
return conflicts;
}
}
冲突笔记在UI上会以红色+删除线显示,帮助玩家快速识别无效笔记。
当玩家选中某个数字时,高亮显示所有包含该数字的笔记:
dart复制Widget _buildNotes(Set<int> notes, int? highlightNumber) {
// ...其他代码...
children: List.generate(9, (index) {
int num = index + 1;
bool isHighlighted = highlightNumber != null && num == highlightNumber;
return Center(
child: Text(
notes.contains(num) ? num.toString() : '',
style: TextStyle(
fontSize: 8.sp,
color: isHighlighted ? Colors.blue : Colors.grey,
fontWeight: isHighlighted ? FontWeight.bold : FontWeight.normal,
),
),
);
}),
}
实现要点:
当填入一个数字时,自动从相关单元格的笔记中移除该数字:
dart复制void _removeNoteFromRelatedCells(int row, int col, int number) {
// 清理同行的笔记
for (int i = 0; i < 9; i++) {
if (i != col) {
notes[row][i].remove(number);
}
}
// 清理同列的笔记
for (int i = 0; i < 9; i++) {
if (i != row) {
notes[i][col].remove(number);
}
}
// 清理同宫格的笔记
int boxRow = (row ~/ 3) * 3;
int boxCol = (col ~/ 3) * 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
int r = boxRow + i;
int c = boxCol + j;
if (r != row || c != col) {
notes[r][c].remove(number);
}
}
}
}
这个功能大大减少了玩家手动维护笔记的工作量,确保笔记始终保持最新状态。
笔记不显示:
笔记状态不更新:
撤销功能异常:
合理使用自动功能:
结合高亮功能:
适度依赖笔记:
添加笔记统计功能,帮助玩家了解当前进度:
dart复制class NoteStatistics {
final int totalNotes;
final int cellsWithNotes;
final Map<int, int> noteDistribution;
static NoteStatistics calculate(List<List<Set<int>>> notes) {
int totalNotes = 0;
int cellsWithNotes = 0;
Map<int, int> distribution = {};
// 初始化分布图
for (int i = 1; i <= 9; i++) {
distribution[i] = 0;
}
// 计算统计
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
Set<int> cellNotes = notes[row][col];
if (cellNotes.isNotEmpty) {
cellsWithNotes++;
totalNotes += cellNotes.length;
for (int note in cellNotes) {
distribution[note] = distribution[note]! + 1;
}
}
}
}
return NoteStatistics(
totalNotes: totalNotes,
cellsWithNotes: cellsWithNotes,
noteDistribution: distribution,
);
}
}
实现笔记状态的保存和恢复功能:
基于笔记数据提供智能提示:
数独游戏的笔记功能实现需要平衡自动化与手动控制的程度,既要提供足够的智能辅助,又要保留玩家思考和解谜的乐趣。通过本文介绍的技术方案,开发者可以构建出功能完善、用户体验良好的数独笔记系统。