use crate::draws::Draws;
use crate::grid::{ACCENTS, BACKGROUNDS, Canvas, Ink, Layer, PUPIL, WHITE, hex};
use crate::{Avatar, Kind, Rgb};
pub(crate) const CONTEXT: &str = "gsichtl 2026-10-02 monster v1";
pub(crate) const SIDE: u8 = 8;
const BODIES: [Rgb; 10] = [
hex(0x7c5cff),
hex(0xff6b6b),
hex(0x4ecdc4),
hex(0xffa94d),
hex(0x69db7c),
hex(0x4dabf7),
hex(0xf783ac),
hex(0xd9572b),
hex(0xa9e34b),
hex(0xe599f7),
];
const SILHOUETTES: [Layer; 3] = [
Layer::new(
2,
&[
".######.", "########", "########", "########", "########", ".######.",
],
false,
),
Layer::new(
2,
&[
"..####..", ".######.", "########", "########", ".######.", "..####..",
],
false,
),
Layer::new(
2,
&[
".######.", ".######.", "########", "########", "########", "########",
],
false,
),
];
const TOPS: [Layer; 6] = [
Layer::new(0, &["..#..#..", "..#..#.."], false),
Layer::new(0, &["a......a", ".a....a."], false),
Layer::new(0, &[".#....#.", ".##..##."], false),
Layer::new(0, &["...aa...", "...##..."], false),
Layer::new(0, &["........", ".#.##.#."], false),
Layer::new(0, &["........", "........"], false),
];
const BROWS: [Layer; 3] = [
Layer::new(3, &["..dddd.."], true),
Layer::new(3, &["........"], true),
Layer::new(3, &[".dd..dd."], true),
];
const EYES: [Layer; 5] = [
Layer::new(4, &["#ww##ww#", "#wp##wp#"], true),
Layer::new(4, &["#ww##ww#", "#pw##pw#"], true),
Layer::new(4, &["#pw##pw#", "#ww##ww#"], true),
Layer::new(4, &["##wwww##", "##wppw##"], true),
Layer::new(4, &["#dd##dd#", "#wp##wp#"], true),
];
const MOUTHS: [Layer; 5] = [
Layer::new(6, &["########", ".#a#a#a."], true),
Layer::new(6, &["##dddd##"], true),
Layer::new(6, &["##dddd##", "..a..a.."], true),
Layer::new(6, &["###dd###"], true),
Layer::new(6, &[], true),
];
#[cfg(test)]
pub(crate) const PARTS: &[&[Layer]] = &[&SILHOUETTES, &TOPS, &BROWS, &EYES, &MOUTHS];
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct MonsterChoice {
bg: usize,
body: usize,
accent: usize,
top: usize,
silhouette: usize,
brow: usize,
eyes: usize,
mouth: usize,
}
pub(crate) fn draw(draws: &mut Draws) -> MonsterChoice {
MonsterChoice {
bg: draws.pick(BACKGROUNDS.len()),
body: draws.pick(BODIES.len()),
accent: draws.pick(ACCENTS.len()),
top: draws.pick(TOPS.len()),
silhouette: draws.pick(SILHOUETTES.len()),
brow: draws.pick(BROWS.len()),
eyes: draws.pick(EYES.len()),
mouth: draws.pick(MOUTHS.len()),
}
}
pub(crate) fn build(choice: MonsterChoice) -> Avatar {
let background = BACKGROUNDS[choice.bg % BACKGROUNDS.len()];
let body = BODIES[choice.body % BODIES.len()];
let accent = ACCENTS[choice.accent % ACCENTS.len()];
let mut canvas = Canvas::new(SIDE);
canvas.paint(&SILHOUETTES[choice.silhouette % SILHOUETTES.len()]);
canvas.paint(&TOPS[choice.top % TOPS.len()]);
canvas.paint(&BROWS[choice.brow % BROWS.len()]);
canvas.paint(&EYES[choice.eyes % EYES.len()]);
canvas.paint(&MOUTHS[choice.mouth % MOUTHS.len()]);
canvas.finish(Kind::Monster, background, |ink| match ink {
Ink::Dark => background,
Ink::White => WHITE,
Ink::Pupil => PUPIL,
Ink::Accent => accent,
Ink::Body | Ink::Hair | Ink::Frame | Ink::Field2 | Ink::Metal => body,
})
}
#[cfg(test)]
mod tests {
use super::{MonsterChoice, build};
use crate::grid::hex;
#[test]
fn reference_bot() {
let bot = build(MonsterChoice::default());
let background = hex(0x1a1530);
assert_eq!(bot.background(), background);
assert_eq!(bot.side(), 8);
let grid = [
"..V..V..", "..V..V..", ".VVVVVV.", "VV....VV", "VWWVVWWV", "VWPVVWPV", "VVVVVVVV",
".VYVYVY.",
];
for (y, row) in grid.iter().enumerate() {
for (x, c) in row.bytes().enumerate() {
let want = match c {
b'V' => Some(hex(0x7c5cff)),
b'W' => Some(hex(0xffffff)),
b'P' => Some(hex(0x11151c)),
b'Y' => Some(hex(0xffd166)),
_ => None,
};
let got = bot.cell(x as u8, y as u8);
let got = got.filter(|&c| !(c == background && want.is_none()));
assert_eq!(got, want, "cell ({x}, {y})");
}
}
}
}