diff --git a/app/page.js b/app/page.js
index 437f375..be8ed40 100644
--- a/app/page.js
+++ b/app/page.js
@@ -25,6 +25,7 @@ import { useEffect, useState } from "react";
export default function Home() {
const [difficulty, setDifficulty] = useState(2);
const [markingAssist, setMarkingAssist] = useState(false);
+ const [beginWithMarks, setBeginWithMarks] = useState(false);
const [appVersion, setAppversion] = useState("");
const [updateDialogOpen, setUpdateDialogOpen] = useState(false);
const [updateManifest, setUpdateManifest] = useState({});
@@ -34,6 +35,7 @@ export default function Home() {
useEffect(() => {
invoke('get_difficulty').then((difficulty) => setDifficulty(difficulty));
invoke('get_marking_assist').then((markingAssist) => setMarkingAssist(markingAssist));
+ invoke('get_begin_with_marks').then((beginWithMarks) => setBeginWithMarks(beginWithMarks));
}, []);
useEffect(() => {
@@ -105,7 +107,7 @@ export default function Home() {
- 开启标记辅助后,开局时会自动标记上所有候选数字
+ 开启标记辅助后,每个格子中的候选数会自动更新
+
+
+
+
+
+
+
+
+ 开启后,开局时会自动标记上所有候选数字
+
+
+
{
+ setBeginWithMarks(checked);
+ invoke('set_begin_with_marks', { beginWithMarks: checked });
+ }}
+ />
+
diff --git a/app/start/page.js b/app/start/page.js
index 76b400b..89abc5d 100644
--- a/app/start/page.js
+++ b/app/start/page.js
@@ -31,6 +31,7 @@ export default function Start() {
const [usedAssist, setUsedAssist] = useState(false);
const markingAssistRef = useRef(false); // 是否开启标记辅助
+ const beginWithMarksRef = useRef(false); // 是否开启开局标记
const historyRef = useRef([]);
const futureRef = useRef([]);
@@ -57,13 +58,15 @@ export default function Start() {
useEffect(() => {
invoke('get_difficulty').then((difficulty) => setDifficulty(difficulty));
invoke('get_marking_assist').then((markingAssist) => { markingAssistRef.current = markingAssist; setUsedAssist(markingAssist); });
+ invoke('get_begin_with_marks').then((beginWithMarks) => { beginWithMarksRef.current = beginWithMarks; });
}, []);
const init = useCallback((grid) => {
setGrid(grid);
setMaxCandidates(getMaxCandidates(grid));
- if (markingAssistRef.current) {
- setMarkedCandidates(Array.from({ length: 9 }, (v) => Array.from({ length: 9 }, (v) => Array.from({ length: 10 }, (v) => true))));
+ if (beginWithMarksRef.current) {
+ setMarkedCandidates(getMaxCandidates(grid));
+ // setMarkedCandidates(Array.from({ length: 9 }, (v) => Array.from({ length: 9 }, (v) => Array.from({ length: 10 }, (v) => true))));
} else {
setMarkedCandidates(Array.from({ length: 9 }, (v) => Array.from({ length: 9 }, (v) => Array.from({ length: 10 }, (v) => false))));
}
@@ -226,8 +229,9 @@ export default function Start() {
}
// 标记候选数
else {
- // 只能标记 maxCandidates[r][c] 中的数字
- if (grid[r][c].value == 0 && (maxCandidates[r][c][num] || event.key == ' ')) {
+ // 没有开启标记辅助时,可以标记任意数字
+ // 开启时,只能标记 maxCandidates[r][c] 中的数字
+ if (grid[r][c].value == 0 && (!markingAssistRef.current || maxCandidates[r][c][num] || event.key == ' ')) {
if (event.key == ' ') { // 按空格键
if (!markedCandidates[r][c].every((is, num) => num == 0 || !is)) {
pushHistory(grid, markedCandidates);
@@ -271,7 +275,7 @@ export default function Start() {
if (!finished && grid)
invoke('get_hint', {
grid: grid.map((row) => row.map((cell) => cell.value)),
- candidates: maskedCandidates
+ candidates: markingAssistRef.current ? maskedCandidates : markedCandidates
}).then((hint) => {
setUsedHint(true);
setHint(hint[0]);
@@ -309,7 +313,7 @@ export default function Start() {
<>
for DirectOption {
- fn from(value: sudoku::techniques::DirectOption) -> Self {
- Self(value.0, value.1, value.2)
- }
-}
+#[serde(remote = "DirectOption")]
+pub struct DirectOptionDef(pub usize, pub usize, pub i8);
#[derive(Serialize)]
-pub struct ReducingCandidatesOption(pub Vec<(Vec<(usize, usize)>, Vec)>);
-impl From for ReducingCandidatesOption {
- fn from(value: sudoku::techniques::ReducingCandidatesOption) -> Self {
- Self(value.0)
- }
-}
+#[serde(remote = "ReducingCandidatesOption")]
+pub struct ReducingCandidatesOptionDef(pub Vec<(Vec<(usize, usize)>, Vec)>);
#[derive(Serialize)]
pub enum HintOption {
+ #[serde(with = "DirectOptionDef")]
Direct(DirectOption),
+ #[serde(with = "ReducingCandidatesOptionDef")]
ReducingCandidates(ReducingCandidatesOption),
}
@@ -37,8 +31,8 @@ pub struct Hint {
pub option: HintOption,
}
-pub trait GetHint {
- fn get_hint(state: &FullState) -> Option;
+pub trait GetHint: Technique {
+ fn get_hint(&self) -> Option;
}
#[derive(Serialize)]
@@ -66,30 +60,24 @@ pub enum Color {
#[derive(Serialize)]
pub enum ElementType {
+ #[serde(with = "HouseDef")]
House(House),
Cell(usize, usize),
Candidate(usize, usize, i8),
}
#[derive(Serialize)]
-pub enum House {
+#[serde(remote = "House")]
+pub enum HouseDef {
Row(usize),
Column(usize),
Block(usize),
}
-fn house_to_string(house: sudoku::techniques::House) -> String {
+fn house_to_string(house: House) -> String {
match house {
sudoku::techniques::House::Row(idx) => format!("第{}行", idx + 1),
sudoku::techniques::House::Column(idx) => format!("第{}列", idx + 1),
sudoku::techniques::House::Block(idx) => format!("第{}宫", idx + 1),
}
}
-
-fn house_to_house(house: sudoku::techniques::House) -> House {
- match house {
- sudoku::techniques::House::Row(idx) => House::Row(idx),
- sudoku::techniques::House::Column(idx) => House::Column(idx),
- sudoku::techniques::House::Block(idx) => House::Block(idx),
- }
-}
diff --git a/src-tauri/src/hint/hidden_subsets.rs b/src-tauri/src/hint/hidden_subsets.rs
index 82ffcaf..8096a3d 100644
--- a/src-tauri/src/hint/hidden_subsets.rs
+++ b/src-tauri/src/hint/hidden_subsets.rs
@@ -1,172 +1,146 @@
use sudoku::{
state::full_state::FullState,
- techniques::{
- hidden_subsets::{HiddenPairBlock, HiddenPairColumn, HiddenPairInfo, HiddenPairRow},
- Technique,
- },
+ techniques::{hidden_subsets::HiddenPair, ReducingCandidates, Technique},
};
-use super::{
- house_to_house, house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption,
- ReducingCandidatesOption, Segment,
-};
-
-fn hidden_pair_to_hint(
- info: HiddenPairInfo,
- option: sudoku::techniques::ReducingCandidatesOption,
-) -> Hint {
- let mut visual_elements = vec![
- Element {
- kind: ElementType::House(house_to_house(info.house)),
- color: Color::House1,
- },
- Element {
- kind: ElementType::Candidate(info.rem_cell_1.0, info.rem_cell_1.1, info.nums[0]),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Candidate(info.rem_cell_1.0, info.rem_cell_1.1, info.nums[1]),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Candidate(info.rem_cell_2.0, info.rem_cell_2.1, info.nums[0]),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Candidate(info.rem_cell_2.0, info.rem_cell_2.1, info.nums[1]),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Cell(info.rem_cell_1.0, info.rem_cell_1.1),
- color: Color::Cell1,
- },
- Element {
- kind: ElementType::Cell(info.rem_cell_2.0, info.rem_cell_2.1),
- color: Color::Cell1,
- },
- ];
- for num in info.rem_nums_1 {
- visual_elements.push(Element {
- kind: ElementType::Candidate(info.rem_cell_1.0, info.rem_cell_1.1, num),
- color: Color::CandidateToRemove,
- });
- }
- for num in info.rem_nums_2 {
- visual_elements.push(Element {
- kind: ElementType::Candidate(info.rem_cell_2.0, info.rem_cell_2.1, num),
- color: Color::CandidateToRemove,
- });
- }
+use super::{house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption, Segment};
- Hint {
- name: "Hidden Pair".into(),
- description: vec![
- Segment {
- text: house_to_string(info.house),
- color: Color::House1,
- },
- Segment {
- text: "中,".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.nums[0].to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: "和".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.nums[1].to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: "只能填在".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: "这两格".into(),
- color: Color::Cell1,
- },
- Segment {
- text: ",所以".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: "这两格".into(),
- color: Color::Cell1,
- },
- Segment {
- text: "也只可能填".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.nums[0].to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: "和".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.nums[1].to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: ",".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: "其他的候选数字".into(),
- color: Color::CandidateToRemove,
- },
- Segment {
- text: "可以删去。".into(),
- color: Color::TextDefault,
- },
- ],
- visual_elements,
- option: HintOption::ReducingCandidates(ReducingCandidatesOption::from(option)),
- }
-}
-
-pub struct HiddenPair;
impl GetHint for HiddenPair {
- fn get_hint(state: &FullState) -> Option {
- let res = HiddenPairBlock::check(state);
- if res.0.is_some() {
- return Some(
- hidden_pair_to_hint(
- res.0.clone().unwrap(),
- ,
- >>::into(res)
- .unwrap()
- .into(),
- ),
- );
- }
- let res = HiddenPairRow::check(state);
- if res.0.is_some() {
- return Some(hidden_pair_to_hint(res.0.clone().unwrap(),
- >>::into(res).unwrap().into()
- ));
- }
- let res = HiddenPairColumn::check(state);
- if res.0.is_some() {
- return Some(
- hidden_pair_to_hint(
- res.0.clone().unwrap(),
- ,
- >>::into(res)
- .unwrap()
- .into(),
- ),
- );
- }
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+ let mut visual_elements = vec![
+ Element {
+ kind: ElementType::House(info.house),
+ color: Color::House1,
+ },
+ Element {
+ kind: ElementType::Candidate(
+ info.rem_cell_1.0,
+ info.rem_cell_1.1,
+ info.nums[0],
+ ),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Candidate(
+ info.rem_cell_1.0,
+ info.rem_cell_1.1,
+ info.nums[1],
+ ),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Candidate(
+ info.rem_cell_2.0,
+ info.rem_cell_2.1,
+ info.nums[0],
+ ),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Candidate(
+ info.rem_cell_2.0,
+ info.rem_cell_2.1,
+ info.nums[1],
+ ),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Cell(info.rem_cell_1.0, info.rem_cell_1.1),
+ color: Color::Cell1,
+ },
+ Element {
+ kind: ElementType::Cell(info.rem_cell_2.0, info.rem_cell_2.1),
+ color: Color::Cell1,
+ },
+ ];
+ for num in info.rem_nums_1 {
+ visual_elements.push(Element {
+ kind: ElementType::Candidate(info.rem_cell_1.0, info.rem_cell_1.1, num),
+ color: Color::CandidateToRemove,
+ });
+ }
+ for num in info.rem_nums_2 {
+ visual_elements.push(Element {
+ kind: ElementType::Candidate(info.rem_cell_2.0, info.rem_cell_2.1, num),
+ color: Color::CandidateToRemove,
+ });
+ }
+ return Some(Hint {
+ name: "Hidden Pair".into(),
+ description: vec![
+ Segment {
+ text: house_to_string(info.house),
+ color: Color::House1,
+ },
+ Segment {
+ text: "中,".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.nums[0].to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "和".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.nums[1].to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "只能填在".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: "这两格".into(),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: ",所以".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: "这两格".into(),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: "也只可能填".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.nums[0].to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "和".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.nums[1].to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: ",".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: "其他的候选数字".into(),
+ color: Color::CandidateToRemove,
+ },
+ Segment {
+ text: "可以删去。".into(),
+ color: Color::TextDefault,
+ },
+ ],
+ visual_elements,
+ option: HintOption::ReducingCandidates(option),
+ });
+ }
None
}
}
diff --git a/src-tauri/src/hint/locked_candidates.rs b/src-tauri/src/hint/locked_candidates.rs
index d431fa2..b28671f 100644
--- a/src-tauri/src/hint/locked_candidates.rs
+++ b/src-tauri/src/hint/locked_candidates.rs
@@ -1,23 +1,27 @@
-use sudoku::{state::full_state::FullState, techniques::Technique, utils::coord_2_block};
-
-use super::{
- house_to_house, house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption,
- ReducingCandidatesOption, Segment,
+use sudoku::{
+ state::full_state::FullState,
+ techniques::{
+ locked_candidates::{Claiming, Pointing},
+ ReducingCandidates, Technique,
+ },
+ utils::coord_2_block,
};
-pub struct Pointing;
+use super::{house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption, Segment};
+
impl GetHint for Pointing {
- fn get_hint(state: &FullState) -> Option {
- let res = sudoku::techniques::locked_candidates::Pointing::check(state);
- if res.0.is_some() {
- let info = res.0.clone().unwrap();
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+
let mut visual_elements = vec![
Element {
kind: ElementType::House(super::House::Block(info.block)),
color: Color::House1,
},
Element {
- kind: ElementType::House(house_to_house(info.rem_house)),
+ kind: ElementType::House(info.rem_house),
color: Color::House2,
},
];
@@ -90,32 +94,27 @@ impl GetHint for Pointing {
color: Color::TextDefault,
},
],
- visual_elements: visual_elements,
- option: HintOption::ReducingCandidates(ReducingCandidatesOption::from(
- ,
- >>::into(res)
- .unwrap(),
- )),
+ visual_elements,
+ option: HintOption::ReducingCandidates(option),
});
}
None
}
}
-pub struct Claiming;
impl GetHint for Claiming {
- fn get_hint(state: &FullState) -> Option {
- let res = sudoku::techniques::locked_candidates::Claiming::check(state);
- if res.0.is_some() {
- let info = res.0.clone().unwrap();
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+
let mut visual_elements = vec![
Element {
kind: ElementType::House(super::House::Block(info.rem_block)),
color: Color::House2,
},
Element {
- kind: ElementType::House(house_to_house(info.house)),
+ kind: ElementType::House(info.house),
color: Color::House1,
},
];
@@ -195,13 +194,8 @@ impl GetHint for Claiming {
color: Color::TextDefault,
},
],
- visual_elements: visual_elements,
- option: HintOption::ReducingCandidates(ReducingCandidatesOption::from(
- ,
- >>::into(res)
- .unwrap(),
- )),
+ visual_elements,
+ option: HintOption::ReducingCandidates(option),
});
}
None
diff --git a/src-tauri/src/hint/naked_subsets.rs b/src-tauri/src/hint/naked_subsets.rs
index 01a3571..427c482 100644
--- a/src-tauri/src/hint/naked_subsets.rs
+++ b/src-tauri/src/hint/naked_subsets.rs
@@ -1,185 +1,239 @@
use sudoku::{
state::full_state::FullState,
techniques::{
- naked_subsets::{NakedPairBlock, NakedPairColumn, NakedPairInfo, NakedPairRow},
- Technique,
+ naked_subsets::{NakedPair, NakedSubset},
+ ReducingCandidates, Technique,
},
};
-use super::{
- house_to_house, house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption,
- ReducingCandidatesOption, Segment,
-};
+use super::{house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption, Segment};
-fn naked_pair_to_hint(
- info: NakedPairInfo,
- option: sudoku::techniques::ReducingCandidatesOption,
-) -> Hint {
- let mut visual_elements = vec![
- Element {
- kind: ElementType::House(house_to_house(info.house)),
- color: Color::House1,
- },
- Element {
- kind: ElementType::Cell(info.cells[0].0, info.cells[0].1),
- color: Color::Cell1,
- },
- Element {
- kind: ElementType::Cell(info.cells[1].0, info.cells[1].1),
- color: Color::Cell1,
- },
- Element {
- kind: ElementType::Candidate(info.cells[0].0, info.cells[0].1, info.rem_num_1),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Candidate(info.cells[0].0, info.cells[0].1, info.rem_num_2),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Candidate(info.cells[1].0, info.cells[1].1, info.rem_num_1),
- color: Color::CandidateToReserve,
- },
- Element {
- kind: ElementType::Candidate(info.cells[1].0, info.cells[1].1, info.rem_num_2),
- color: Color::CandidateToReserve,
- },
- ];
- for cell in info.rem_cells_1 {
- visual_elements.push(Element {
- kind: ElementType::Candidate(cell.0, cell.1, info.rem_num_1),
- color: Color::CandidateToRemove,
- });
- }
- for cell in info.rem_cells_2 {
- visual_elements.push(Element {
- kind: ElementType::Candidate(cell.0, cell.1, info.rem_num_2),
- color: Color::CandidateToRemove,
- });
+impl GetHint for NakedPair {
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+
+ let mut visual_elements = vec![
+ Element {
+ kind: ElementType::House(info.house),
+ color: Color::House1,
+ },
+ Element {
+ kind: ElementType::Cell(info.cells[0].0, info.cells[0].1),
+ color: Color::Cell1,
+ },
+ Element {
+ kind: ElementType::Cell(info.cells[1].0, info.cells[1].1),
+ color: Color::Cell1,
+ },
+ Element {
+ kind: ElementType::Candidate(info.cells[0].0, info.cells[0].1, info.rem_num_1),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Candidate(info.cells[0].0, info.cells[0].1, info.rem_num_2),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Candidate(info.cells[1].0, info.cells[1].1, info.rem_num_1),
+ color: Color::CandidateToReserve,
+ },
+ Element {
+ kind: ElementType::Candidate(info.cells[1].0, info.cells[1].1, info.rem_num_2),
+ color: Color::CandidateToReserve,
+ },
+ ];
+ for cell in info.rem_cells_1 {
+ visual_elements.push(Element {
+ kind: ElementType::Candidate(cell.0, cell.1, info.rem_num_1),
+ color: Color::CandidateToRemove,
+ });
+ }
+ for cell in info.rem_cells_2 {
+ visual_elements.push(Element {
+ kind: ElementType::Candidate(cell.0, cell.1, info.rem_num_2),
+ color: Color::CandidateToRemove,
+ });
+ }
+
+ return Some(Hint {
+ name: "Naked Pair".into(),
+ description: vec![
+ Segment {
+ text: house_to_string(info.house),
+ color: Color::House1,
+ },
+ Segment {
+ text: "中,".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: "这两格".into(),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: "只能填".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.rem_num_1.to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "和".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.rem_num_2.to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: ",所以".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.rem_num_1.to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "和".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.rem_num_2.to_string(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "也只可能填在".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: "这两格".into(),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: ",进而可以将".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.rem_num_1.to_string(),
+ color: Color::CandidateToRemove,
+ },
+ Segment {
+ text: "和".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: info.rem_num_2.to_string(),
+ color: Color::CandidateToRemove,
+ },
+ Segment {
+ text: "从其他的格子的候选数字中删去。".into(),
+ color: Color::TextDefault,
+ },
+ ],
+ visual_elements,
+ option: HintOption::ReducingCandidates(option),
+ });
+ }
+ None
}
+}
+
+impl GetHint for NakedSubset {
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+
+ let nums_str: Vec = info.nums.iter().map(|num| num.to_string()).collect();
+ let nums_str = nums_str.join(" ");
- Hint {
- name: "Naked Pair".into(),
- description: vec![
- Segment {
- text: house_to_string(info.house),
+ let mut visual_elements = vec![Element {
+ kind: ElementType::House(info.house),
color: Color::House1,
- },
- Segment {
- text: "中,".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: "这两格".into(),
- color: Color::Cell1,
- },
- Segment {
- text: "只能填".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.rem_num_1.to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: "和".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.rem_num_2.to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: ",所以".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.rem_num_1.to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: "和".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.rem_num_2.to_string(),
- color: Color::CandidateToReserve,
- },
- Segment {
- text: "也只可能填在".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: "这两格".into(),
+ }];
+
+ visual_elements.extend(info.cells.iter().map(|(r, c)| Element {
+ kind: ElementType::Cell(*r, *c),
color: Color::Cell1,
- },
- Segment {
- text: ",进而可以将".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.rem_num_1.to_string(),
- color: Color::CandidateToRemove,
- },
- Segment {
- text: "和".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: info.rem_num_2.to_string(),
- color: Color::CandidateToRemove,
- },
- Segment {
- text: "从其他的格子的候选数字中删去。".into(),
- color: Color::TextDefault,
- },
- ],
- visual_elements,
- option: HintOption::ReducingCandidates(ReducingCandidatesOption::from(option)),
- }
-}
+ }));
-pub struct NakedPair;
-impl GetHint for NakedPair {
- fn get_hint(state: &FullState) -> Option {
- let res = NakedPairBlock::check(state);
- if res.0.is_some() {
- return Some(
- naked_pair_to_hint(
- res.0.clone().unwrap(),
- ,
- >>::into(res)
- .unwrap()
- .into(),
- ),
- );
- }
- let res = NakedPairRow::check(state);
- if res.0.is_some() {
- return Some(naked_pair_to_hint(
- res.0.clone().unwrap(),
- >>::into(
- res,
- )
- .unwrap()
- .into(),
- ));
- }
- let res = NakedPairColumn::check(state);
- if res.0.is_some() {
- return Some(
- naked_pair_to_hint(
- res.0.clone().unwrap(),
- ,
- >>::into(res)
- .unwrap()
- .into(),
- ),
- );
- }
+ visual_elements.extend(info.nums.iter().flat_map(|num| {
+ info.cells.iter().map(|(r, c)| Element {
+ kind: ElementType::Candidate(*r, *c, *num),
+ color: Color::CandidateToReserve,
+ })
+ }));
+ visual_elements.extend(info.removes.iter().flat_map(|(cells, num)| {
+ cells.iter().map(|(r, c)| Element {
+ kind: ElementType::Candidate(*r, *c, *num),
+ color: Color::CandidateToRemove,
+ })
+ }));
+
+ return Some(Hint {
+ name: match info.k {
+ 3 => "Naked Triplet".into(),
+ 4 => "Naked Quadruplet".into(),
+ _ => format!("Naked {}-Subset", info.k),
+ },
+ description: vec![
+ Segment {
+ text: house_to_string(info.house),
+ color: Color::House1,
+ },
+ Segment {
+ text: "中,".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: format!("这{}格", info.k),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: "只能填".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: nums_str.clone(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: ",所以".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: nums_str.clone(),
+ color: Color::CandidateToReserve,
+ },
+ Segment {
+ text: "也只可能填在".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: format!("这{}格", info.k),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: ",进而可以将".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: nums_str,
+ color: Color::CandidateToRemove,
+ },
+ Segment {
+ text: "从其他的格子的候选数字中删去。".into(),
+ color: Color::TextDefault,
+ },
+ ],
+ visual_elements,
+ option: HintOption::ReducingCandidates(option),
+ });
+ }
None
}
}
diff --git a/src-tauri/src/hint/singles.rs b/src-tauri/src/hint/singles.rs
index f57813a..3e98e28 100644
--- a/src-tauri/src/hint/singles.rs
+++ b/src-tauri/src/hint/singles.rs
@@ -1,159 +1,107 @@
use sudoku::{
state::full_state::FullState,
techniques::{
- singles::{
- HiddenSingleBlock, HiddenSingleColumn, HiddenSingleInfo, HiddenSingleRow,
- NakedSingleInfo,
- },
- Technique,
+ singles::{HiddenSingle, NakedSingle},
+ Direct, Technique,
},
};
-use super::{
- house_to_house, house_to_string, Color, DirectOption, Element, ElementType, GetHint, Hint,
- HintOption, Segment,
-};
-
-fn hidden_single_to_hint(info: HiddenSingleInfo, option: sudoku::techniques::DirectOption) -> Hint {
- Hint {
- name: "Hidden Single".into(),
- description: vec![
- Segment {
- text: house_to_string(info.house),
- color: Color::House1,
- },
- Segment {
- text: "中只有".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: "此格".into(),
- color: Color::Cell1,
- },
- Segment {
- text: "可以填".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: format!("{}", info.fillable.2),
- color: Color::NumToFill,
- },
- Segment {
- text: "。".into(),
- color: Color::TextDefault,
- },
- ],
- visual_elements: vec![
- Element {
- kind: ElementType::House(house_to_house(info.house)),
- color: Color::House1,
- },
- Element {
- kind: ElementType::Cell(info.fillable.0, info.fillable.1),
- color: Color::Cell1,
- },
- Element {
- kind: ElementType::Candidate(info.fillable.0, info.fillable.1, info.fillable.2),
- color: Color::NumToFill,
- },
- ],
- option: HintOption::Direct(DirectOption::from(option)),
- }
-}
+use super::{house_to_string, Color, Element, ElementType, GetHint, Hint, HintOption, Segment};
-pub struct HiddenSingle;
impl GetHint for HiddenSingle {
- fn get_hint(state: &FullState) -> Option {
- let res = HiddenSingleBlock::check(state);
- if res.0.is_some() {
- let info = res.0;
- return info.map(|info| {
- hidden_single_to_hint(
- info,
- >>::into(
- res,
- )
- .unwrap(),
- )
- });
- }
- let res = HiddenSingleRow::check(state);
- if res.0.is_some() {
- let info = res.0;
- return info.map(|info| {
- hidden_single_to_hint(
- info,
- >>::into(res)
- .unwrap(),
- )
- });
- }
- let res = HiddenSingleColumn::check(state);
- if res.0.is_some() {
- let info = res.0;
- return info.map(|info| {
- hidden_single_to_hint(
- info,
- >>::into(
- res,
- )
- .unwrap(),
- )
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+ return Some(Hint {
+ name: "Hidden Single".into(),
+ description: vec![
+ Segment {
+ text: house_to_string(info.house),
+ color: Color::House1,
+ },
+ Segment {
+ text: "中只有".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: "此格".into(),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: "可以填".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: format!("{}", info.fillable.2),
+ color: Color::NumToFill,
+ },
+ Segment {
+ text: "。".into(),
+ color: Color::TextDefault,
+ },
+ ],
+ visual_elements: vec![
+ Element {
+ kind: ElementType::House(info.house),
+ color: Color::House1,
+ },
+ Element {
+ kind: ElementType::Cell(info.fillable.0, info.fillable.1),
+ color: Color::Cell1,
+ },
+ Element {
+ kind: ElementType::Candidate(
+ info.fillable.0,
+ info.fillable.1,
+ info.fillable.2,
+ ),
+ color: Color::NumToFill,
+ },
+ ],
+ option: HintOption::Direct(option),
});
}
None
}
}
-fn naked_single_to_hint(info: NakedSingleInfo, option: sudoku::techniques::DirectOption) -> Hint {
- Hint {
- name: "Naked Single".into(),
- description: vec![
- Segment {
- text: "此格".into(),
- color: Color::Cell1,
- },
- Segment {
- text: "只能填".into(),
- color: Color::TextDefault,
- },
- Segment {
- text: format!("{}", info.0 .2),
- color: Color::NumToFill,
- },
- Segment {
- text: "。".into(),
- color: Color::TextDefault,
- },
- ],
- visual_elements: vec![
- Element {
- kind: ElementType::Cell(info.0 .0, info.0 .1),
- color: Color::Cell1,
- },
- Element {
- kind: ElementType::Candidate(info.0 .0, info.0 .1, info.0 .2),
- color: Color::NumToFill,
- },
- ],
- option: HintOption::Direct(DirectOption::from(option)),
- }
-}
-
-pub struct NakedSingle;
impl GetHint for NakedSingle {
- fn get_hint(state: &FullState) -> Option {
- let res = sudoku::techniques::singles::NakedSingle::check(state);
- if res.0.is_some() {
- let info = res.0;
- return info.map(|info| {
- naked_single_to_hint(
- info,
- ,
- >>::into(res)
- .unwrap(),
- )
+ fn get_hint(&self) -> Option {
+ if >::appliable(&self) {
+ let info = self.0.clone().unwrap();
+ let option = >::option(&self).unwrap();
+ return Some(Hint {
+ name: "Naked Single".into(),
+ description: vec![
+ Segment {
+ text: "此格".into(),
+ color: Color::Cell1,
+ },
+ Segment {
+ text: "只能填".into(),
+ color: Color::TextDefault,
+ },
+ Segment {
+ text: format!("{}", info.0 .2),
+ color: Color::NumToFill,
+ },
+ Segment {
+ text: "。".into(),
+ color: Color::TextDefault,
+ },
+ ],
+ visual_elements: vec![
+ Element {
+ kind: ElementType::Cell(info.0 .0, info.0 .1),
+ color: Color::Cell1,
+ },
+ Element {
+ kind: ElementType::Candidate(info.0 .0, info.0 .1, info.0 .2),
+ color: Color::NumToFill,
+ },
+ ],
+ option: HintOption::Direct(option),
});
}
None
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 8b60a78..904bfad 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1,13 +1,7 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-use app::hint::{
- hidden_subsets::HiddenPair,
- locked_candidates::{Claiming, Pointing},
- naked_subsets::NakedPair,
- singles::{HiddenSingle, NakedSingle},
- GetHint, Hint,
-};
+use app::hint::{GetHint, Hint};
use serde::Serialize;
use std::sync::{Arc, Mutex};
use sudoku::{
@@ -18,6 +12,12 @@ use sudoku::{
judge::judge_sudoku as judge,
solver::{advanced::AdvancedSolver, Solver},
state::full_state::FullState,
+ techniques::{
+ hidden_subsets::HiddenPair,
+ locked_candidates::{Claiming, Pointing},
+ naked_subsets::{NakedPair, NakedSubset},
+ singles::{HiddenSingle, NakedSingle},
+ },
Grid,
};
use tauri::State;
@@ -32,6 +32,8 @@ fn main() {
get_difficulty,
set_marking_assist,
get_marking_assist,
+ set_begin_with_marks,
+ get_begin_with_marks,
get_hint
])
.run(tauri::generate_context!())
@@ -42,13 +44,15 @@ fn main() {
struct Settings {
difficulty: u8,
marking_assist: bool,
+ begin_with_marks: bool,
}
impl Default for Settings {
fn default() -> Self {
Self {
- difficulty: 2,
- marking_assist: false,
+ difficulty: 1,
+ marking_assist: true,
+ begin_with_marks: false,
}
}
}
@@ -100,6 +104,16 @@ fn get_marking_assist(settings: State<'_, SettingsState>) -> Result {
Ok(settings.0.lock().unwrap().marking_assist)
}
+#[tauri::command]
+fn set_begin_with_marks(begin_with_marks: bool, settings: State<'_, SettingsState>) {
+ settings.0.lock().unwrap().begin_with_marks = begin_with_marks;
+}
+
+#[tauri::command]
+fn get_begin_with_marks(settings: State<'_, SettingsState>) -> Result {
+ Ok(settings.0.lock().unwrap().begin_with_marks)
+}
+
#[derive(Serialize)]
pub enum GetHintResult {
Success,
@@ -120,19 +134,22 @@ fn get_hint(grid: [[i8; 9]; 9], candidates: [[[bool; 10]; 9]; 9]) -> (Option