use std::{collections::HashMap, time::Instant}; use wave_function_collapse::wave_function::{ Node, NodeStateCollection, WaveFunction, NodeStateProbability, collapsable_wave_function::{sequential_collapsable_wave_function::SequentialCollapsableWaveFunction, collapsable_wave_function::CollapsableWaveFunction} }; /// This struct represents a Sudoku puzzle. struct SudokuPuzzle { number_per_row_per_column: Vec>> } impl SudokuPuzzle { fn print(&self) { let mut number_per_column_per_row: Vec>> = Vec::new(); for (x_index, number_per_row) in self.number_per_row_per_column.iter().enumerate() { for (y_index, number) in number_per_row.iter().enumerate() { if y_index == 0 { number_per_column_per_row.push(Vec::new()); } number_per_column_per_row.get_mut(x_index).unwrap().push(number.clone()); } } for (_, number_per_column) in number_per_column_per_row.iter().enumerate() { println!("-------------------"); print!("|"); for (_, number_option) in number_per_column.iter().enumerate() { if let Some(number) = number_option { print!("{number}"); } else { print!(" "); } print!("|"); } println!(""); } println!("-------------------"); } fn get_solution(&self) -> Result { // setup nodes and node state collections let mut node_id_per_y_per_x: HashMap> = HashMap::new(); for (x_index, number_per_row) in self.number_per_row_per_column.iter().enumerate() { let mut node_id_per_y: HashMap = HashMap::new(); for (y_index, _) in number_per_row.iter().enumerate() { let node_id = format!("node_{}_{}", x_index, y_index); node_id_per_y.insert(y_index, node_id); } node_id_per_y_per_x.insert(x_index, node_id_per_y); } let mut exclusive_node_state_collections: Vec> = Vec::new(); for number in 1u8..10 { let mut node_state_ids: Vec = Vec::new(); for other_number in 1u8..10 { if other_number != number { node_state_ids.push(format!("state_{other_number}")); } } let node_state_collection = NodeStateCollection::new( format!("exclusive_{number}"), format!("state_{number}"), node_state_ids ); exclusive_node_state_collections.push(node_state_collection); } // all other node states are possible expect for "number" let mut possible_node_state_collection_per_number: HashMap> = HashMap::new(); for number in 1u8..10 { let mut node_state_ids: Vec = Vec::new(); for state in 1u8..10 { if state != number { node_state_ids.push(format!("state_{state}")); } } let node_state_collection = NodeStateCollection::new( format!("specific_{number}_possible"), format!("state_{number}"), node_state_ids ); possible_node_state_collection_per_number.insert(number, node_state_collection); } // no node states are possible if "number" let mut impossible_node_state_collection_per_number: HashMap> = HashMap::new(); for number in 1u8..10 { let node_state_collection = NodeStateCollection::new( format!("specific_{number}_impossible"), format!("state_{number}"), vec![] ); impossible_node_state_collection_per_number.insert(number, node_state_collection); } // when from node is "from number" only "destination state" is possible for neighbor let mut always_node_state_collection_per_to_number_per_from_number: HashMap>> = HashMap::new(); for from_number in 1u8..10 { let from_number_node_state_id = format!("state_{from_number}"); let mut always_node_state_collection_per_to_number: HashMap> = HashMap::new(); for to_number in 1u8..10 { let to_number_node_state_id = format!("state_{to_number}"); let node_state_collection = NodeStateCollection::new( format!("from_{from_number}_to_{to_number}"), from_number_node_state_id.clone(), vec![to_number_node_state_id] ); always_node_state_collection_per_to_number.insert(to_number, node_state_collection); } always_node_state_collection_per_to_number_per_from_number.insert(from_number, always_node_state_collection_per_to_number); } let mut node_state_collection_per_id: HashMap> = HashMap::new(); let mut nodes: Vec> = Vec::new(); for (from_x_index, from_number_per_row) in self.number_per_row_per_column.iter().enumerate() { for (from_y_index, from_number_option) in from_number_per_row.iter().enumerate() { let mut node_state_collection_ids_per_neighbor_node_id: HashMap> = HashMap::new(); for (to_x_index, to_number_per_row) in self.number_per_row_per_column.iter().enumerate() { for (to_y_index, to_number_option) in to_number_per_row.iter().enumerate() { if !(from_x_index == to_x_index && from_y_index == to_y_index) && ( from_x_index == to_x_index || from_y_index == to_y_index || ((from_x_index / 3) == (to_x_index / 3) && (from_y_index / 3) == (to_y_index / 3)) ) { //println!("Neighbors: ({from_x_index}, {from_y_index}) and ({to_x_index}, {to_y_index})"); let mut node_state_collection_ids: Vec = Vec::new(); if let Some(from_number) = from_number_option { // if the from node will already have a value if let Some(to_number) = to_number_option { // and the to node will already have a value // then when "from" is in the required state, only permit "to"'s required state // else when "from" is in any other state, permit nothing for possible_from_number in 1u8..10 { let node_state_collection: &NodeStateCollection; if possible_from_number == *from_number { node_state_collection = always_node_state_collection_per_to_number_per_from_number.get(&possible_from_number).unwrap().get(&to_number).unwrap(); } else { node_state_collection = impossible_node_state_collection_per_number.get(&possible_from_number).unwrap(); } //println!("When from is {possible_from_number} and to is {to_number} then {:?}", node_state_collection); node_state_collection_ids.push(node_state_collection.id.clone()); node_state_collection_per_id.insert(node_state_collection.id.clone(), node_state_collection.clone()); } } else { // and the "to" node is unknown // then when "from" is in the required state, permit all but that state // else when "from" is in any other state, permit nothing for possible_from_number in 1u8..10 { let node_state_collection: &NodeStateCollection; if possible_from_number == *from_number { node_state_collection = possible_node_state_collection_per_number.get(&possible_from_number).unwrap(); } else { node_state_collection = impossible_node_state_collection_per_number.get(&possible_from_number).unwrap(); } //println!("When from is {possible_from_number} and to is unknown then {:?}", node_state_collection); node_state_collection_ids.push(node_state_collection.id.clone()); node_state_collection_per_id.insert(node_state_collection.id.clone(), node_state_collection.clone()); } } } else { // the "from" node is unknown if let Some(to_number) = to_number_option { // and the "to" node is known // then for any possible value of "from" always require the "to" value for possible_from_number in 1u8..10 { let node_state_collection = always_node_state_collection_per_to_number_per_from_number.get(&possible_from_number).unwrap().get(to_number).unwrap(); //println!("When from is {possible_from_number} and to is {to_number} then {:?}", node_state_collection); node_state_collection_ids.push(node_state_collection.id.clone()); node_state_collection_per_id.insert(node_state_collection.id.clone(), node_state_collection.clone()); } } else { // and the "to" node is always unknown // then for any possible value of "from" permit anything but that value for "to" for node_state_collection in exclusive_node_state_collections.iter() { //println!("When from is unknown and to is unknown then {:?}", node_state_collection); node_state_collection_ids.push(node_state_collection.id.clone()); node_state_collection_per_id.insert(node_state_collection.id.clone(), node_state_collection.clone()); } } } let to_node_id = format!("node_{}_{}", to_x_index, to_y_index); node_state_collection_ids_per_neighbor_node_id.insert(to_node_id, node_state_collection_ids); } else { //println!("Not neighbors: ({from_x_index}, {from_y_index}) and ({to_x_index}, {to_y_index})"); } } } let mut node_state_ids: Vec = Vec::new(); for number in 1u8..10 { node_state_ids.push(format!("state_{number}")); } let node = Node::new( node_id_per_y_per_x.get(&from_x_index).unwrap().get(&from_y_index).unwrap().clone(), NodeStateProbability::get_equal_probability(&node_state_ids), node_state_collection_ids_per_neighbor_node_id ); nodes.push(node); } } let wave_function = WaveFunction::new(nodes, node_state_collection_per_id.values().cloned().collect()); wave_function.validate().unwrap(); let collapsed_wave_function_result = wave_function.get_collapsable_wave_function::>(None).collapse(); if let Ok(collapsed_wave_function) = collapsed_wave_function_result { let mut state_per_row_per_column: Vec>> = Vec::new(); for index in 1u8..10 { state_per_row_per_column.push(Vec::new()); for _ in 1u8..10 { state_per_row_per_column[(index as usize) - 1].push(None); } } for (node, node_state) in collapsed_wave_function.node_state_per_node_id.iter() { let node_string_split = node.split("_").collect::>(); let x_index = node_string_split[2].parse::().unwrap(); let y_index = node_string_split[1].parse::().unwrap(); let node_state_string_split = node_state.split("_").collect::>(); let state = node_state_string_split[1].parse::().unwrap(); state_per_row_per_column[y_index as usize][x_index as usize] = Some(state); } let solved_puzzle = SudokuPuzzle { number_per_row_per_column: state_per_row_per_column }; Ok(solved_puzzle) } else { Err(collapsed_wave_function_result.err().unwrap()) } } } fn main() { let start = Instant::now(); let mut number_per_row_per_column: Vec>> = Vec::new(); number_per_row_per_column.push(vec![None, Some(7), Some(3), Some(2), None, Some(4), Some(6), Some(9), Some(1)]); number_per_row_per_column.push(vec![None, Some(2), Some(8), None, None, Some(6), None, None, Some(7)]); number_per_row_per_column.push(vec![None, None, Some(6), Some(1), None, Some(7), None, None, Some(8)]); number_per_row_per_column.push(vec![None, Some(1), Some(5), Some(7), Some(6), Some(3), None, Some(2), Some(4)]); number_per_row_per_column.push(vec![Some(6), None, None, None, None, None, Some(8), Some(7), None]); number_per_row_per_column.push(vec![Some(7), None, None, Some(9), None, None, None, None, None]); number_per_row_per_column.push(vec![Some(3), None, Some(1), Some(6), None, None, None, None, None]); number_per_row_per_column.push(vec![Some(2), Some(8), None, Some(5), Some(4), Some(9), Some(3), None, None]); number_per_row_per_column.push(vec![None, Some(6), None, Some(8), None, None, None, None, None]); let puzzle = SudokuPuzzle { number_per_row_per_column: number_per_row_per_column }; puzzle.print(); let solution_result = puzzle.get_solution(); if let Ok(solution) = solution_result { solution.print(); } else { println!("Error: {}", solution_result.err().unwrap()); } let duration = start.elapsed(); println!("Duration: {:?}", duration); }