use std::collections::HashMap;
use super::{Key, PlayerSide};
use crate::bms::rng::JavaRandom;
pub trait KeyConverter {
fn convert(&mut self, key: Key) -> Key;
}
pub trait PlayerSideKeyConverter {
fn convert(&mut self, pair: (PlayerSide, Key)) -> (PlayerSide, Key);
}
impl KeyMappingConvertMirror {
#[must_use]
pub const fn new(keys: Vec<Key>) -> Self {
Self { keys }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyMappingConvertMirror {
keys: Vec<Key>,
}
impl KeyConverter for KeyMappingConvertMirror {
fn convert(&mut self, key: Key) -> Key {
self.keys
.iter()
.position(|k| k == &key)
.and_then(|position| {
let mirror_index = self.keys.len().saturating_sub(position + 1);
self.keys.get(mirror_index)
})
.copied()
.unwrap_or(key)
}
}
#[derive(Debug, Clone)]
pub struct KeyMappingConvertLaneRotateShuffle {
arrangement: HashMap<Key, Key>,
}
impl KeyMappingConvertLaneRotateShuffle {
#[must_use]
pub fn new(keys: &[Key], seed: i64) -> Self {
Self {
arrangement: Self::make_random(keys, seed),
}
}
fn make_random(keys: &[Key], seed: i64) -> HashMap<Key, Key> {
let mut rng = JavaRandom::new(seed);
let mut result: HashMap<Key, Key> = HashMap::new();
if keys.is_empty() {
return result;
}
if let &[key] = keys {
result.insert(key, key);
return result;
}
let inc = rng.next_int_bound(2) == 1;
let start = rng.next_int_bound(keys.len() as i32 - 1) as usize + usize::from(inc);
let mut rlane = start;
for &lane_key in keys {
let Some(&mapped_key) = keys.get(rlane) else {
break;
};
result.insert(lane_key, mapped_key);
rlane = if inc {
(rlane + 1) % keys.len()
} else {
(rlane + keys.len() - 1) % keys.len()
};
}
result
}
}
impl KeyConverter for KeyMappingConvertLaneRotateShuffle {
fn convert(&mut self, key: Key) -> Key {
self.arrangement.get(&key).copied().unwrap_or(key)
}
}
#[derive(Debug, Clone)]
pub struct KeyMappingConvertLaneRandomShuffle {
arrangement: HashMap<Key, Key>,
}
impl KeyMappingConvertLaneRandomShuffle {
#[must_use]
pub fn new(keys: &[Key], seed: i64) -> Self {
Self {
arrangement: Self::make_random(keys, seed),
}
}
fn make_random(keys: &[Key], seed: i64) -> HashMap<Key, Key> {
let mut rng = JavaRandom::new(seed);
let mut result: HashMap<Key, Key> = HashMap::new();
if keys.is_empty() {
return result;
}
let mut l = keys.to_vec();
for &lane in keys {
let r = rng.next_int_bound(l.len() as i32) as usize;
let mapped = l.swap_remove(r);
result.insert(lane, mapped);
}
result
}
}
impl KeyConverter for KeyMappingConvertLaneRandomShuffle {
fn convert(&mut self, key: Key) -> Key {
self.arrangement.get(&key).copied().unwrap_or(key)
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyMappingConvertFlip;
impl PlayerSideKeyConverter for KeyMappingConvertFlip {
fn convert(&mut self, pair: (PlayerSide, Key)) -> (PlayerSide, Key) {
let (side, key) = pair;
let flipped_side = match side {
PlayerSide::Player1 => PlayerSide::Player2,
PlayerSide::Player2 => PlayerSide::Player1,
};
(flipped_side, key)
}
}
#[cfg(test)]
mod channel_mode_tests {
use super::*;
#[test]
fn test_key_converter_mirror() {
let mut converter =
KeyMappingConvertMirror::new(vec![Key::Key(1), Key::Key(2), Key::Key(3)]);
let keys = vec![
Key::Key(1),
Key::Key(2),
Key::Key(3),
Key::Key(4),
Key::Key(5),
];
let expected_keys = vec![
Key::Key(3),
Key::Key(2),
Key::Key(1),
Key::Key(4),
Key::Key(5),
];
let result: Vec<_> = keys.into_iter().map(|key| converter.convert(key)).collect();
assert_eq!(result, expected_keys);
}
fn parse_examples(examples_str: &[&str]) -> Vec<(Vec<usize>, i64)> {
examples_str
.iter()
.map(|s| {
let v = s.split_whitespace().collect::<Vec<_>>();
let [list, seed] = v.as_slice() else {
println!("{v:?}");
panic!("Invalid input: expected [list, seed]");
};
let list = list
.chars()
.map(|c| c.to_digit(10).unwrap() as usize)
.collect::<Vec<_>>();
let seed = seed.parse::<i64>().unwrap();
(list, seed)
})
.collect::<Vec<_>>()
}
fn key_to_value(key: Key) -> usize {
match key {
Key::Key(n) => n as usize,
Key::Scratch(n) => n as usize + 10,
Key::FootPedal => 20,
Key::FreeZone => 21,
}
}
fn run_shuffle_test_case<T>(
test_case_idx: usize,
expected_list: &[usize],
seed: i64,
keys: &[Key],
mut converter: T,
) where
T: KeyConverter,
{
println!("Test case {test_case_idx}: seed = {seed}");
let result_values = keys
.iter()
.map(|&key| key_to_value(converter.convert(key)))
.collect::<Vec<_>>();
println!(" Expected: {expected_list:?}");
println!(" Got: {result_values:?}");
println!(" Match: {}", result_values == expected_list);
if result_values != expected_list {
println!(" FAILED!");
}
println!();
}
#[test]
fn test_random_shuffle() {
let examples_str = [
"1234567 4752",
"1234576 2498",
"4372615 12728",
"4372651 9734",
"4375126 139",
];
let examples = parse_examples(&examples_str);
let init_keys = [
Key::Key(1),
Key::Key(2),
Key::Key(3),
Key::Key(4),
Key::Key(5),
Key::Key(6),
Key::Key(7),
];
for (i, (list, seed)) in examples.iter().enumerate() {
let rnd = KeyMappingConvertLaneRandomShuffle::new(&init_keys, *seed);
run_shuffle_test_case(i, list, *seed, &init_keys, rnd);
}
}
#[test]
fn test_lane_rotate_shuffle() {
let examples_str = ["1765432 3581225"];
let examples = parse_examples(&examples_str);
let init_keys = [
Key::Key(1),
Key::Key(2),
Key::Key(3),
Key::Key(4),
Key::Key(5),
Key::Key(6),
Key::Key(7),
];
for (i, (list, seed)) in examples.iter().enumerate() {
let rnd = KeyMappingConvertLaneRotateShuffle::new(&init_keys, *seed);
run_shuffle_test_case(i, list, *seed, &init_keys, rnd);
}
}
#[test]
fn test_player_side_key_converter_flip() {
let mut converter = KeyMappingConvertFlip;
let test_cases = vec![
(PlayerSide::Player1, Key::Key(1)),
(PlayerSide::Player2, Key::Key(2)),
(PlayerSide::Player1, Key::Scratch(1)),
(PlayerSide::Player2, Key::FreeZone),
];
let input_pairs: Vec<_> = test_cases.clone();
let expected_pairs: Vec<_> = test_cases
.iter()
.map(|(side, key)| {
let flipped_side = match side {
PlayerSide::Player1 => PlayerSide::Player2,
PlayerSide::Player2 => PlayerSide::Player1,
};
(flipped_side, *key)
})
.collect();
let result: Vec<_> = input_pairs
.iter()
.map(|&pair| converter.convert(pair))
.collect();
assert_eq!(result, expected_pairs);
let result2: Vec<_> = result.iter().map(|&pair| converter.convert(pair)).collect();
assert_eq!(result2, input_pairs);
}
}