Created
February 22, 2024 06:38
-
-
Save herabit/0231413739e418f65108446446613096 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::iter::FusedIterator; | |
use enumset::EnumSetType; | |
#[derive(Debug, PartialOrd, Ord, Hash, EnumSetType)] | |
pub enum Horizontal { | |
None = 0, | |
East, | |
West, | |
Both, | |
} | |
impl Horizontal { | |
pub const ALL: [Horizontal; 4] = [ | |
Horizontal::None, | |
Horizontal::East, | |
Horizontal::West, | |
Horizontal::Both, | |
]; | |
#[inline(always)] | |
pub const fn len(self) -> u32 { | |
match self { | |
Horizontal::None => 0, | |
Horizontal::East | Horizontal::West => 1, | |
Horizontal::Both => 2, | |
} | |
} | |
#[inline(always)] | |
pub const fn complement(self) -> Horizontal { | |
match self { | |
Horizontal::None => Horizontal::Both, | |
Horizontal::East => Horizontal::West, | |
Horizontal::West => Horizontal::East, | |
Horizontal::Both => Horizontal::None, | |
} | |
} | |
#[inline(always)] | |
pub const fn flip(self) -> Horizontal { | |
match self { | |
Horizontal::None => Horizontal::None, | |
Horizontal::East => Horizontal::West, | |
Horizontal::West => Horizontal::East, | |
Horizontal::Both => Horizontal::Both, | |
} | |
} | |
} | |
#[derive(Debug, PartialOrd, Ord, Hash, EnumSetType)] | |
pub enum Vertical { | |
None = 0, | |
North, | |
South, | |
Both, | |
} | |
impl Vertical { | |
pub const ALL: [Vertical; 4] = [ | |
Vertical::None, | |
Vertical::North, | |
Vertical::South, | |
Vertical::Both, | |
]; | |
#[inline(always)] | |
pub const fn len(self) -> u32 { | |
match self { | |
Vertical::None => 0, | |
Vertical::North | Vertical::South => 1, | |
Vertical::Both => 2, | |
} | |
} | |
#[inline(always)] | |
pub const fn complement(self) -> Vertical { | |
match self { | |
Vertical::None => Vertical::Both, | |
Vertical::North => Vertical::South, | |
Vertical::South => Vertical::North, | |
Vertical::Both => Vertical::None, | |
} | |
} | |
#[inline(always)] | |
pub const fn flip(self) -> Vertical { | |
match self { | |
Vertical::None => Vertical::None, | |
Vertical::North => Vertical::South, | |
Vertical::South => Vertical::North, | |
Vertical::Both => Vertical::Both, | |
} | |
} | |
} | |
#[derive(Debug, PartialOrd, Ord, Hash, EnumSetType)] | |
pub enum Direction { | |
North = 0, | |
South, | |
East, | |
West, | |
} | |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
pub struct Tile(Vertical, Horizontal); | |
impl Tile { | |
pub const COUNT: usize = { | |
let mut count = 0; | |
let mut h_index = 0; | |
while h_index < Horizontal::ALL.len() { | |
let mut v_index = 0; | |
while v_index < Vertical::ALL.len() { | |
if Tile::new(Vertical::ALL[v_index], Horizontal::ALL[h_index]).is_some() { | |
count += 1; | |
} | |
v_index += 1; | |
} | |
h_index += 1; | |
} | |
count | |
}; | |
pub const ALL: [Tile; Tile::COUNT] = { | |
let mut all: [Tile; Tile::COUNT] = [Tile(Vertical::None, Horizontal::None); Tile::COUNT]; | |
let mut index = 0; | |
let mut h_index = 0; | |
while h_index < Horizontal::ALL.len() { | |
let mut v_index = 0; | |
while v_index < Vertical::ALL.len() { | |
if let Some(tile) = Tile::new(Vertical::ALL[v_index], Horizontal::ALL[h_index]) { | |
all[index] = tile; | |
index += 1; | |
} | |
v_index += 1; | |
} | |
h_index += 1; | |
} | |
all | |
}; | |
#[inline(always)] | |
pub const fn new(vertical: Vertical, horizontal: Horizontal) -> Option<Tile> { | |
let raw = Tile(vertical, horizontal); | |
// Ensure that there is either zero, or at least two connections. | |
match raw.connections() { | |
0 | 2.. => Some(raw), | |
_ => None, | |
} | |
} | |
#[inline(always)] | |
pub const fn connections(self) -> u32 { | |
self.0.len() + self.1.len() | |
} | |
#[inline(always)] | |
pub const fn offset(self) -> u32 { | |
(self.0 as u32) | ((self.1 as u32) << 2) | |
} | |
#[inline(always)] | |
pub const fn to_tileset(self) -> TileSet { | |
TileSet { | |
bits: 1 << self.offset(), | |
} | |
} | |
#[inline(always)] | |
pub const fn from_offset(offset: u32) -> Option<Tile> { | |
let horizontal = match offset & 3 { | |
0 => Horizontal::None, | |
1 => Horizontal::East, | |
2 => Horizontal::West, | |
3 => Horizontal::Both, | |
_ => return None, | |
}; | |
let vertical = match offset >> 2 { | |
0 => Vertical::None, | |
1 => Vertical::North, | |
2 => Vertical::South, | |
3 => Vertical::Both, | |
_ => return None, | |
}; | |
Tile::new(vertical, horizontal) | |
} | |
#[inline(always)] | |
pub const fn to_tuple(self) -> (Vertical, Horizontal) { | |
(self.0, self.1) | |
} | |
#[inline(always)] | |
pub const fn complement(self) -> Tile { | |
Tile(self.0.complement(), self.1.complement()) | |
} | |
#[inline(always)] | |
pub const fn flip(self) -> Tile { | |
Tile(self.0.flip(), self.1.flip()) | |
} | |
#[inline(always)] | |
pub const fn token(self) -> char { | |
match self { | |
Tile(Vertical::None, Horizontal::None) => ' ', | |
Tile(Vertical::None, Horizontal::Both) => '─', | |
Tile(Vertical::North, Horizontal::East) => '└', | |
Tile(Vertical::North, Horizontal::West) => '┘', | |
Tile(Vertical::North, Horizontal::Both) => '┴', | |
Tile(Vertical::South, Horizontal::East) => '┌', | |
Tile(Vertical::South, Horizontal::West) => '┐', | |
Tile(Vertical::South, Horizontal::Both) => '┬', | |
Tile(Vertical::Both, Horizontal::None) => '│', | |
Tile(Vertical::Both, Horizontal::East) => '├', | |
Tile(Vertical::Both, Horizontal::West) => '┤', | |
Tile(Vertical::Both, Horizontal::Both) => '┼', | |
_ => char::REPLACEMENT_CHARACTER, | |
} | |
} | |
} | |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | |
pub struct TileSet { | |
bits: u32, | |
} | |
impl TileSet { | |
pub const MASK: u32 = { | |
let mut mask = 0; | |
let mut index = 0; | |
while index < Tile::ALL.len() { | |
mask |= 1 << Tile::ALL[index].offset(); | |
index += 1; | |
} | |
mask | |
}; | |
#[inline(always)] | |
pub const fn new_truncated(bits: u32) -> TileSet { | |
TileSet { | |
bits: bits & TileSet::MASK, | |
} | |
} | |
#[inline(always)] | |
pub const fn all() -> TileSet { | |
TileSet::new_truncated(u32::MAX) | |
} | |
#[inline(always)] | |
pub const fn empty() -> TileSet { | |
TileSet::new_truncated(0) | |
} | |
#[inline(always)] | |
pub const fn bits(self) -> u32 { | |
self.bits | |
} | |
#[inline(always)] | |
pub const fn len(self) -> u32 { | |
self.bits.count_ones() | |
} | |
#[inline(always)] | |
pub const fn is_empty(self) -> bool { | |
self.bits == 0 | |
} | |
#[inline(always)] | |
pub const fn is_all(self) -> bool { | |
self.bits == TileSet::MASK | |
} | |
#[inline(always)] | |
pub const fn intersects(self, other: TileSet) -> bool { | |
self.bits & other.bits != 0 | |
} | |
#[inline(always)] | |
pub const fn contains(self, other: TileSet) -> bool { | |
self.bits & other.bits == other.bits | |
} | |
#[inline(always)] | |
pub const fn complement(self) -> TileSet { | |
TileSet::new_truncated(!self.bits) | |
} | |
#[inline(always)] | |
pub const fn union(self, other: TileSet) -> TileSet { | |
TileSet { | |
bits: self.bits | other.bits, | |
} | |
} | |
#[inline(always)] | |
pub const fn intersection(self, other: TileSet) -> TileSet { | |
TileSet { | |
bits: self.bits & other.bits, | |
} | |
} | |
#[inline(always)] | |
pub const fn difference(self, other: TileSet) -> TileSet { | |
TileSet { | |
bits: self.bits & !other.bits, | |
} | |
} | |
#[inline(always)] | |
pub const fn symmetric_difference(self, other: TileSet) -> TileSet { | |
TileSet { | |
bits: self.bits ^ other.bits, | |
} | |
} | |
#[inline(always)] | |
pub fn toggle(&mut self, other: TileSet) { | |
*self = self.symmetric_difference(other); | |
} | |
#[inline(always)] | |
pub fn insert(&mut self, other: TileSet) { | |
*self = self.union(other); | |
} | |
#[inline(always)] | |
pub fn remove(&mut self, other: TileSet) { | |
*self = self.difference(other); | |
} | |
#[inline(always)] | |
pub const fn iter(self) -> TileSetIter { | |
TileSetIter { bitset: self.bits } | |
} | |
} | |
#[derive(Debug, Clone)] | |
pub struct TileSetIter { | |
bitset: u32, | |
} | |
impl TileSetIter { | |
pub const fn remaining(&self) -> TileSet { | |
TileSet { bits: self.bitset } | |
} | |
} | |
impl Iterator for TileSetIter { | |
type Item = Tile; | |
#[inline(always)] | |
fn next(&mut self) -> Option<Self::Item> { | |
if self.bitset == 0 { | |
return None; | |
} | |
let offset = self.bitset.trailing_zeros(); | |
self.bitset &= self.bitset - 1; | |
Tile::from_offset(offset) | |
} | |
#[inline(always)] | |
fn size_hint(&self) -> (usize, Option<usize>) { | |
let len = self.len(); | |
(len, Some(len)) | |
} | |
} | |
impl FusedIterator for TileSetIter {} | |
impl ExactSizeIterator for TileSetIter { | |
#[inline(always)] | |
fn len(&self) -> usize { | |
self.bitset.count_ones() as usize | |
} | |
} | |
impl IntoIterator for TileSet { | |
type Item = Tile; | |
type IntoIter = TileSetIter; | |
#[inline(always)] | |
fn into_iter(self) -> Self::IntoIter { | |
TileSetIter { bitset: self.bits } | |
} | |
} | |
impl From<Tile> for TileSet { | |
#[inline(always)] | |
fn from(value: Tile) -> Self { | |
value.to_tileset() | |
} | |
} | |
impl Extend<Tile> for TileSet { | |
#[inline(always)] | |
fn extend<T: IntoIterator<Item = Tile>>(&mut self, iter: T) { | |
for tile in iter { | |
self.insert(tile.to_tileset()); | |
} | |
} | |
} | |
impl<'a> Extend<&'a Tile> for TileSet { | |
#[inline(always)] | |
fn extend<T: IntoIterator<Item = &'a Tile>>(&mut self, iter: T) { | |
for tile in iter { | |
self.insert(tile.to_tileset()); | |
} | |
} | |
} | |
impl Extend<TileSet> for TileSet { | |
#[inline(always)] | |
fn extend<T: IntoIterator<Item = TileSet>>(&mut self, iter: T) { | |
for tile_set in iter { | |
self.insert(tile_set); | |
} | |
} | |
} | |
impl<'a> Extend<&'a TileSet> for TileSet { | |
#[inline(always)] | |
fn extend<T: IntoIterator<Item = &'a TileSet>>(&mut self, iter: T) { | |
for tile_set in iter { | |
self.insert(*tile_set); | |
} | |
} | |
} | |
impl<T> FromIterator<T> for TileSet | |
where | |
TileSet: Extend<T>, | |
{ | |
#[inline(always)] | |
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { | |
let mut tile_set = TileSet::empty(); | |
tile_set.extend(iter); | |
tile_set | |
} | |
} | |
/// Not an actual test, really. Fuck off. | |
#[test] | |
fn test() { | |
for tile in TileSet::all().iter() { | |
println!("{tile:?}"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment