The codebase I'm contributing to uses const generics on the Frame
type to improve data locality/performance. However the type is very clumsy to use. I plan to write a lot of functions like percent_grounded
which compute stats about the game based on frame data, but I want to find a better way to do this than crazy case statements with nearly identical code. Is there an idiomatic way to reduce repetition like this with/without macros?
fn main() {
let player_data: [Data; 1] = [Data { grounded: true }];
let frame: Frame<1> = Frame { player_data };
let frames = Frames::P1(vec![frame]);
println!("Percent grounded: {:?}", percent_grounded(&frames));
}
pub struct Data {
pub grounded: bool
// ...
}
// Single frame of the game. Const generics allows a 2-player game (most common)
// to take up half the space of a 4-player game (max). This is better for data
// locality/performance
pub struct Frame<const N: usize> {
pub player_data: [Data; N],
//pub index: i32,
// ...
}
// Encapsulates the frame data from a game
pub enum Frames {
P1(Vec<Frame<1>>),
P2(Vec<Frame<2>>),
P3(Vec<Frame<3>>),
P4(Vec<Frame<4>>),
}
// However this is where the tedium begins. I'd like to avoid having to write
// code like this. Macros could be a decent start.
impl Frames {
pub fn len(&self) -> usize {
match self {
Self::P1(frames) => frames.len(),
Self::P2(frames) => frames.len(),
Self::P3(frames) => frames.len(),
Self::P4(frames) => frames.len(),
}
}
// ...
}
// The Frames enum contains important data, so this sort of repetitiveness
// would permeate everywhere.
pub fn percent_grounded(frames: &Frames) -> Vec<f32> {
let mut grounded_percentages = Vec::new();
let total_frames = frames.len();
match frames {
Frames::P1(fs) => {
grounded_percentages.resize(1, 0.0);
for f in fs {
if f.player_data[0].grounded {
grounded_percentages[0] += 1.0 / total_frames as f32;
}
}
}
Frames::P2(_fs) => {
// ... would have to repeat pretty much same code above
}
Frames::P3(_fs) => {
// ...
}
Frames::P4(_fs) => {
// ...
}
}
grounded_percentages
}
Output:
Percent grounded: [1.0]
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.69s
Running `target/debug/playground`