This commit is contained in:
@@ -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
|
||||
|
||||
73
canvas/benches/painting.rs
Normal file
73
canvas/benches/painting.rs
Normal 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);
|
||||
30
canvas/benches/random_card.rs
Normal file
30
canvas/benches/random_card.rs
Normal 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))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user