This commit is contained in:
2022-02-05 19:00:27 +01:00
parent f0fc466452
commit 1a78cec632
4 changed files with 361 additions and 1 deletions

View File

@ -0,0 +1,222 @@
use clap::Parser;
use std::collections::HashSet;
use std::collections::HashMap;
use rand::distributions::{Distribution, Uniform};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
#[derive(FromPrimitive)]
enum Operator {
Add,
Subtract,
Multiplicate,
Divide,
}
#[derive(FromPrimitive, PartialEq, Clone, Copy)]
enum AddSubOrMultiplicateDivide {
AddSub,
MultiplicateDivide,
}
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Args {
/// maximale Anzahl der auszugebenden Lösungen
#[clap(short, long, default_value_t = 1)]
maximum: usize,
/// Anzahl der Operatoren
#[clap(short, long, default_value_t = 5)]
count: u8,
/// zeigt die Lösung an
#[clap(short, long, takes_value = false)]
solution_print: bool,
}
type RiddleMap = HashMap<i64, u64>;
struct PartOperation {
results: RiddleMap,
last_operator: Option<usize>,
}
struct ResultStore {
riddles: RiddleMap,
results_already_taken: HashSet<i64>,
}
impl ResultStore {
fn new() -> Self {
Self{
riddles: HashMap::new(),
results_already_taken: HashSet::new(),
}
}
fn store(&mut self, result: i64, operators: u64) {
if self.results_already_taken.contains(&result) {return;}
match self.riddles.remove(&result) {
None => {self.riddles.insert(result, operators);}
Some(_x) => {self.results_already_taken.insert(result);}
}
}
}
fn main() {
let args = Args::parse();
let mut digits: Vec<u8> = vec!(0; (args.count + 1) as usize);
{
let mut selectable_digits: Vec<u8> = Vec::from_iter(0..9);
let mut digits_count: [u8; 9] = [0; 9];
let min: u8 = digits.len() as u8 / 16;
let max: u8 = digits.len() as u8 / 4 + 1;
let mut left_for_non_min: u8 = digits.len() as u8 - 9 * min;
let mut digits_min_statisfied: HashSet<u8> = HashSet::with_capacity(9);
for digit in digits.iter_mut() {
if left_for_non_min == 0 {
selectable_digits.retain(|x| !digits_min_statisfied.contains(x));
}
let rand_i = Uniform::new(0, selectable_digits.len()).sample(&mut rand::thread_rng());
let rand_number = selectable_digits[rand_i];
digits_count[rand_number as usize] += 1;
let digit_used = digits_count[rand_number as usize];
if digit_used == min {
if left_for_non_min == 0 {
selectable_digits.remove(rand_i);
} else {digits_min_statisfied.insert(rand_number);}
}
if digit_used > min {left_for_non_min -= 1;}
if digit_used == max {selectable_digits.remove(rand_i);}
*digit = rand_number + 1;
}
}
{
let mut results = ResultStore::new();
for dm_as_map in 0..2u64.pow(args.count as u32) {
let mut results_multiplicate: Vec<PartOperation> = Vec::new();
{
let mut last_i: usize = 0;
let mut i: usize;
let mut state: AddSubOrMultiplicateDivide = FromPrimitive::from_u64(dm_as_map & 1).unwrap();
while last_i < args.count as usize {
i = last_i;
while i < args.count as usize && dm_as_map >> i & 1 == state as u64 {
if state == AddSubOrMultiplicateDivide::AddSub {
results_multiplicate.push(insert_digit(&digits, i));
}
i += 1;
}
if state == AddSubOrMultiplicateDivide::MultiplicateDivide {
let mut part_results = ResultStore::new();
i += 1;
let mut digits_calc = digits[last_i .. i].iter();
let first_digit = *digits_calc.next().unwrap() as i64;
calc_part(digits_calc, &mut part_results, last_i as u8 * 2, first_digit, 0);
results_multiplicate.push(PartOperation{
results: part_results.riddles,
last_operator: last_operator_helper(last_i),
});
}
state = match state {
AddSubOrMultiplicateDivide::MultiplicateDivide => AddSubOrMultiplicateDivide::AddSub,
AddSubOrMultiplicateDivide::AddSub => AddSubOrMultiplicateDivide::MultiplicateDivide,
};
last_i = i;
}
if dm_as_map >> args.count - 1 & 1 == AddSubOrMultiplicateDivide::AddSub as u64 {
results_multiplicate.push(insert_digit(&digits, digits.len() - 1));
}
{
let mut iter = results_multiplicate.iter();
for (part_result, operators) in &iter.next().unwrap().results {
add_sub(iter.clone(), &mut results, *part_result, *operators);
}
}
}
}
let mut not_first = false;
for (result, operators) in results.riddles {
if not_first {println!();}
not_first = true;
print_riddle(&digits, false, result, operators);
if args.solution_print {
print_riddle(&digits, true, result, operators);
}
}
}
}
fn calc_part<'a>(mut iter: impl Clone + Iterator<Item = &'a u8>, results: &mut ResultStore, operator_index: u8, part_result: i64, operators: u64) {
match iter.next() {
None => results.store(part_result, operators),
Some(next) => {
let next_digit = *next as i64;
for operator in [Operator::Multiplicate, Operator::Divide] {
calc_part(iter.clone(), results, operator_index + 2,
match operator {
Operator::Multiplicate => part_result * next_digit,
Operator::Divide => {
if part_result % next_digit != 0 {continue;}
part_result / next_digit
}
_ => 0,
},
operators | (operator as u64) << operator_index);
}
}
}
}
fn add_sub<'a>(mut iter: impl Clone + Iterator<Item = &'a PartOperation>, results: &mut ResultStore, part_result: i64, operators: u64) {
match iter.next() {
None => {
if part_result > 0 {results.store(part_result, operators);}
}
Some(next) => {
for (next_result, part_operators) in &next.results {
for operator in [Operator::Add, Operator::Subtract] {
add_sub(iter.clone(), results,
match operator {
Operator::Add => part_result + next_result,
Operator::Subtract => part_result - next_result,
_ => 0,
},
operators | part_operators | (operator as u64) << 2 * next.last_operator.unwrap()
);
}
}
}
}
}
fn last_operator_helper(index: usize) -> Option<usize> {
if index == 0 {None} else {Some(index as usize - 1)}
}
fn insert_digit(digits: &Vec<u8>, i: usize) -> PartOperation {
let mut result_map = RiddleMap::new();
result_map.insert(digits[i] as i64, 0);
return PartOperation{
results: result_map,
last_operator: last_operator_helper(i),
};
}
fn print_riddle(digits: &Vec<u8>, show_solution: bool, result: i64, operators: u64) {
print!("{}: ", if show_solution {"Lösung"} else {"Rätsel"});
let mut i = 0;
for digit in digits[.. digits.len() - 1].iter() {
print!("{} {} ", digit, if show_solution {
match FromPrimitive::from_u64(operators >> i & 3).unwrap() {
Operator::Multiplicate => '*',
Operator::Divide => ':',
Operator::Add => '+',
Operator::Subtract => '-',
}
} else {'○'});
i += 2;
}
println!("{} = {}", digits.last().unwrap(), result);
}