Worked on benchmarks
All checks were successful
Rust / build_and_test (push) Successful in 47s

This commit is contained in:
Lukas Wölfer
2025-07-31 10:09:38 +02:00
parent e5b5e31f46
commit aa5d621e00
9 changed files with 818 additions and 17 deletions

View File

@@ -4,4 +4,13 @@ version = "0.1.0"
edition = "2024"
[dependencies]
arbitrary = "1.4.1"
itertools = "0.14.0"
[dev-dependencies]
criterion = { version = "0.7", features = ["html_reports"] }
rand = "0.9.2"
[[bench]]
name = "painting"
harness = false

View File

@@ -0,0 +1,73 @@
use arbitrary::{Arbitrary, Unstructured};
use canvas::{Card, draw_painting};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use rand::{Rng, rngs::StdRng};
use std::{hint::black_box, time::Duration};
use crate::random_card::RandomCard;
mod random_card;
fn criterion_benchmark(c: &mut Criterion) {
let cards = vec![
Card::from_dbg_string(".ttt."),
Card::from_dbg_string("t.l.."),
Card::from_dbg_string("ll..pt"),
];
assert_eq!(draw_painting(&cards, &[]), Some([0, 1, 2]));
c.bench_function("painting optimizer", |b| {
b.iter(|| {
draw_painting(&black_box(cards.clone()), &[]);
});
});
}
fn random_card_pool<const N: usize>(rng: &mut StdRng) -> Vec<Card> {
<[RandomCard; N] as Arbitrary>::arbitrary(&mut Unstructured::new(&rng.random::<[u8; 32]>()))
.unwrap()
.map(|x| x.0)
.to_vec()
}
fn from_elem(c: &mut Criterion) {
use rand::SeedableRng;
// Define a seed. This can be any value that implements the `SeedableRng` trait.
let seed: [u8; 32] = [
8u64.to_be_bytes(),
8u64.to_be_bytes(),
8u64.to_be_bytes(),
8u64.to_be_bytes(),
]
.concat()
.try_into()
.unwrap();
// Create a new RNG with the specified seed.
let mut rng: StdRng = SeedableRng::from_seed(seed);
// Generate a random number using the seeded RNG.
let card_pool = [
random_card_pool::<4>(&mut rng),
random_card_pool::<8>(&mut rng),
random_card_pool::<16>(&mut rng),
random_card_pool::<32>(&mut rng),
random_card_pool::<64>(&mut rng),
random_card_pool::<128>(&mut rng),
];
let mut group = c.benchmark_group("from_elem");
for size in card_pool.iter() {
group.bench_with_input(BenchmarkId::from_parameter(size.len()), size, |b, size| {
b.iter(|| {
draw_painting(&black_box(size.clone()), &[])
});
});
}
group.finish();
}
criterion_group!(name = benches;
config = Criterion::default()
.warm_up_time(Duration::from_millis(100))
.measurement_time(Duration::from_millis(500));
targets = criterion_benchmark, from_elem);
criterion_main!(benches);

View File

@@ -0,0 +1,30 @@
use arbitrary::{Arbitrary, Unstructured};
use canvas::{Card, CardField};
#[derive(Debug, Clone)]
pub struct RandomCard(pub(crate) Card);
#[derive(Debug)]
struct RandomCardField(CardField);
impl<'a> Arbitrary<'a> for RandomCardField {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let i = u.int_in_range(0..=15)?;
if let Ok(w) = CardField::try_from_bits(i) {
Ok(RandomCardField(w))
} else {
Ok(RandomCardField(CardField::Empty))
}
}
}
impl<'a> Arbitrary<'a> for RandomCard {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let mut r = vec![];
for _ in 0..5 {
r.push(u.arbitrary::<RandomCardField>()?)
}
let w: [RandomCardField; 5] = r.try_into().unwrap();
let r = Card::from(w.map(|x: RandomCardField| x.0));
Ok(RandomCard(r))
}
}

View File

@@ -20,7 +20,7 @@ impl Iterator for CardFieldIter {
self.index += 1;
let bitmask = (1 << (CardField::BITPACKED_SIZE as u8)) - 1;
let result = (self.field >> (CardField::BITPACKED_SIZE as u8 * old_index)) & bitmask;
Some(CardField::from_bits(result as u8))
Some(CardField::try_from_bits(result as u8).unwrap())
} else {
None
}

View File

@@ -1,5 +1,5 @@
#![allow(dead_code)]
#![warn(clippy::unwrap_used, clippy::expect_used)]
use crate::objective::Objective;
mod card;
@@ -7,7 +7,9 @@ mod icon_bitfield;
mod objective;
mod optimizer;
use card::Card;
pub use card::Card;
pub use optimizer::draw_painting;
#[derive(Debug)]
struct Shop {
@@ -46,25 +48,29 @@ pub enum CardField {
}
impl CardField {
const BITPACKED_SIZE: usize = 4;
fn from_bits(bits: u8) -> Self {
pub const BITPACKED_SIZE: usize = 4;
pub fn try_from_bits(bits: u8) -> Result<Self, ()> {
{
if bits == 0b1111 {
Self::Empty
let bits = bits & ((1 << Self::BITPACKED_SIZE) - 1);
if (bits) == 0b1111 {
Ok(Self::Empty)
} else if (bits & (1 << 3)) != 0 {
let bits = bits & ((1 << 3) - 1);
if bits == 5 {
Self::DoubleIcon(Icon::Triangle, Icon::Color)
Ok(Self::DoubleIcon(Icon::Triangle, Icon::Color))
} else if bits >= 3 {
Self::DoubleIcon(Icon::Square, Icon::try_from(bits + 2 - 3).unwrap())
Ok(Self::DoubleIcon(
Icon::Square,
Icon::try_from(bits + 2 - 3)?,
))
} else {
Self::DoubleIcon(Icon::Circle, Icon::try_from(bits + 1).unwrap())
Ok(Self::DoubleIcon(Icon::Circle, Icon::try_from(bits + 1)?))
}
} else if bits & (1 << 2) != 0 {
let bits = bits & ((1 << 2) - 1);
Self::PointsPer(Icon::try_from(bits).unwrap())
Ok(Self::PointsPer(Icon::try_from(bits)?))
} else {
Self::Icon(Icon::try_from(bits).unwrap())
Ok(Self::Icon(Icon::try_from(bits)?))
}
}
}
@@ -139,7 +145,7 @@ impl PointThresholds {
}
#[derive(Debug)]
struct CriteriaCard {
pub struct CriteriaCard {
objective: Objective,
// Points depending on how often the criteria was met
points: PointThresholds,

View File

@@ -31,7 +31,7 @@ impl Iterator for OneIndexIterator {
}
}
fn draw_painting(cards: &[Card], obj: &[(&CriteriaCard, u8)]) -> Option<[usize; 3]> {
pub fn draw_painting(cards: &[Card], obj: &[(&CriteriaCard, u8)]) -> Option<[usize; 3]> {
let bitfields = cards.iter().map(IconBitfield::new).collect::<Vec<_>>();
let mut best: Option<([usize; 3], u8)> = None;
for stack in (0..cards.len()).permutations(3) {
@@ -63,7 +63,6 @@ fn draw_painting(cards: &[Card], obj: &[(&CriteriaCard, u8)]) -> Option<[usize;
.sum::<u8>();
let total_points = bonus_points + criteria_points;
let stack_result = (stack_indices, total_points);
dbg!(stack_result);
if let Some(bp) = &mut best {
if bp.1 < total_points {
*bp = stack_result;