initial commit

This commit is contained in:
2026-05-15 19:55:37 -04:00
commit 590995f969
6 changed files with 272 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
Generated
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "wynn-mounts"
version = "0.1.0"
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "wynn-mounts"
version = "0.1.0"
edition = "2024"
[dependencies]
+64
View File
@@ -0,0 +1,64 @@
pub type Int = i16;
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub struct Mount {
pub speed: Int,
pub accel: Int,
pub altitude: Int,
pub energy: Int,
pub handling: Int,
pub toughness: Int,
pub boost: Int,
pub training: Int,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Mat {
Ingot,
Gem,
Wood,
Paper,
String,
Grains,
Oil,
Meat,
}
macro_rules! mtch {
($struct:ident $($mat:ident {$($field:ident $amt:expr)*},)*) => {
impl $struct {
pub fn apply(&mut self, mat: Mat) {
match mat {
$(Mat::$mat => {
$(self.$field -= $amt;)*
},)*
}
}
pub fn undo(&mut self, mat: Mat) {
match mat {
$(Mat::$mat => {
$(self.$field += $amt;)*
},)*
}
}
pub fn useless(&self, mat: Mat) -> bool {
match mat {
$(Mat::$mat => {
$(self.$field <= 0 &&)* true
},)*
}
}
}
};
}
mtch! { Mount
Ingot { energy 4 toughness 8 },
Gem { speed 4 energy 2 training 6 },
Wood { speed 2 accel 6 toughness 4 },
Paper { altitude 8 boost 4 },
String { accel 2 handling 4 boost 6 },
Grains { speed 8 altitude 4 },
Oil { altitude 2 handling 6 training 4 },
Meat { accel 4 energy 8 },
}
+141
View File
@@ -0,0 +1,141 @@
mod data;
mod solve;
pub use data::*;
pub use solve::*;
fn main() {
let mut priority = vec![
Mat::Wood,
Mat::Paper,
Mat::Grains,
Mat::String,
Mat::Oil,
Mat::Meat,
Mat::Ingot,
Mat::Gem,
];
print_priority(&priority);
let input = std::io::stdin();
for line in input.lines() {
let Ok(line) = line else {
println!("error reading line");
continue;
};
let Some((cmd, args)) = line.split_once(" ") else {
println!("space expected after command");
continue;
};
match cmd {
"solve" => run_solve(args, &priority),
"priority" => run_priority(args, &mut priority),
_ => {
println!("unknown command");
}
}
}
}
fn run_solve(args: &str, priority: &Priority) {
let parts = args.split(" ");
let mut attrs = [0; 8];
let mut i = 0;
for part in parts {
if part.is_empty() {
continue;
}
if i == 8 {
i += 1;
break;
}
let Some((limit, max)) = part.split_once("/") else {
println!("bad stat input");
return;
};
let Ok(limit): Result<Int, _> = limit.parse() else {
println!("bad stat input");
return;
};
let Ok(max): Result<Int, _> = max.parse() else {
println!("bad stat input");
return;
};
attrs[i] = max - limit;
i += 1;
}
if i != 8 {
println!("wrong number of parts ({i}, need 8)");
return;
}
let mount = Mount {
speed: attrs[0],
accel: attrs[1],
altitude: attrs[2],
energy: attrs[3],
handling: attrs[4],
toughness: attrs[5],
boost: attrs[6],
training: attrs[7],
};
println!("requirements: {mount:?}");
let res = solve(mount, priority);
println!("{}: {res:?}", res.len());
}
fn run_priority(args: &str, priority: &mut Priority) {
let parts = args.split(" ");
let mut new = Vec::new();
for (i, part) in parts.enumerate() {
if part.is_empty() {
continue;
}
if i == 8 {
println!("too many materials provided");
return;
}
new.push(match part.to_lowercase().as_str() {
"ingot" => Mat::Ingot,
"gem" => Mat::Gem,
"wood" => Mat::Wood,
"paper" => Mat::Paper,
"string" => Mat::String,
"grains" => Mat::Grains,
"oil" => Mat::Oil,
"meat" => Mat::Meat,
_ => {
println!("unknown material {part}");
continue;
}
});
}
if validate_priority(&new) {
*priority = new;
print_priority(priority);
} else {
println!("invalid priority (2+ of a material)");
}
}
fn print_priority(priority: &Priority) {
println!("priority:");
for (i, mat) in priority.iter().enumerate() {
println!(" {}: {mat:?}", i + 1);
}
}
fn validate_priority(priority: &Priority) -> bool {
let mut used = std::collections::HashSet::new();
for p in priority {
if !used.insert(p) {
return false;
}
}
true
}
fn usage() {
println!("Usage:");
println!(" > solve 41/63 50/60 55/55 43/64 60/66 51/55 48/59 41/63");
println!(" solves for the least number of materials needed given a priority");
println!(" > priority wood paper grains string oil meat");
println!(" sets the material priority for solving (what to use and what to try first)");
}
+53
View File
@@ -0,0 +1,53 @@
pub use crate::data::*;
pub fn solve(mut mount: Mount, priority: &Priority) -> Vec<Mat> {
let mut best = Vec::new();
let mut stack = vec![0];
loop {
let mut mat_i = *stack.last().unwrap();
let mat = priority[mat_i];
if !mount.useless(mat) {
mount.apply(mat);
if mount.finished() {
if best.is_empty() || stack.len() < best.len() {
best = stack.clone();
}
} else if best.is_empty() || stack.len() + 1 < best.len() {
stack.push(mat_i);
continue;
}
mount.undo(mat);
}
while mat_i == priority.len() - 1 {
stack.pop();
if let Some(&prev) = stack.last() {
mat_i = prev;
} else {
return best.into_iter().map(|i| priority[i]).collect();
}
mount.undo(priority[mat_i]);
}
*stack.last_mut().unwrap() += 1;
}
}
impl Mat {
pub fn first() -> Self {
Self::Wood
}
}
impl Mount {
pub fn finished(&self) -> bool {
self.speed <= 0
&& self.accel <= 0
&& self.altitude <= 0
&& self.energy <= 0
&& self.handling <= 0
&& self.toughness <= 0
&& self.boost <= 0
&& self.training <= 0
}
}
pub type Priority = Vec<Mat>;