Skip to content

Instantly share code, notes, and snippets.

@herabit
Created February 22, 2024 06:38
Show Gist options
  • Save herabit/0231413739e418f65108446446613096 to your computer and use it in GitHub Desktop.
Save herabit/0231413739e418f65108446446613096 to your computer and use it in GitHub Desktop.
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