162 lines
5.6 KiB
Rust
162 lines
5.6 KiB
Rust
use std::fs;
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Move {
|
|
digit: u8,
|
|
swaps: PutTaken,
|
|
}
|
|
|
|
pub struct DigitHistory {
|
|
pub original_digit: u8,
|
|
move_option: Option<Move>,
|
|
}
|
|
|
|
pub struct ChangedSticks {
|
|
pub digit_i: usize,
|
|
pub stick_map: u8,
|
|
}
|
|
|
|
#[derive(Clone, Copy, derive_more::SubAssign, derive_more::AddAssign, derive_more::Add)]
|
|
struct PutTaken {
|
|
put: u16,
|
|
taken: u16,
|
|
}
|
|
|
|
struct ChangeDigitsControl {
|
|
change_digit: bool,
|
|
break_free: bool,
|
|
}
|
|
|
|
pub const HEX: [u8; 16] = [
|
|
0b1110111, // 0
|
|
0b0010010, // 1
|
|
0b1011101, // 2
|
|
0b1011011, // 3
|
|
0b0111010, // 4
|
|
0b1101011, // 5
|
|
0b1101111, // 6
|
|
0b1010010, // 7
|
|
0b1111111, // 8
|
|
0b1111011, // 9
|
|
0b1111110, // A
|
|
0b0101111, // B
|
|
0b1100101, // C
|
|
0b0011111, // D
|
|
0b1101101, // E
|
|
0b1101100, // F
|
|
];
|
|
|
|
pub fn largest_hex(filepath: &str) -> (Vec<DigitHistory>, u16) {
|
|
let mut digits: Vec<DigitHistory>;
|
|
let max_swaps: u16;
|
|
{
|
|
let content = fs::read_to_string(filepath).unwrap();
|
|
let mut lines = content.lines();
|
|
let line = lines.next().unwrap();
|
|
digits = Vec::with_capacity(line.len());
|
|
for character in line.chars() {
|
|
digits.push(DigitHistory {
|
|
original_digit: character.to_digit(16).unwrap() as u8,
|
|
move_option: None,
|
|
});
|
|
}
|
|
max_swaps = lines.next().unwrap().parse().unwrap();
|
|
}
|
|
{
|
|
let mut total_swaps = PutTaken{ put: 0, taken: 0};
|
|
for i in 0..digits.len() {
|
|
if total_swaps.taken == total_swaps.put && total_swaps.put == max_swaps {break;}
|
|
total_swaps = change_digits(digits.iter_mut(), total_swaps, max_swaps, (), true, |_, _, _| {
|
|
ChangeDigitsControl {change_digit: true, break_free: true}
|
|
});
|
|
if total_swaps.put != total_swaps.taken {
|
|
total_swaps = change_digits(digits.iter_mut().rev(), total_swaps, max_swaps, i16::MAX, false, |new_total_swaps, total_swaps, best_diff| {
|
|
let new_diff = (new_total_swaps.put as i16 - new_total_swaps.taken as i16).abs();
|
|
let change_digit = new_diff < (total_swaps.put as i16 - total_swaps.taken as i16).abs()
|
|
&& new_diff < *best_diff;
|
|
if change_digit {*best_diff = new_diff;}
|
|
ChangeDigitsControl {change_digit, break_free: false}
|
|
});
|
|
}
|
|
}
|
|
println!("taken {}, put {}", total_swaps.taken, total_swaps.put);
|
|
(digits, max_swaps)
|
|
}
|
|
}
|
|
|
|
pub fn to_hex_str(digits: &[DigitHistory]) -> String {
|
|
let mut largest_hex_str = String::with_capacity(digits.len());
|
|
for digit in digits {
|
|
largest_hex_str += &format!("{:X}", digit.move_option.map_or(digit.original_digit, |m| m.digit));
|
|
}
|
|
largest_hex_str
|
|
}
|
|
|
|
pub fn gen_swaps(digits: &[DigitHistory], number_of_swaps: usize) -> (Vec<ChangedSticks>, Vec<ChangedSticks>) {
|
|
let mut moves_put: Vec<ChangedSticks> = Vec::with_capacity(number_of_swaps);
|
|
let mut moves_taken: Vec<ChangedSticks> = Vec::with_capacity(number_of_swaps);
|
|
for (j, DigitHistory {original_digit, move_option}) in digits.iter().enumerate() {
|
|
if let Some(moved) = move_option {
|
|
let from_hex = HEX[*original_digit as usize];
|
|
let to_hex = HEX[moved.digit as usize];
|
|
let changed = from_hex ^ to_hex;
|
|
let put = changed & to_hex;
|
|
let taken = changed & from_hex;
|
|
for (put_or_taken, moves_put_or_taken) in [(put, &mut moves_put), (taken, &mut moves_taken)] {
|
|
for i in 0..u8::BITS {
|
|
let stick_map = put_or_taken & 1 << i;
|
|
if stick_map != 0 {
|
|
moves_put_or_taken.push(ChangedSticks {
|
|
digit_i: j,
|
|
stick_map,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
(moves_put, moves_taken)
|
|
}
|
|
|
|
fn change_digits<'a, I: Iterator<Item = &'a mut DigitHistory>, T: Copy,
|
|
F: Fn(PutTaken, PutTaken, &mut T) -> ChangeDigitsControl>
|
|
(iter: I, mut total_swaps: PutTaken, max_swaps: u16, init_t: T, higher_digit: bool, run: F) -> PutTaken {
|
|
for DigitHistory {original_digit, move_option} in iter {
|
|
if higher_digit && move_option.is_some() {continue;}
|
|
let mut swaps_with_undo = total_swaps;
|
|
if let Some(moved) = move_option {
|
|
swaps_with_undo -= moved.swaps;
|
|
}
|
|
let digit_hex = HEX[*original_digit as usize];
|
|
let mut best_move: Option<Move> = None;
|
|
let mut t = init_t;
|
|
for (j, to_digit) in HEX.iter().enumerate().rev() {
|
|
if j as u8 == *original_digit {
|
|
if higher_digit {break;}
|
|
else {continue;}
|
|
}
|
|
let changed_sticks = digit_hex ^ *to_digit;
|
|
let swaps = PutTaken {
|
|
put: (changed_sticks & *to_digit).count_ones() as u16,
|
|
taken: (changed_sticks & digit_hex).count_ones() as u16,
|
|
};
|
|
let new_total_swaps = swaps_with_undo + swaps;
|
|
if new_total_swaps.put <= max_swaps && new_total_swaps.taken <= max_swaps {
|
|
let control = run(new_total_swaps, total_swaps, &mut t);
|
|
if control.change_digit {
|
|
best_move = Some(Move {
|
|
digit: j as u8,
|
|
swaps,
|
|
});
|
|
}
|
|
if control.break_free {break;}
|
|
}
|
|
}
|
|
if let Some(moved) = best_move {
|
|
*move_option = best_move;
|
|
total_swaps = swaps_with_undo + moved.swaps;
|
|
}
|
|
}
|
|
total_swaps
|
|
}
|