#sudoku-solver #brute-force #sudoku #puzzle #human #puzzle-solver

rustoku

使用人类和暴力搜索技术解决数独,并带有可玩应用程序构建框架和无需依赖项

2 个版本

0.1.1 2023 年 1 月 24 日
0.1.0 2023 年 1 月 23 日

#499算法


rustoku_gui 中使用

MIT 许可证

285KB
6.5K SLoC

Rustoku

Finned Jellyfish

我们都喜欢数独和编写暴力搜索器,但这个库使用人类风格的技术来解决数独谜题,同时结合传统的暴力搜索。
以下是一些已实现或计划实现的方法。

    已实现
  • 暴力搜索
  • 单可能性
  • 单候选
  • 裸元组
  • 隐藏元组
  • 声称候选
  • 指向候选
  • 基本鱼(X-wing、Swordfish...)
  • 鳍鱼
    尚未实现
  • 生鱼片鱼
  • 弗兰肯斯坦鱼
  • 突变鱼

软件开发和解决的数独的难度排名通常是通过比较已填充方格数与总数来计算的。这给出一个粗略的难度估计,但有许多例子表明,即使填充的方格数很少,某些谜题也很容易解决。使用人类技术可以计算一个更准确的评分。

暴力搜索器也无法给你提高解决技能的提示。这个库可以拿一个你可能难以解决的未解谜题,并给你一个关于困难步骤的提示,而无需 necessarily 给出谜题的答案。

这是一个正在进行中的项目

正在进行更新和重构,包括解决技术、缓存人类技术以提高性能,以及库接口,包括如何存储移动和获取难度等问题。解决技术尚未在大于 9x9 的谜题上进行测试。

关于这个库

这个库主要是为了给我自己在 Rust 中开发的经验,包括不同的数据结构来解决人类风格数独的独特算法。这使得这个库不需要任何标准库之外的依赖项。经过一些未来的变化,这个库可以在需要 no_std 属性的地方使用。

对于 9x9 谜题,使用 use::rustoku::basic::* 导入所有正常 9x9 谜题所需的 struct 和 traits。
对于 16x16 或 25x25,使用 use::rustoku::medium::*
对于36x36或49x49,使用use::rustoku::large::*
出于某种原因你想更大
对于64x64、81x81或100x100的谜题:use::rustoku::xlarge::*

示例

这些示例包含在存储库中。要运行一个示例

cargo run --example <example_name>

其中<example_name>替换为brutehumanhint或其他。

使用包含点(.)的字符串作为未解决的谜题输入。长度,以及作为有效谜题,都会被检查。如果使用无效字符串,则返回Err

暴力搜索

fn main() -> Result<(), Box<dyn std::error::Error>> {
   use rustoku::basic::*;
   use rustoku::OutputString;
   let puz = "...15..3.9..4....7.58.9....31....72.4.......8.......5....24...55.......6.71..9...";
   let puzzle = Sudoku::new(puz)?;
   println!("--Original--\n{}", puzzle.output_string('.', None));
   if let Solution::One(grid) = puzzle.solution() {
      println!("--Brute--\n{}", grid.output_string('.', None));      
   }
   Ok(())
}

此示例将使用暴力方法解决输入字符串。可以通过使用puz.solution()或通过实现Display特质来显示解决方案。

这将产生以下输出

--Original--
...15..3.9..4....7.58.9....31....72.4.......8.......5....24...55.......6.71..9...
--Brute--
742156839963428517158397642316985724495712368827634951689243175534871296271569483

人类解决

use rustoku::medium::*;
use rustoku::{OutputString, Technique};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
 let str = "..53.....8......2..7..1.5..4....53...1..7...6..32...8..6.5....9..4....3......97..";

 // Find the brute force solution.  Not needed for human solving, but will verify that solutions match
 let puz = Sudoku::new(str)?;

 let (human, moves) = puz.human_solve()?;

 // This is the brute force solution.
 let solution = puz.solution().get()?;

 assert_eq!(human, solution);
 println!("--Human--\n{:?}\n", human.output_string('.', None));

 let is_a_fish = |a_move: &Move| {
  if let Some(tech) = a_move.technique() {
   match tech {
    Technique::FishN(_)
    | Technique::Jellyfish
    | Technique::Swordfish
    | Technique::XWing
    | Technique::FinnedN(_)
    | Technique::FinnedJellyfish
    | Technique::FinnedSwordfish
    | Technique::FinnedXWing => true,
    _ => false,
   }
  } else {
   false
  }
 };

 let count = moves.into_iter().filter(is_a_fish).count();
 println!(
  "There were {} fish techniques used to solve this puzzle",
  count
 );

 Ok(())
}

此示例使用人类和暴力方法解决谜题,然后确保解决方案相同。
以下输出将显示

--Human--
"145327698839654127672918543496185372218473956753296481367542819984761235521839764"

There were 3 basic fish techniques used to solve this puzzle

人类解决使用难度系统,以便首先执行最简单的技术,然后在找到提示后提高难度。一旦找到并应用提示,求解器将再次从最简单的技术开始。未来的工作将包括允许自定义难度计算。

玩数独

此库允许创建数独游戏,而不仅仅是解决谜题。请参阅示例应用程序进行演示。

示例

克隆存储库并查看更多示例。

演示应用程序的截图

使用此库和Druid制作了一个简单的演示。在这里找到

Claiming Candidates
Finned Jellyfish
Hidden Triple Naked Quad Swordfish

无运行时依赖项