Skip to content
Snippets Groups Projects
Verified Commit 64e18b79 authored by Louis's avatar Louis :fire:
Browse files

Initial commit; basic 1x1 and 3x3 impl for autotiling

parents
No related branches found
No related tags found
No related merge requests found
/target
/Cargo.lock
[package]
name = "micro_autotile"
version = "0.1.0"
edition = "2021"
description = "A rusty implementation of LDTK autotiling"
authors = [
"Louis Capitanchik <louis@microhacks.co.uk>"
]
[features]
default = ["impl_fastrand"]
impl_fastrand = ["dep:fastrand"]
[dependencies]
fastrand = { version = "1.8.0", optional = true }
\ No newline at end of file
use std::ops::Add;
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, Copy, Clone)]
pub enum TileStatus {
#[default]
Ignore,
Nothing,
Anything,
Is(usize),
IsNot(usize),
}
impl PartialEq<Option<usize>> for TileStatus {
fn eq(&self, other: &Option<usize>) -> bool {
match self {
Self::Ignore => true,
Self::Nothing => other.is_none(),
Self::Anything => other.is_some(),
Self::Is(value) => &Some(*value) == other,
Self::IsNot(value) => &Some(*value) != other,
}
}
}
impl TileStatus {
pub fn to_ldtk_value(&self) -> i64 {
match self {
Self::Ignore => 0,
Self::Nothing => -1000001,
Self::Anything => 1000001,
Self::Is(value) => *value as i64,
Self::IsNot(value) => -(*value as i64),
}
}
pub fn from_ldtk_value(value: i64) -> Self {
match value {
0 => Self::Ignore,
1000001 => Self::Anything,
-1000001 => Self::Nothing,
other => {
if other > 0 {
Self::Is(other as usize)
} else {
Self::IsNot(other.unsigned_abs() as usize)
}
}
}
}
}
#[derive(Clone, Debug, Default)]
#[repr(transparent)]
pub struct TileMatcher(pub [TileStatus; 9]);
impl TileMatcher {
pub const fn single(value: usize) -> Self {
Self([
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Is(value),
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
])
}
pub const fn single_match(value: TileStatus) -> Self {
Self([
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
value,
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
TileStatus::Ignore,
])
}
pub fn matches(&self, layout: &TileLayout) -> bool {
self.0
.iter()
.zip(layout.0.iter())
.all(|(status, reality)| *status == *reality)
}
pub fn from_ldtk_array(value: Vec<i64>) -> Option<Self> {
if value.len() == 1 {
let tile = value[0];
Some(Self::single_match(TileStatus::from_ldtk_value(tile)))
} else if value.len() == 9 {
Some(TileMatcher(
[
value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7],
value[8],
]
.map(TileStatus::from_ldtk_value),
))
} else {
None
}
}
}
#[derive(Clone, Debug, Default)]
#[repr(transparent)]
pub struct TileLayout(pub [Option<usize>; 9]);
impl TileLayout {
pub fn single(value: usize) -> Self {
TileLayout([None, None, None, None, Some(value), None, None, None, None])
}
pub fn surrounding(&self) -> [Option<usize>; 8] {
[
self.0[0], self.0[1], self.0[2], self.0[3], self.0[5], self.0[6], self.0[7], self.0[8],
]
}
}
#[derive(Clone, Debug, Default)]
pub enum TileOutput {
#[default]
Skip,
Single(usize),
Random(Vec<usize>),
}
impl TileOutput {
pub const fn single(value: usize) -> Self {
TileOutput::Single(value)
}
pub const fn any(value: Vec<usize>) -> Self {
TileOutput::Random(value)
}
}
#[derive(Clone, Debug, Default)]
pub struct AutoTileRule {
pub matcher: TileMatcher,
pub output: TileOutput,
pub chance: f32,
}
impl AutoTileRule {
pub const fn exact(input_value: usize, output_value: usize) -> Self {
Self::exact_chance(input_value, output_value, 1.0)
}
pub const fn exact_chance(input_value: usize, output_value: usize, chance: f32) -> Self {
AutoTileRule {
matcher: TileMatcher::single(input_value),
output: TileOutput::single(output_value),
chance,
}
}
pub const fn single_when(matcher: TileMatcher, output_value: usize) -> Self {
AutoTileRule {
matcher,
output: TileOutput::single(output_value),
chance: 1.0,
}
}
pub const fn single_any(input_value: usize, output_value: Vec<usize>) -> Self {
Self::single_any_chance(input_value, output_value, 1.0)
}
pub const fn single_any_chance(
input_value: usize,
output_value: Vec<usize>,
chance: f32,
) -> Self {
AutoTileRule {
matcher: TileMatcher::single(input_value),
output: TileOutput::any(output_value),
chance,
}
}
pub const fn any_any_chance(
input_value: TileMatcher,
output_value: Vec<usize>,
chance: f32,
) -> Self {
AutoTileRule {
matcher: input_value,
output: TileOutput::any(output_value),
chance,
}
}
#[cfg(feature = "impl_fastrand")]
pub fn get_match(&self, input: &TileLayout) -> Option<&TileOutput> {
let chance = fastrand::f32();
if chance <= self.chance && self.matcher.matches(input) {
Some(&self.output)
} else {
None
}
}
#[cfg(feature = "impl_fastrand")]
pub fn get_match_seeded(
&self,
input: &TileLayout,
seeded: &fastrand::Rng,
) -> Option<&TileOutput> {
let chance = seeded.f32();
if chance <= self.chance && self.matcher.matches(input) {
Some(&self.output)
} else {
None
}
}
}
#[derive(Clone, Debug, Default)]
pub struct AutoRuleSet(pub Vec<AutoTileRule>);
impl Add<AutoRuleSet> for AutoRuleSet {
type Output = AutoRuleSet;
fn add(self, rhs: AutoRuleSet) -> Self::Output {
AutoRuleSet([self.0.as_slice(), rhs.0.as_slice()].concat())
}
}
impl AutoRuleSet {
#[cfg(feature = "impl_fastrand")]
pub fn get_match(&self, input: &TileLayout) -> Option<&TileOutput> {
for rule in self.0.iter() {
let result = rule.get_match(input);
if result.is_some() {
return result;
}
}
None
}
#[cfg(feature = "impl_fastrand")]
pub fn get_match_seeded(
&self,
input: &TileLayout,
seeded: &fastrand::Rng,
) -> Option<&TileOutput> {
for rule in self.0.iter() {
let result = rule.get_match_seeded(input, seeded);
if result.is_some() {
return result;
}
}
None
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment