From 48cc893d42951013df25a99fb10cd74951af1bf6 Mon Sep 17 00:00:00 2001 From: mehbark <terezi@pyrope.net> Date: Tue, 11 Mar 2025 17:36:34 -0400 Subject: [PATCH] hardcode stuff --- Cargo.lock | 31 ------- Cargo.toml | 3 +- gen_efficacy.rb | 25 ++++++ src/main.rs | 228 +++++++++++++++++++++++++----------------------- 4 files changed, 144 insertions(+), 143 deletions(-) create mode 100644 gen_efficacy.rb diff --git a/Cargo.lock b/Cargo.lock index 2e60f1b..2d355a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,12 +134,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.3" @@ -186,16 +180,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "heck" version = "0.5.0" @@ -210,8 +194,6 @@ checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" dependencies = [ "bytemuck", "byteorder-lite", - "color_quant", - "gif", "num-traits", "png", ] @@ -228,12 +210,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.171" @@ -290,7 +266,6 @@ version = "0.1.0" dependencies = [ "clap", "image", - "lazy_static", "rand", "serde", "serde_json", @@ -435,12 +410,6 @@ dependencies = [ "wit-bindgen-rt", ] -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index 95e0b0c..228530c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,7 @@ edition = "2021" [dependencies] clap = { version = "4.5.32", features = ["derive"] } -image = { version = "0.25.5", default-features = false, features = ["png", "gif"] } -lazy_static = "1.5.0" +image = { version = "0.25.5", default-features = false, features = ["png"] } rand = "0.9.0" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" diff --git a/gen_efficacy.rb b/gen_efficacy.rb new file mode 100644 index 0000000..5480e18 --- /dev/null +++ b/gen_efficacy.rb @@ -0,0 +1,25 @@ +require 'json' + +eff = JSON.parse(File.read("src/types.json")) + +def make_branches(type, defenders, efficacy) + if defenders.empty? + [] + else + ["(#{type}, #{defenders.join(' | ')}) => #{efficacy},"] + end +end + +branches = eff.flat_map do |eff| + type = eff['name'] + [ + *make_branches(type, eff['immunes'], 'Immune'), + *make_branches(type, eff['weaknesses'], 'Weak'), + *make_branches(type, eff['strengths'], 'Strong'), + *make_branches(type, ['_'], 'Neutral'), + ] +end + +puts "match (self, defender) { + #{branches.join("\n ")} +}" diff --git a/src/main.rs b/src/main.rs index 13ba984..adc6ed4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,14 @@ #![allow( clippy::cast_sign_loss, clippy::cast_possible_wrap, - clippy::cast_possible_truncation + clippy::cast_possible_truncation, + clippy::enum_glob_use )] -// translate type efficacies to probabilities -// https://github.com/filipekiss/pokemon-type-chart/blob/master/types.json -const TYPE_ADVANTAGES_SRC: &str = include_str!("types.json"); -// https://gist.github.com/apaleslimghost/0d25ec801ca4fc43317bcff298af43c3 (titlecased) -const TYPE_COLORS_SRC: &str = include_str!("type-colors.json"); - -use std::{ - collections::{HashMap, HashSet}, - fmt, process, -}; +use std::{fmt, process}; use clap::Parser; use image::{ImageBuffer, Rgb}; -use lazy_static::lazy_static; use rand::{ distr::{Distribution, StandardUniform}, Rng, @@ -36,39 +27,21 @@ fn main() { } = Args::parse(); let mut rng = rand::rng(); + let mut game = Game::new_random(width, height, &mut rng); + let mut last = game.clone(); for i in 0..steps { - game.step(&mut rng); + let GameStep { old, new } = game.step(last, &mut rng); + game = new; + last = old; let path = format!("{output_prefix}{i:0OUTPUT_DIGITS$}.png"); + // TODO: reuse `ImageBuffer` if let Err(e) = ImageBuffer::from(&game).save(path) { eprintln!("error saving image: {e}"); process::exit(1); } } - - // TODO: refactor to error enum to avoid this - // TODO: animation (at least fast image sequence) - // TODO: don't constantly alloc buffers - // if we need more errors -} - -lazy_static! { - static ref TYPE_ADVANTAGES: HashMap<Type, TypeAdvantage> = { - let rows: Vec<TypeAdvantageRow> = serde_json::from_str(TYPE_ADVANTAGES_SRC).unwrap(); - rows.into_iter().map(|r| (r.name, r.into())).collect() - }; - static ref TYPE_COLORS: HashMap<Type, Rgb<u8>> = { - let colors: HashMap<Type, String> = serde_json::from_str(TYPE_COLORS_SRC).unwrap(); - colors - .into_iter() - .map(|(typ, color)| { - let color = u32::from_str_radix(&color[1..], 16).unwrap(); - let [_, r, g, b] = color.to_be_bytes(); - (typ, Rgb([r, g, b])) - }) - .collect() - }; } #[derive(Debug, clap::Parser)] @@ -93,6 +66,12 @@ struct Game { field: Vec<Type>, } +#[derive(Debug, Clone)] +struct GameStep { + old: Game, + new: Game, +} + impl Game { fn new_random(width: u16, height: u16, rng: &mut impl Rng) -> Self { let (width, height) = (i64::from(width), i64::from(height)); @@ -123,8 +102,7 @@ impl Game { // let's see if we can get away with not swapping the field // nah it's easy - fn step(&mut self, rng: &mut impl Rng) { - let mut next = self.clone(); + fn step(self, mut next: Self, rng: &mut impl Rng) -> GameStep { for x in 0..self.width { for y in 0..self.height { let dx = rng.random_range(-1..=1); @@ -147,7 +125,10 @@ impl Game { } } } - self.field = next.field; + GameStep { + old: self, + new: next, + } } } @@ -183,9 +164,9 @@ enum Type { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum Efficacy { - Super, + Strong, Neutral, - NotVery, + Weak, Immune, } @@ -211,59 +192,119 @@ impl Type { Self::Fairy, ]; + // https://gist.github.com/apaleslimghost/0d25ec801ca4fc43317bcff298af43c3 fn color(self) -> Rgb<u8> { - TYPE_COLORS[&self] - } - - fn advantage(self) -> &'static TypeAdvantage { - &TYPE_ADVANTAGES[&self] + use Type::*; + match self { + Normal => Rgb([0xA8, 0xA7, 0x7A]), + Fire => Rgb([0xEE, 0x81, 0x30]), + Water => Rgb([0x63, 0x90, 0xF0]), + Electric => Rgb([0xF7, 0xD0, 0x2C]), + Grass => Rgb([0x7A, 0xC7, 0x4C]), + Ice => Rgb([0x96, 0xD9, 0xD6]), + Fighting => Rgb([0xC2, 0x2E, 0x28]), + Poison => Rgb([0xA3, 0x3E, 0xA1]), + Ground => Rgb([0xE2, 0xBF, 0x65]), + Flying => Rgb([0xA9, 0x8F, 0xF3]), + Psychic => Rgb([0xF9, 0x55, 0x87]), + Bug => Rgb([0xA6, 0xB9, 0x1A]), + Rock => Rgb([0xB6, 0xA1, 0x36]), + Ghost => Rgb([0x73, 0x57, 0x97]), + Dragon => Rgb([0x6F, 0x35, 0xFC]), + Dark => Rgb([0x70, 0x57, 0x46]), + Steel => Rgb([0xB7, 0xB7, 0xCE]), + Fairy => Rgb([0xD6, 0x85, 0xAD]), + } } // nothing is immune to itself, phew fn win_chance(self, other: Type) -> f32 { - use Efficacy::{Immune, Neutral, NotVery, Super}; + use Efficacy::{Immune, Neutral, Strong, Weak}; let self_eff = self.efficacy(other); let def_eff = other.efficacy(self); // if you are twice as effective, you should win twice as often: 2/3 vs 1/3 // if you are four times as effective, you should win four times as often: 4/5 vs 1/5 match (self_eff, def_eff) { - (Super, Super) | (Neutral, Neutral) | (NotVery, NotVery) | (Immune, Immune) => 0.5, + (Strong, Strong) | (Neutral, Neutral) | (Weak, Weak) | (Immune, Immune) => 0.5, (Immune, _) => 1.0, (_, Immune) => 0.0, - (Efficacy::Super, Efficacy::Neutral) | (Efficacy::Neutral, Efficacy::NotVery) => { - 2. / 3. - } - (Efficacy::Neutral, Efficacy::Super) | (Efficacy::NotVery, Efficacy::Neutral) => { - 1. / 3. - } - (Efficacy::Super, Efficacy::NotVery) => 4. / 5., - (Efficacy::NotVery, Efficacy::Super) => 1. / 5., + (Efficacy::Strong, Efficacy::Neutral) | (Efficacy::Neutral, Efficacy::Weak) => 2. / 3., + (Efficacy::Neutral, Efficacy::Strong) | (Efficacy::Weak, Efficacy::Neutral) => 1. / 3., + (Efficacy::Strong, Efficacy::Weak) => 4. / 5., + (Efficacy::Weak, Efficacy::Strong) => 1. / 5., } } + // fudge it, i'm hard-coding + // https://github.com/filipekiss/pokemon-type-chart/blob/master/types.json + // great lint normally but not here + #[allow(clippy::match_same_arms)] fn efficacy(self, defender: Type) -> Efficacy { - if defender.is_immune_to(self) { - return Efficacy::Immune; + use Efficacy::*; + use Type::*; + match (self, defender) { + (Normal, Ghost) => Immune, + (Normal, Rock | Steel) => Weak, + (Normal, _) => Neutral, + (Fire, Fire | Water | Rock | Dragon) => Weak, + (Fire, Grass | Ice | Bug | Steel) => Strong, + (Fire, _) => Neutral, + (Water, Water | Grass | Dragon) => Weak, + (Water, Fire | Ground | Rock) => Strong, + (Water, _) => Neutral, + (Electric, Ground) => Immune, + (Electric, Electric | Grass | Dragon) => Weak, + (Electric, Water | Flying) => Strong, + (Electric, _) => Neutral, + (Grass, Fire | Grass | Poison | Flying | Bug | Dragon | Steel) => Weak, + (Grass, Water | Ground | Rock) => Strong, + (Grass, _) => Neutral, + (Ice, Fire | Water | Ice | Steel) => Weak, + (Ice, Grass | Ground | Flying | Dragon) => Strong, + (Ice, _) => Neutral, + (Fighting, Ghost) => Immune, + (Fighting, Poison | Flying | Psychic | Bug | Fairy) => Weak, + (Fighting, Normal | Ice | Rock | Dark | Steel) => Strong, + (Fighting, _) => Neutral, + (Poison, Steel) => Immune, + (Poison, Poison | Ground | Rock | Ghost) => Weak, + (Poison, Grass | Fairy) => Strong, + (Poison, _) => Neutral, + (Ground, Flying) => Immune, + (Ground, Grass | Bug) => Weak, + (Ground, Fire | Electric | Poison | Rock | Steel) => Strong, + (Ground, _) => Neutral, + (Flying, Electric | Rock | Steel) => Weak, + (Flying, Grass | Fighting | Bug) => Strong, + (Flying, _) => Neutral, + (Psychic, Dark) => Immune, + (Psychic, Psychic | Steel) => Weak, + (Psychic, Fighting | Poison) => Strong, + (Psychic, _) => Neutral, + (Bug, Fire | Fighting | Poison | Flying | Ghost | Steel | Fairy) => Weak, + (Bug, Grass | Psychic | Dark) => Strong, + (Bug, _) => Neutral, + (Rock, Fighting | Ground | Steel) => Weak, + (Rock, Fire | Ice | Flying | Bug) => Strong, + (Rock, _) => Neutral, + (Ghost, Normal) => Immune, + (Ghost, Dark) => Weak, + (Ghost, Psychic | Ghost) => Strong, + (Ghost, _) => Neutral, + (Dragon, Fairy) => Immune, + (Dragon, Steel) => Weak, + (Dragon, Dragon) => Strong, + (Dragon, _) => Neutral, + (Dark, Fighting | Dark | Fairy) => Weak, + (Dark, Psychic | Ghost) => Strong, + (Dark, _) => Neutral, + (Steel, Fire | Water | Electric | Steel) => Weak, + (Steel, Ice | Rock | Fairy) => Strong, + (Steel, _) => Neutral, + (Fairy, Fire | Poison | Steel) => Weak, + (Fairy, Fighting | Dragon | Dark) => Strong, + (Fairy, _) => Neutral, } - if self.is_strong_against(defender) { - return Efficacy::Super; - } - if self.is_weak_against(defender) { - return Efficacy::NotVery; - } - Efficacy::Neutral - } - - fn is_immune_to(self, other: Type) -> bool { - other.advantage().immunes.contains(&self) - } - - fn is_strong_against(self, other: Type) -> bool { - self.advantage().strengths.contains(&other) - } - - fn is_weak_against(self, other: Type) -> bool { - self.advantage().weaknesses.contains(&other) } } @@ -278,36 +319,3 @@ impl fmt::Display for Type { write!(f, "{self:?}") } } - -#[derive(Debug, Deserialize)] -struct TypeAdvantageRow { - name: Type, - immunes: HashSet<Type>, - strengths: HashSet<Type>, - weaknesses: HashSet<Type>, -} - -/// Stats for the _attacker_. -#[derive(Debug)] -struct TypeAdvantage { - immunes: HashSet<Type>, - strengths: HashSet<Type>, - weaknesses: HashSet<Type>, -} - -impl From<TypeAdvantageRow> for TypeAdvantage { - fn from( - TypeAdvantageRow { - immunes, - strengths, - weaknesses, - .. - }: TypeAdvantageRow, - ) -> Self { - Self { - immunes, - strengths, - weaknesses, - } - } -}